JSON to Parquet Converter
Turn an API export into something a data stack can query. Real column types, real compression, and a file pandas, Polars, DuckDB and Spark all open with no schema argument.
To convert JSON to Parquet, paste your array of objects above. Nested objects flatten into dotted columns, each column is typed once from all of its values, and DuckDB running in your browser writes the file with BIGINT, DOUBLE, BOOLEAN, DATE or VARCHAR columns. Choose Snappy, Zstd or no compression, then download.
Want to filter or join before writing it? Open the app
JSON is a transport format and Parquet is a storage format
JSON is excellent at moving a few hundred records between two systems and terrible at holding a few million for analysis. It is verbose, it has no schema, it repeats every key on every record, and reading one column out of it means parsing all of them.
Parquet is the opposite on every count: columnar, typed, compressed, and readable one column at a time. Converting an export once, at the point it enters your storage, saves the parsing cost on every query afterwards.
The size difference is usually dramatic. A JSON export where every record repeats a dozen key names and a handful of low-cardinality status strings compresses to a small fraction of its size, because a columnar format stores each of those once.
The schema you end up with
Each column is typed once from all of its values and that decision is handed to DuckDB explicitly rather than being sniffed again:
- BIGINT for a column of whole numbers, so counts and identifiers stay exact.
- DOUBLE for a numeric column with fractions in it.
- BOOLEAN for a column of real JSON booleans.
- DATE for a column of exact ISO dates, which is what makes a date predicate push down at query time.
- VARCHAR for everything else, including any column with a padded value in it.
Declaring the types explicitly is what stops the schema disagreeing with the preview you just looked at. Left to sniff, a loader could decide a column of order references is an integer and silently change every one of them.
Flattening, and the honest trade
Nested objects become dotted columns: customer.id, customer.name, customer.city. Parquet does support genuinely nested types, and this writes flat columns instead, which is worth being clear about.
Flat columns are what most query engines are fastest on, what every BI tool can see without configuration, and what a person reading the schema can understand. Nested Parquet is more faithful to the source and materially harder to work with downstream.
Nesting past four levels stays as JSON text in a VARCHAR column, and an array of objects does too. That keeps the row count identical to the record count, which matters because a converter that spread arrays into rows would quietly multiply your data.
Worked example
Read the result back in Python and there is nothing left to configure:
>>> import pandas as pd
>>> df = pd.read_parquet("orders.parquet")
>>> df.dtypes
order_id object
placed_at object
status object
customer.id int64
customer.name object
items int64
total float64
gift bool
dtype: object
No dtype map, no json_normalize call, no post-processing to fix the one column that arrived as a string. The flattening happened once, in the file, rather than in every script that reads it.
What JSON shapes are accepted
- An array of objects is the ordinary case, one row per object.
- An object wrapping an array, such as
{ "data": [ ... ] }, is unwrapped automatically, which is what most API exports look like. - JSON Lines is recognised when the whole document will not parse but every line parses on its own, with a note saying so.
- A single object becomes a one-row file, which is occasionally what you want and never an error.
- A file with one bad line produces an error naming the line rather than a byte offset, because "unexpected token at position 91204" is useless in a large export.
Columns from the union of every record
Real exports are not uniform. A field added to an API in March is absent from every record before it, and a converter that builds its schema from the first object drops that field entirely.
The columns here are the union of every key across every record, in the order they were first seen. A field present in one record out of fifty thousand still gets a column, with nulls everywhere else, which is the honest representation and the one a query can ask about.
The type is then decided from every value that column does have, so a field that is a number in most records and a string in a handful becomes VARCHAR rather than failing the write.
How it runs and what that means
DuckDB is compiled to WebAssembly and executes inside the page. The first conversion downloads that engine, a few megabytes, and caches it, so the second is immediate.
Nothing is uploaded. For a JSON export that means the customer records, the event log or the support transcript you are converting stays on your machine, which is frequently the reason people use a browser-based tool for this rather than a hosted one.
The practical ceiling is your tab's memory rather than a policy. For anything well past a hundred megabytes the full editor streams and is the better route, and it writes Parquet too.
Frequently Asked Questions
Does the Parquet file carry real types?
Yes: BIGINT, DOUBLE, BOOLEAN, DATE and VARCHAR, decided once per column from all the values and handed to DuckDB explicitly rather than sniffed a second time. That is the entire advantage over routing through a CSV.
Are nested objects written as nested Parquet types?
No, they are flattened into dotted columns. Parquet supports nesting, but flat columns are what query engines are fastest on and what every BI tool can see without configuration. Nesting past four levels stays as JSON text in a VARCHAR column.
Will pandas need arguments to read it?
No. The schema is in the file, so read_parquet returns the right dtypes with no dtype map and no json_normalize call. The flattening happened once when the file was written rather than in every script that reads it.
Snappy or Zstd?
Snappy unless you have a reason. Every Parquet implementation reads it and it decompresses fast. Zstd is noticeably smaller and every current reader supports it, which suits cold storage; very old pyarrow builds may not.
Is my JSON uploaded?
No. It is parsed and written to Parquet by DuckDB compiled to WebAssembly, both inside your browser tab. Nothing is sent, stored or logged, which is usually the deciding factor when the export holds customer records.
Why is the first conversion slower?
The DuckDB WebAssembly engine is a few megabytes and downloads on first use, then stays cached for the session. Later conversions start immediately.
Store the export properly
Columnar, typed, compressed, and it never leaves your machine.
Back to the converter