API reference

Convert CSV to JSON

Convert CSV to a JSON array of objects (keyed by the header) or of arrays, optionally typing numbers and booleans.

GET POST /api/convert/csv-to-json MCP tool csv_to_json

Reads input exactly as /api/csv/format does (same delimiter and header detection) and converts it to JSON. shape=objects gives one object per row, keyed by the header names (an empty name becomes column_N; a repeated name gets _2, _3, …). shape=arrays gives one array per row, the header row first. auto is objects when there is a header. Without types every value is a string, exactly as written. With types, a column whose values are all integers or numbers becomes JSON numbers (every digit kept, even beyond 2^53), a true/false column becomes booleans, and empty values become null; text, date and mixed columns stay strings. CSV with errors (see the format operation) is rejected rather than guessed at.

Browser version (runs locally): CSV Validate & Format.

Try it

Credentials
Remember these credentials

Not saved: you will paste them again next time.

No credentials yet? Get a client ID and secret. They are sent only to this site, in an Authorization: Basic header.

POST /api/convert/csv-to-json with a JSON body

Parameters

Parameter Type Default Description
input required string (≤ 50000 chars) — The CSV text. Send it as a string field or as a raw text/plain POST body.
delimiter auto | comma | semicolon | tab | pipe auto Field delimiter of the input: comma, semicolon, tab or pipe. auto picks the one that gives the most consistent field count over the first 50 records, ignoring quoted text, and falls back to comma.
header auto | yes | no auto Whether the first row is a header. auto says yes when its values are all text (no numbers, booleans or dates), all different, and none repeats in its column below.
trim_fields boolean false Remove spaces and tabs around unquoted values. Quoted values are always kept exactly.
shape auto | objects | arrays auto objects (needs a header), arrays, or auto: objects when there is a header, arrays otherwise.
types boolean false Convert number and boolean columns to JSON numbers and booleans, and empty values to null.

Also accepted on every endpoint: format (json | text). Parameters may come from the query string, a JSON object body, an urlencoded or multipart form, or (for input) a text/plain body.

Global parameters
Parameter Type Default Description
format json | text json json returns the documented object. text returns only the primary value as text/plain (see "Text output").

Response 200 application/json

Field Type Description
json string The JSON text (2-space indentation).
value any The same data as a JSON value.
shape "objects" | "arrays" The shape used.
header array of string | null The header row; null when there is none.
rowCount integer Data rows converted.
columns array of object One entry per column.
columns[].name string Header name, or Column N without a header.
columns[].type "empty" | "integer" | "number" | "boolean" | "date" | "datetime" | "text" | "mixed" Inferred type of the non-empty values: integer, number (integers and decimals), boolean (true/false), date (YYYY-MM-DD), datetime (ISO 8601), text, mixed, or empty when every value is empty.
columns[].empty integer Empty (or missing) values.
columns[].distinct integer Distinct non-empty values.
columns[].maxLength integer Longest value, in Unicode code points.
dialect object How the input was read.
dialect.delimiter "," | ";" | " " | "|" The delimiter used to read the input (a tab is "\t").
dialect.delimiterDetected boolean True when the delimiter was detected rather than given (false when detection fell back to comma).
dialect.lineEnding "lf" | "crlf" | "cr" | "mixed" | "none" Line breaks between rows (line breaks inside quoted values are not counted).
dialect.hasHeader boolean True when the first row is treated as the header.
dialect.headerDetected boolean True when that decision came from the heuristic (header=auto).
dialect.bom boolean True when the input starts with a byte-order mark.

Text output (format=text, text/plain): The JSON text.

Errors

Errors are JSON: {"error": "<message>", "code": "<code>", "param": "<name>"?}.

Status Code When
422 unprocessable_input The CSV has an error (unclosed or stray quote, text after a quote, a row with the wrong number of fields; the message gives line and column), or shape=objects and there is no header.
400 invalid_parameter input is longer than 50,000 characters, or an option is not a listed value.
400 missing_parameter A required parameter is absent.
400 unknown_parameter A parameter name is not in the table above (typos are rejected, not ignored).
413 payload_too_large The request body exceeds 1 MiB.
405 method_not_allowed A method other than GET, POST or OPTIONS.

Behaviour

  • Idempotent: the same arguments always return the same result.
  • Requires a client ID and secret (authentication). Each call is recorded as one metadata row against your client ID; inputs and outputs are never stored or logged. Responses carry cache-control: no-store.
  • CORS: any origin may call this endpoint from a browser, using your credentials.

Examples

Responses below were produced by running each request through this code at build time. Long strings are shortened.

Objects with typed columns

curl -X POST 'https://util.caruana.me/api/convert/csv-to-json' \
  -H 'content-type: application/json' \
  -d '{"input":"id,name,price,in_stock,added\n1,Widget,9.99,true,2024-03-01\n2,\"Gadget, mini\",12,false,\n3,Gizmo,,true,2024-04-15\n","types":true}'

Response 200

{
  "json": "[\n  {\n    \"id\": 1,\n    \"name\": \"Widget\",\n    \"price\": 9.99,\n    \"in_stock\": true,\n    \"added\": \"2024-03-01\"\n  },\n  {\n   … (211 more characters)",
  "value": [
    {
      "id": 1,
      "name": "Widget",
      "price": 9.99,
      "in_stock": true,
      "added": "2024-03-01"
    },
    {
      "id": 2,
      "name": "Gadget, mini",
      "price": 12,
      "in_stock": false,
      "added": null
    },
    {
      "id": 3,
      "name": "Gizmo",
      "price": null,
      "in_stock": true,
      "added": "2024-04-15"
    }
  ],
  "shape": "objects",
  "header": [
    "id",
    "name",
    "price",
    "in_stock",
    "added"
  ],
  "rowCount": 3,
  "columns": [
    {
      "name": "id",
      "type": "integer",
      "empty": 0,
      "distinct": 3,
      "maxLength": 1
    },
    {
      "name": "name",
      "type": "text",
      "empty": 0,
      "distinct": 3,
      "maxLength": 12
    },
    {
      "name": "price",
      "type": "number",
      "empty": 1,
      "distinct": 2,
      "maxLength": 4
    },
    {
      "name": "in_stock",
      "type": "boolean",
      "empty": 0,
      "distinct": 2,
      "maxLength": 5
    },
    {
      "name": "added",
      "type": "date",
      "empty": 1,
      "distinct": 2,
      "maxLength": 10
    }
  ],
  "dialect": {
    "delimiter": ",",
    "delimiterDetected": true,
    "lineEnding": "lf",
    "hasHeader": true,
    "headerDetected": true,
    "bom": false
  }
}
Same call as an MCP tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "csv_to_json",
    "arguments": {
      "input": "id,name,price,in_stock,added\n1,Widget,9.99,true,2024-03-01\n2,\"Gadget, mini\",12,false,\n3,Gizmo,,true,2024-04-15\n",
      "types": true
    }
  }
}

Arrays from tab-separated data, every value a string

curl -X POST 'https://util.caruana.me/api/convert/csv-to-json' \
  -H 'content-type: application/json' \
  -d '{"input":"x\ty\n1\t2\n3\t4\n","shape":"arrays"}'

Response 200

{
  "json": "[\n  [\"x\", \"y\"],\n  [\"1\", \"2\"],\n  [\"3\", \"4\"]\n]",
  "value": [
    [
      "x",
      "y"
    ],
    [
      "1",
      "2"
    ],
    [
      "3",
      "4"
    ]
  ],
  "shape": "arrays",
  "header": [
    "x",
    "y"
  ],
  "rowCount": 2,
  "columns": [
    {
      "name": "x",
      "type": "integer",
      "empty": 0,
      "distinct": 2,
      "maxLength": 1
    },
    {
      "name": "y",
      "type": "integer",
      "empty": 0,
      "distinct": 2,
      "maxLength": 1
    }
  ],
  "dialect": {
    "delimiter": "\t",
    "delimiterDetected": true,
    "lineEnding": "lf",
    "hasHeader": true,
    "headerDetected": true,
    "bom": false
  }
}
Same call as an MCP tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "csv_to_json",
    "arguments": {
      "input": "x\ty\n1\t2\n3\t4\n",
      "shape": "arrays"
    }
  }
}