Flatten nested JSON to table with dot-notation keys
Turn nested data into a flat list of rows, each with its full path: the city inside a customer's address becomes customer.address.city, and the first order's number becomes orders.0.id with the value 7. Empty sections are kept. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Every nested property receives a dot-notation path, and array positions become numeric path segments, so values such as an order identifier can be addressed consistently as orders.0.id. The result is convenient for spreadsheet rows, database staging tables, logs, mapping tools, and any workflow that needs predictable paths instead of nested structures.
From nested records to rows with full paths
Nested data keeps related values together, which suits programs, but spreadsheets, staging tables, logs and mapping tools expect a flat list. This tool walks through every section and every list and joins each step with a dot: a name inside customer becomes customer.name, and the identifier of the first order becomes orders.0.id, because list positions are numbered from zero. Each final value becomes one row holding its path and its value, in the order it appears, so the result drops straight into a two-column table. The example produces 8 rows, from customer.name with Ada to metadata with an empty section.
What is kept, and one trap to know about
Values keep their kind, and empty sections or lists are kept as values instead of disappearing, so the flat version still shows that those paths existed. The trap is a dot that is already part of a name: it is not escaped, so a name written as user.name looks exactly like a name stored inside user. If you need to rebuild the original reliably, rename such entries first or keep the original next to the flat copy. Flat paths are also handy for comparing two records, because every difference can be located by its full path.
Convert nested structure into predictable paths
A value stored under a customer object and then a name property becomes customer.name. The first element of an orders array is addressed through orders.0, so its identifier becomes orders.0.id. Each leaf is returned as a row with a key and a value, which makes the output straightforward to display, filter, or transform into columns.
Strings remain strings, numbers remain numbers, booleans remain booleans, and null remains null. Empty objects and empty arrays are emitted as values rather than silently disappearing, so the flattened output still records that those paths existed in the source document.
Prepare input and interpret the output
Its root must be either an object or an array. This requirement avoids an ambiguous empty key for standalone strings, numbers, booleans, or null. Object property names are used exactly as supplied, while array indexes are written as zero-based numeric segments.
If the root itself is an empty object or array, the returned key is an empty string and the value preserves that empty container. Be aware that dots already present inside an original property name are not escaped. For example, a literal property named user.name produces the same visible path as a nested user object containing name.
What you can do with it
Compare structured records
Flatten two objects into stable path/value sequences so differences can be located by their full paths.
FAQ
How are arrays represented?
Array positions become zero-based numeric path segments, such as orders.0.id and orders.1.id.
What happens to empty objects and arrays?
They are returned as leaf values so their paths are not lost. An empty root container uses an empty key.
Are dots in original property names escaped?
No. Property names are preserved exactly, so rename dotted properties first if paths must be reversible without ambiguity.
Are numbers and true or false values kept as they are?
Yes. Text stays text, numbers stay numbers, true and false stay true and false, and empty values stay empty. In the example, orders.0.id keeps the number 7 and customer.active keeps true, so nothing has to be converted back afterwards.
Can I flatten a single value such as a number or a word?
No. The top level must be a record with names or a list, because a lone value would have no path at all. A single number, word, true or false or empty value is turned down with a clear message instead of a confusing result.
Related tools
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/data/flatten-nested-json \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"json":"{\"customer\":{\"name\":\"Ada\",\"address\":{\"city\":\"London\"}},\"orders\":[{\"id\":7,\"paid\":true}]}"}'const res = await fetch("https://api.kit.forhosting.com/data/flatten-nested-json", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"json": "{\"customer\":{\"name\":\"Ada\",\"address\":{\"city\":\"London\"}},\"orders\":[{\"id\":7,\"paid\":true}]}"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/data/flatten-nested-json",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"json": "{\"customer\":{\"name\":\"Ada\",\"address\":{\"city\":\"London\"}},\"orders\":[{\"id\":7,\"paid\":true}]}"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/data/flatten-nested-json", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"json":"{\\"customer\\":{\\"name\\":\\"Ada\\",\\"address\\":{\\"city\\":\\"London\\"}},\\"orders\\":[{\\"id\\":7,\\"paid\\":true}]}"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"json":"{\"customer\":{\"name\":\"Ada\",\"address\":{\"city\":\"London\"}},\"orders\":[{\"id\":7,\"paid\":true}]}"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/data/flatten-nested-json", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"json": "{\"customer\":{\"name\":\"Ada\",\"address\":{\"city\":\"London\"}},\"orders\":[{\"id\":7,\"paid\":true}]}"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "data.flatten_nested_json",
"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
max_mb | 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. |
Technical notes
Turn a nested JSON object or array into a simple sequence of key/value rows without writing a one-off traversal script. Processing is deterministic, preserves JSON value types, and rejects malformed JSON or a scalar root with a clear input error.
Convert nested structure into predictable paths
Nested JSON is ideal for APIs because it keeps related values together, but many reporting and import tools expect a flat sequence of fields. This capability walks every object property and array element, joining each segment with a dot. Traversal follows the order present in the parsed JSON, providing stable results for the same input.
Prepare input and interpret the output
Provide the complete JSON document in the json field as text. The response contains pairs, an array whose entries have key and value fields and can therefore be treated directly as table rows. If your data uses dotted property names and path identity must be reversible, rename those properties before flattening or retain the original JSON alongside the flattened result.
Use flat pairs in data workflows
The flattened pairs work well as an intermediate representation. A spreadsheet automation can place keys in one column and values in another; an ingestion job can pivot selected paths into a wide table row; and a comparison tool can index pairs by key before checking two documents. Numeric array segments also make repeated records explicit instead of merging values from different elements. Because the operation performs no network requests and uses no random values, timestamps, or model inference, identical JSON text produces identical output every time. Invalid JSON fails rather than returning a partial result, and valid scalar JSON fails because it does not meet the object-or-array contract. For very large documents, consider whether a streaming or schema-specific pipeline is more appropriate, since flattening necessarily creates one output row for every primitive leaf or empty container in the entire document.
Integration use cases
Prepare an API response for a table
Turn nested response fields into explicit paths that can be selected, mapped, or shown as key/value rows.
Build import mappings
Inspect dot-notation paths before mapping selected JSON values into spreadsheet or database columns.
Technical questions
What does it cost?
Each API item costs $0.002. The browser runner can process the input locally for free.
Are JSON value types preserved?
Yes. Strings, numbers, booleans, null, and empty containers retain their JSON types in the value field.
Can I submit a JSON string or number as the root?
No. The parsed root must be an object or array; scalar roots return an invalid input error.