How to query a JSON file with SQL
Most API exports come as JSON, and JSON is miserable to read by eye. Turning it into a table usually means writing a script. Real exports come in a few predictable shapes, though, so the conversion can happen the moment you open the file.
The shape that just works
An array of flat objects is already a table. Each object is a row and each key is a column. That’s what most "Export as JSON" buttons produce, and it needs no prep.
If a key only shows up in some objects, it still becomes a column. Rows without it get a null, like a blank cell in a spreadsheet.
SELECT customer_id, plan, monthly_revenue
FROM subscriptions
WHERE monthly_revenue > 100
ORDER BY monthly_revenue DESC;Wrapper objects from APIs
API responses usually wrap the rows in an envelope with some metadata, like `{ "results": [...], "next": "..." }`. The array is your data. The rest is paging info.
When the object holds exactly one array, NoEgress unwraps it for you. A saved API response loads as the table you wanted, not as one row of metadata.
Nested values
A table cell holds a single value, so nested objects and arrays are kept as JSON text. A field like `tags: ["a","b"]` arrives as the string `["a","b"]`. You can still see it and query it.
For a quick question, plain string matching is often all you need.
SELECT id, tags
FROM events
WHERE tags LIKE '%urgent%';Pull out a nested field
When string matching isn’t enough, DuckDB can parse that JSON text and extract the field you want.
SELECT
id,
json_extract_string(meta, '$.source') AS source,
json_extract_string(meta, '$.campaign') AS campaign
FROM events
WHERE json_extract_string(meta, '$.source') = 'email';Check what actually loaded
JSON has no schema. The columns you get depend on which keys appear in the file. Profile the table before you query it, so you know which fields are filled in often enough to group by and which are mostly empty.
SELECT
COUNT(*) AS rows,
COUNT(campaign) AS has_campaign,
COUNT(DISTINCT source) AS distinct_sources
FROM events;Join JSON to a spreadsheet
Every file you open lands in the same session. A JSON export from one system joins straight to a CSV or Excel file from another. That’s usually the real job behind "how do I query this JSON?"
SELECT
e.id,
e.source,
c.company_name
FROM events e
JOIN customers c
ON e.customer_id = c.customer_id;Frequently asked
Can I run SQL on a JSON file without converting it first?
Yes. An array of objects becomes a table as is. A wrapper object holding one array gets unwrapped automatically.
What happens to nested objects and arrays?
They’re kept as JSON text in the cell. You can match on them as strings or parse them with DuckDB’s json_extract functions.
What if my JSON is a single object rather than an array?
It loads as a one-row table. Handy for looking at a saved API response or a config file.
More guides
NoEgress vs Excel, Power Query, ChatGPT and DuckDB
How to use ChatGPT-style AI on sensitive spreadsheets without uploading them
An Excel alternative for files with millions of rows
How to query a CSV file with SQL
How to open large CSV files Excel can't handle
How to run SQL on an Excel spreadsheet
How to analyze confidential data without uploading it
How to check if a website uploads your file
How to compare two CSV files and find what changed
Data quality checks to run before trusting a dataset
DuckDB online: run DuckDB in your browser with nothing to install
How to ask a local AI model about your CSV or Excel data
How to remove duplicate rows from a CSV or Excel file
How to split a CSV or Excel file into multiple files by column