Convert

Parse CSV text into rows and records

POST/v1/convert/csv/to/json

View as Markdown

Parses CSV text correctly: commas and line breaks inside quoted fields, escaped quotes, CRLF or LF, a byte order mark and blank lines. Returns one object per row keyed by the header and the same rows as lists. A row with fewer or more values than the header is kept (padded or extended) and reported in warnings, nothing is dropped. Set infer_types to turn columns of whole numbers, decimals and true or false into integers, numbers and booleans; empty cells in those columns become null and column_types reports what each column became. Leading zeros, plus signs, decimal commas, dates and mixed columns stay text.

Request body

Name Type Required Description
content string yes The CSV text
delimiter string no The one character between fields (default ",")
has_header boolean no True when the first row holds the column names (default true)
clean_headers boolean no Trim the column names and give empty names a name like column_2. Duplicates are always made unique (default true)
infer_types boolean no True turns whole-number columns into integers, decimal columns into numbers and true/false columns into booleans. Empty cells in those columns become null. Leading zeros, plus signs, decimal commas, dates and mixed columns stay text (default false)

Example request

A request body that works, as JSON.

{
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}

Code samples

Replace YOUR_TOKEN with your access token.

curl

curl -X POST 'https://api.skillfulsardine.com/v1/convert/csv/to/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}'

PowerShell

$body = @'
{
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}
'@
$response = Invoke-RestMethod -Method Post -Uri 'https://api.skillfulsardine.com/v1/convert/csv/to/json' `
  -Headers @{ Authorization = 'Bearer YOUR_TOKEN' } `
  -ContentType 'application/json; charset=utf-8' `
  -Body ([System.Text.Encoding]::UTF8.GetBytes($body))
$response | ConvertTo-Json -Depth 10

Python

import requests

payload = {
    "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n",
}

response = requests.post(
    "https://api.skillfulsardine.com/v1/convert/csv/to/json",
    headers={"Authorization": "Bearer YOUR_TOKEN"},
    json=payload,
    timeout=30,
)
print(response.json())

JavaScript

const response = await fetch("https://api.skillfulsardine.com/v1/convert/csv/to/json", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}),
});
const result = await response.json();
console.log(result);

Node

// Node 18 or later. Save as call.mjs and run: API_TOKEN=... node call.mjs
const token = process.env.API_TOKEN;

const response = await fetch("https://api.skillfulsardine.com/v1/convert/csv/to/json", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}),
});
const result = await response.json();
console.log(result);

Rust

// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let body: serde_json::Value = serde_json::from_str(r#"{
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}"#)?;
    let response = reqwest::blocking::Client::new()
        .post("https://api.skillfulsardine.com/v1/convert/csv/to/json")
        .bearer_auth("YOUR_TOKEN")
        .json(&body)
        .send()?;
    println!("{}", response.text()?);
    Ok(())
}

Swift

import Foundation

var request = URLRequest(url: URL(string: "https://api.skillfulsardine.com/v1/convert/csv/to/json")!)
request.httpMethod = "POST"
request.setValue("Bearer YOUR_TOKEN", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(#"""
{
  "content": "name,city,note\nAna,Porto,\"Likes coffee, black\"\nRui,Lisboa,\n"
}
"""#.utf8)

let (data, _) = try await URLSession.shared.data(for: request)
print(String(decoding: data, as: UTF8.self))

Response

The fields inside data of a successful response.

Name Type Required Description
headers array of string yes Column names, empty when has_header is false
records array of object yes One object per data row keyed by column name, empty without a header. Values are text unless infer_types is true, then they are integer, number, boolean or null
rows array of array of string or integer or number or boolean yes One list of values per data row, header row excluded. Values are text unless infer_types is true, then they are integer, number, boolean or null
row_count integer yes Number of data rows
column_types object yes The type inferred for each column when infer_types is true, one of text, integer, number or boolean. Empty when inference is off
warnings array of CsvParseWarning yes Rows with fewer or more values than the header

Example response

A real answer from the operation, as JSON. The fields in the table above are inside data.

{
  "data": {
    "headers": [
      "name",
      "city",
      "note"
    ],
    "records": [
      {
        "name": "Ana",
        "city": "Porto",
        "note": "Likes coffee, black"
      },
      {
        "name": "Rui",
        "city": "Lisboa",
        "note": ""
      }
    ],
    "rows": [
      [
        "Ana",
        "Porto",
        "Likes coffee, black"
      ],
      [
        "Rui",
        "Lisboa",
        ""
      ]
    ],
    "row_count": 2,
    "column_types": {},
    "warnings": []
  },
  "errors": null,
  "meta": {
    "request_id": "046af686-a337-4b31-ab9e-9087e78918cd",
    "timestamp": "2026-10-27T07:19:42.021015Z",
    "version": "2026-03-26"
  },
  "pagination": null
}

Responses

Status When it happens
200 Successful Response
400 The body respects the operation schema, but the use case rejected it. Each problem is one ApiError entry
413 The request is larger than the request size limit of the API allows
422 The body does not respect the operation schema. Each problem is one ApiError entry with the code validation_error
500 Something failed inside the server and was not an input problem. The same envelope, errors may be empty