How to convert a csv file to json for api import
- Step 1Export your data as CSV — From your spreadsheet, database admin tool, or app export, save a CSV with a header row. The header row becomes the JSON object keys, so name columns the way your API field names need them (e.g.
firstName, notFirst Name, if the endpoint is camelCase-strict). - Step 2Drop the CSV onto the converter above — The tool accepts
.csv,.tsv, and.txt. PapaParse auto-detects the delimiter (comma, tab, semicolon) from the file, so a tab-separated export works without changing anything. Everything parses in your browser. - Step 3Choose Array of objects as the output format — For a standard REST POST body you want Array of objects — the default. The preview pane shows the first 1.5 KB of the generated JSON so you can confirm the shape before downloading.
- Step 4Leave type inference on (or turn it off deliberately) — Infer numbers, booleans, null is on by default. Keep it on so the API receives typed values. Turn it off only if your endpoint genuinely wants every field as a string — then
"42"stays a string in the output. - Step 5Pick an indent — Minified for the smallest request body, 2 spaces (default) or 4 spaces for a body you will read by hand. Indentation has no effect on how the API parses the JSON — it is cosmetic for the array/grouped modes only.
- Step 6Download and post the JSON — Click Download JSON to save the array, or copy from the preview for a small file. Send it as the request body with
Content-Type: application/json. Cross-check the Records stat against your source row count before posting to a production endpoint.
What this converter does and does not do
The five real options on the tool, and the capabilities that live in sibling tools instead.
| Capability | In this tool? | How / where |
|---|---|---|
| Array of objects output | Yes | outputMode: array — the default REST body shape |
| NDJSON / JSON Lines output | Yes | outputMode: ndjson — one compact object per line |
| Type inference (number / boolean / null) | Yes | inferTypes, default on |
| Indent control | Yes | Minified / 2 / 4 spaces |
| Skip empty cells (omit the key) | Yes | skipEmptyValues, default off |
Wrap array in an envelope like { "records": [...] } | No | Wrap the downloaded array yourself, or see the Airtable guide |
Build nested objects from dotted headers (address.city) | No | Output is flat per row; for hierarchy use Grouped mode or json-unflattener afterwards |
| Validate the JSON against a schema | No | Use json-validator on the output |
How type inference maps CSV cells to JSON
Inference tests the trimmed cell. Anything that does not match these patterns is emitted as a string. With inference off, every cell is a string.
| CSV cell | Inferred JSON | Type | Note |
|---|---|---|---|
42 | 42 | number | Integer, kept only if it is a safe integer |
3.14 | 3.14 | number | Decimal |
1.2e3 | 1200 | number | Scientific notation is parsed |
true / false | true / false | boolean | Case-insensitive (TRUE also works) |
null | null | null | The literal word, case-insensitive |
007 | 7 | number | Leading zeros are lost — see edge cases for ID columns |
90210 | 90210 | number | A ZIP code becomes a number — usually not what you want |
+1 555 010 | "+1 555 010" | string | Does not match a number pattern, stays a string |
| `` (empty) | "" | string | Empty stays an empty string unless Skip empty cells is on |
Cookbook
Real CSV-in / JSON-out pairs for REST request bodies. Each shows the exact option that produced it.
Standard typed array for a POST body
ExampleThe default settings — array of objects, inference on, 2-space indent. Numbers and booleans come out typed so a schema-validating endpoint accepts the body.
Input (users.csv):
id,name,age,active
1,Ada,36,true
2,Linus,54,false
Output (array, inference on):
[
{ "id": 1, "name": "Ada", "age": 36, "active": true },
{ "id": 2, "name": "Linus", "age": 54, "active": false }
]
curl -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
--data @users.jsonMinified body to keep the request small
ExampleSwitch indent to Minified for the smallest payload. Same typed values, no whitespace.
Input:
sku,price,inStock
A-1,9.99,true
Output (array, Minified):
[{"sku":"A-1","price":9.99,"inStock":true}]Keep everything a string for a string-only API
ExampleSome endpoints (legacy form bridges, certain search-indexing APIs) want every field as a string. Turn inference off and 9.99 and true stay quoted.
Input:
sku,price,inStock
A-1,9.99,true
Output (array, inference OFF):
[{"sku":"A-1","price":"9.99","inStock":"true"}]Omit blank fields so optional keys are absent
ExampleWith Skip empty cells on, a blank cell drops the key entirely rather than sending "". Useful when the API treats a missing key and an empty string differently (e.g. PATCH semantics).
Input:
id,name,nickname
1,Ada,
2,Linus,Tux
Output (Skip empty cells ON):
[
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Linus", "nickname": "Tux" }
]NDJSON for a line-delimited bulk endpoint
ExampleSwitch to NDJSON when the endpoint reads one record per line (some bulk-ingest and log-style APIs). Each line is a compact object; indentation does not apply.
Input:
id,event
1,login
2,logout
Output (NDJSON):
{"id":1,"event":"login"}
{"id":2,"event":"logout"}Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
ID / ZIP / phone column loses its leading zeros
By designWith inference on, 007 matches the integer pattern and becomes 7; a ZIP like 01970 becomes 1970. If your API needs the literal string, turn off Infer numbers, booleans, null — then every value, including the ID, stays a string. Inference is all-or-nothing per file, so for a mixed file (numeric age, string zip) convert with inference off and let the API coerce, or post-process with json-key-renamer / a script.
Big integer ID is preserved as a string
PreservedAn ID like a 19-digit Twitter/X snowflake or a BIGINT exceeds JavaScript's safe-integer range. Inference checks Number.isSafeInteger and, when it fails, leaves the value as a string rather than silently corrupting it to a rounded float. So 9007199254740993 comes out as "9007199254740993". This is the correct behaviour for IDs — most APIs want big IDs as strings anyway.
Numeric-looking string you wanted to keep quoted
By designA version string 1.0, an order number 0042, or a measurement 42 all get coerced to numbers with inference on. There is no per-column inference toggle — the choice is global. If only one or two columns are affected, the simplest fix is inference off plus an API that coerces the rest, or convert then fix the specific keys downstream.
Duplicate header names collapse
OverwriteIf two columns share a header (e.g. two email columns from a bad export), they map to the same JSON key and the later column wins — the earlier value is lost, because a JSON object cannot have two identical keys. Rename one column before converting (e.g. with csv-header-rename) so both values survive.
Blank header cell becomes column_N
ExpectedIf the header row has an empty cell, that column is keyed column_1, column_2, … by position so no data is silently dropped. Give the column a real name in the source CSV before converting if your API expects a specific field name.
Ragged rows (fewer cells than headers)
ExpectedA row with fewer cells than the header row gets empty strings for the missing trailing columns — the keys are still present with "" values (or omitted if Skip empty cells is on). A row with extra cells beyond the header count has those extra cells dropped, since there is no key for them.
Free tier file or row cap hit
LimitOn the free tier conversion is capped at a 2 MB file and 500 rows. Larger API datasets need Pro (100 MB / 100,000 rows) or higher. If you only need to test the endpoint, export a 100-row sample first; for a one-off large import, split with json-flattener is not it — split the source CSV into chunks under the cap before converting.
Output is flat, but the API wants nested objects
Not supported hereHeaders like address.city are treated as literal keys, not as a path into a nested object — this tool emits one flat object per row. To build { "address": { "city": ... } }, convert here first, then run the array through json-unflattener, which expands dotted keys into nested structure.
Frequently asked questions
Why is my API returning a 400 / validation error on the converted JSON?
The usual cause is a type mismatch. If you converted with inference off, every value is a string and a schema expecting "age": number rejects "age": "42". Re-convert with Infer numbers, booleans, null on. Conversely, if the API wants an ID as a string but inference turned it into a number, turn inference off. Run the output through json-validator to see exactly which field is wrong.
Does the converter wrap the array in an envelope like { "data": [...] }?
No. It emits a bare JSON array ([ {...}, {...} ]). If your API wants an envelope, paste the array into your code where the envelope is built, or edit the file to wrap it. The Airtable guide covers the common { "records": [...] } wrapper case.
How do I keep an ID column as a string so leading zeros survive?
Turn off type inference. With it off, every cell — including IDs — is emitted as a quoted string, so 007 stays "007". The trade-off is that genuinely numeric fields (age, price) also become strings, so this works best when the API coerces or accepts strings for those.
Will my CSV data be uploaded anywhere?
No. Parsing and conversion run entirely in your browser via PapaParse. The CSV's contents — including any customer PII — never reach a JAD Apps server. The only thing recorded server-side is an anonymous run counter when you are signed in, which you can opt out of.
What's the difference between array output and NDJSON for an API?
Array output is one JSON array ([ {...}, {...} ]) — the body shape almost every standard REST POST expects. NDJSON is one compact JSON object per line, with no enclosing brackets, used by bulk-ingest and streaming endpoints (Elasticsearch _bulk, BigQuery loads, some logging APIs). Pick array unless the endpoint's docs specifically ask for line-delimited JSON.
Does indentation affect how the API parses the body?
No — JSON parsers ignore insignificant whitespace, so 2-space, 4-space, and minified bodies parse identically. Indent only changes readability and byte size. Use Minified to shave bytes on a size-capped request; use 2 spaces when you want to read the body in Postman or a log.
Can it build nested JSON for endpoints that want objects inside objects?
Not from dotted headers — each row becomes one flat object. For grouping rows under a key (e.g. by category) use the Grouped output mode. To expand address.city style headers into real nested objects, convert here and then run the result through json-unflattener.
How big a CSV can I convert for an API import?
Free tier: up to 2 MB and 500 rows. Pro: 100 MB and 100,000 rows. Pro+Media: 500 MB and 500,000 rows. Developer: 5 GB with no row cap. For testing an endpoint, a small sample is usually enough; for a large one-time import, split the CSV into chunks under your tier's cap and convert each.
My API expects camelCase keys but my CSV has 'First Name' headers — can the tool rename them?
Not in this tool — the header text becomes the key verbatim. Rename the columns in the source CSV first (or use csv-header-rename) so the headers read firstName, then convert. The converter never alters key text beyond trimming surrounding whitespace.
What happens to a cell that's just whitespace or empty?
An empty cell stays an empty string "" in the JSON. A whitespace-only cell where the trimmed value is empty is also kept as the original string. If you would rather omit the key entirely, turn on Skip empty cells — then blank cells drop their key from that record.
Can I convert a tab-separated (TSV) export the same way?
Yes. The tool accepts .tsv and .txt as well as .csv, and PapaParse auto-detects the delimiter from the file content. A tab- or semicolon-separated export converts with no extra configuration.
Can I automate this conversion in a pipeline before posting to the API?
Yes. The tool runs locally in the browser; for a headless pipeline, pair the @jadapps/runner once and POST the payload to 127.0.0.1:9789/v1/tools/csv-to-json/run. A common pattern: scheduled CSV export → runner converts to a typed JSON array → your script POSTs it to the API. The CSV's data never reaches JAD's servers — it runs on your machine.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.