Convert JSON to CSV
Convert a JSON array into dependable CSV without writing a script or wondering whether irregular records silently shifted your columns.
Run — free
This strict converter takes the keys from the first object as the header, checks that every object has exactly the same keys, and then escapes commas, quotation marks, and line breaks according to standard CSV rules. Empty arrays, nested values, invalid JSON, and mismatched records produce a clear error instead of an ambiguous spreadsheet. The same deterministic conversion is available in the browser and through the API.
Prepare a uniform array of flat objects
Start with valid JSON whose top-level value is an array. Every item must be an object, and every field value must be a JSON scalar—string, number, boolean, or null. Nested objects and arrays are deliberately rejected because there is no single honest way to place a nested structure into a CSV cell. Some converters stringify nested values, while others flatten paths or multiply rows; each choice changes the meaning of the data. This tool stays predictable by requiring the tabular shape to be explicit before conversion. The array must contain at least one object, and the first object must contain at least one key. Its key order becomes the CSV column order, so arrange that first record as you want the spreadsheet to appear. JSON object key order is retained by the converter. Values may include commas, quotation marks, carriage returns, or line breaks; you do not need to escape those for CSV yourself, because the serializer handles them after the JSON has been parsed.
Understand strict header and row validation
The header row is derived only from the first object's keys. Each remaining object is then compared with that header as a complete key set. A record with a missing key fails, and a record with an extra key also fails, even if the extra value is empty or null. The order of keys in later objects does not matter: values are always emitted in the first object's column order. This strict check prevents a subtle class of export bugs. A permissive converter may create a growing union of columns, leave unexplained blanks, or shift data depending on which record introduced a property. Here, irregular input stops with an error that identifies the mismatched item. Null is allowed as an intentional flat value and becomes an empty CSV cell. Missing keys are not treated as null because absence and an explicit null can represent different states in a source system. Validate or normalize irregular records upstream, then submit the uniform result for a stable export.
Use the CSV safely in spreadsheets and pipelines
After validation, each header and value is serialized as one CSV cell. Cells containing a comma, quotation mark, carriage return, or line break are wrapped in double quotes, and quotation marks inside a cell are doubled. Numbers and booleans use their JSON text representation, while null produces an empty cell. The result contains one header row followed by one row per object, joined with line-feed characters. Because the algorithm is deterministic and does not use a network, clock, random source, model, or external service, identical input produces identical output. That makes it suitable for tests, scheduled exports, build steps, and repeatable data handoffs. In the browser, paste the JSON and copy the generated CSV without sending the data elsewhere. For automation, call the API at the published price of $0.002 per request. Save the returned CSV text with a .csv extension, then open or import it with the delimiter set to a comma.
What you can do with it
Export API records for a spreadsheet
Turn a uniform array returned by an internal API into columns that operations or finance teams can sort, filter, and review.
Create deterministic test fixtures
Generate stable CSV fixtures from JSON records while failing immediately if a test record introduces or omits a field.
Validate a tabular data handoff
Enforce one shared schema across all records before delivering a CSV to an importer, analyst, customer, or reporting pipeline.
FAQ
How is the CSV header chosen?
The converter uses the first object's keys in their existing order. Every later object must have exactly that same set of keys.
What happens when objects have different keys?
The conversion fails with an invalid-input error identifying the mismatched item. Extra and missing keys are both rejected.
Are nested objects or arrays supported as values?
No. Each value must be a string, number, boolean, or null. Normalize or flatten nested data before using this strict converter.
How are commas, quotes, and line breaks escaped?
A cell containing any of those characters is enclosed in double quotes, and each quotation mark inside the cell is doubled.
What does the API conversion cost?
Each successful API request costs $0.002. The browser version runs locally for free.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/web/json-to-csv \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"[{\"name\":\"Ada\",\"role\":\"Engineer\",\"active\":true},{\"name\":\"Grace\",\"role\":\"Admiral\",\"active\":false}]"}'const res = await fetch("https://api.kit.forhosting.com/web/json-to-csv", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "[{\"name\":\"Ada\",\"role\":\"Engineer\",\"active\":true},{\"name\":\"Grace\",\"role\":\"Admiral\",\"active\":false}]"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/json-to-csv",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "[{\"name\":\"Ada\",\"role\":\"Engineer\",\"active\":true},{\"name\":\"Grace\",\"role\":\"Admiral\",\"active\":false}]"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/json-to-csv", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"[{\\"name\\":\\"Ada\\",\\"role\\":\\"Engineer\\",\\"active\\":true},{\\"name\\":\\"Grace\\",\\"role\\":\\"Admiral\\",\\"active\\":false}]"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"[{\"name\":\"Ada\",\"role\":\"Engineer\",\"active\":true},{\"name\":\"Grace\",\"role\":\"Admiral\",\"active\":false}]"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/json-to-csv", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "[{\"name\":\"Ada\",\"role\":\"Engineer\",\"active\":true},{\"name\":\"Grace\",\"role\":\"Admiral\",\"active\":false}]"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.json_to_csv",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |