Transform JSON Data into Tables

Transform JSON data into Excel tables tutorial showing Power Query JSON import nested data expansion and table transformation
Convert JSON data into organized Excel tables with Power Query. This tutorial explains how to import JSON files or API responses, expand nested objects and arrays, transform data into tabular format, clean and reshape records, and load the results into Excel for analysis. Ideal for Excel users, analysts, developers, data professionals, and Microsoft 365 users working with web services, APIs, or structured data sources.

An API hands you a wall of curly braces and square brackets, and somewhere inside is the table you actually need. JSON is how nearly every modern web service returns data, but Excel wants rows and columns. Power Query is the bridge. It reads JSON — from a file, a URL, or a pasted string — and unfolds its nested structure into a clean, refreshable table with a few clicks, no coding required. Once the query is built, a single Refresh pulls the latest data and reshapes it exactly the same way every time.

This guide explains the two building blocks of all JSON (records and lists), shows exactly how to expand each one, and works through six real scenarios from a flat API response to deeply nested data with arrays inside arrays.

The Two Shapes in Every JSON File — Record and List

All JSON, no matter how complex, is built from just two structures. Learn to recognise them and every JSON file becomes readable. A record is a set of named fields (curly braces). A list is an ordered sequence of items (square brackets). Power Query shows each one differently and expands each one with a different button.

Record vs List — how to tell them apart: RECORD { } — named key/value pairs, ONE thing { "name": "Ava", "age": 30, "city": "Dubai" } In Power Query: shows as [Record] Expand with: the ⇔ (two-arrow) icon → picks which fields become columns LIST [ ] — an ordered collection, MANY things [ "red", "green", "blue" ] [ {record}, {record}, {record} ] ← very common: a list of records In Power Query: shows as [List] Convert with: "To Table", then expand the records inside The golden pattern for API data: A LIST of RECORDS → To Table → Expand columns → done. This one pattern handles the majority of real API responses.
Records become columns, lists become rows. Keep this single sentence in mind and JSON stops being intimidating. When you see [Record], you expand it sideways into columns. When you see [List], you turn it into rows first, then expand the records those rows contain.

Getting JSON into Power Query — Three Sources

Power Query can read JSON from three places, and the entry point is slightly different for each. A saved file, a live web URL, and a pasted string each start the same query engine, so once the data is in, every later step is identical regardless of where it came from.

1.
From a file: Data > Get Data > From File > From JSON, then browse to the .json file.
2.
From a URL (live API): Data > Get Data > From Other Sources > From Web, then paste the API endpoint. Refresh re-pulls the latest data each time.
3.
From a pasted string: use Data > Get Data > Blank Query, then in the formula bar wrap your text with Json.Document("...") to parse it.

Example 1: A Flat List of Records — The 80% Case

Most APIs return a list of records: an array where each element is one object with the same fields. A list of employees, orders, or products all follow this shape. This is the pattern you will meet most often, and it resolves in three clicks: convert the list to a table, then expand the record columns.

Source JSON — a list of employee records: [ { "id": 1, "name": "Ava", "dept": "Sales", "salary": 52000 }, { "id": 2, "name": "Ben", "dept": "IT", "salary": 61000 }, { "id": 3, "name": "Cara", "dept": "Finance", "salary": 58000 } ] Steps in Power Query: 1. Power Query shows the whole thing as a single [List]. 2. Click "To Table" (Convert tab) → OK. Now one row per record, each still showing [Record] in the single column. 3. Click the ⇔ expand icon on the column header. Untick "Use original column name as prefix" → OK. Result — a clean table: id │ name │ dept │ salary ───┼──────┼─────────┼─────── 1 │ Ava │ Sales │ 52000 2 │ Ben │ IT │ 61000 3 │ Cara │ Finance │ 58000
That is the core skill. To Table, then Expand. Almost every flat API feed you will ever meet resolves with exactly these two moves.

Example 2: Data Wrapped Inside a Parent Object

Many APIs do not return a bare list. Instead they wrap it inside a parent record with metadata — a status field, a page number, and a "data" field that holds the actual array. Here you first drill into the wrapper to reach the list, then apply the familiar To Table and Expand steps to the array you find inside.

Source JSON — a list nested inside a wrapper: { "status": "ok", "page": 1, "total": 3, "data": [ { "id": 1, "name": "Ava" }, { "id": 2, "name": "Ben" }, { "id": 3, "name": "Cara" } ] } Steps in Power Query: 1. The top level is a [Record] (a wrapper object). 2. Click the "data" field's value → it drills into the [List] inside. (The status/page/total fields are ignored — you only want data.) 3. Now you have a [List] of records → "To Table" → Expand. Tip: the drill-down adds a step like = Source[data] in the formula bar. That single line jumps straight to the array you need.

Example 3: Nested Records — Flattening an Address Object

A record can contain another record. A customer object might hold an "address" field that is itself an object with street, city, and postcode. Expanding the outer record reveals the inner one as a [Record] value, which you then expand again. Each expansion peels back one layer, turning nested fields into flat columns with clear dotted names.

Source JSON — a record inside a record: [ { "name": "Ava", "address": { "city": "Dubai", "postcode": "00000" } }, { "name": "Ben", "address": { "city": "London", "postcode": "EC1A" } } ] Steps: 1. To Table → Expand the top record → you get: name, address where "address" still shows [Record]. 2. Click the ⇔ icon on the "address" column → tick city, postcode. 3. Choose whether to keep the "address." prefix on the new columns. Result: name │ address.city │ address.postcode ─────┼──────────────┼───────────────── Ava │ Dubai │ 00000 Ben │ London │ EC1A

Example 4: A List Inside Each Record — One-to-Many Data

This is where JSON gets genuinely powerful. Each record can contain its own list — an order with multiple line items, or a student with multiple courses. Expanding a list column gives you a choice: expand to new rows (one row per item, duplicating the parent) or aggregate (count or sum the items). Expanding to rows is how you flatten one-to-many data into a normal table.

Source JSON — a list inside each record: [ { "order": "A1", "items": ["Pen","Pad","Clip"] }, { "order": "A2", "items": ["Mug"] } ] Steps: 1. To Table → Expand the record → columns: order, items ([List]). 2. Click the ⇔ icon on "items" → choose "Expand to New Rows". Result — the parent repeats for each child item: order │ items ──────┼────── A1 │ Pen A1 │ Pad A1 │ Clip A2 │ Mug Alternative: instead of expanding, choose to "Extract Values" and join the list into one cell → "Pen, Pad, Clip" in a single row.

Example 5: A Live API Feed That Refreshes Automatically

The real payoff of Power Query is repeatability. Connect to a live API endpoint with From Web, build your expansion steps once, and every Refresh re-pulls current data and reshapes it identically. A currency-rate feed, a stock ticker, or an internal reporting API becomes a self-updating table. Furthermore, you can schedule refreshes so the data is current without anyone touching the query.

Live API pattern: Data > Get Data > From Web URL: https://api.example.com/v1/sales?period=current Power Query parses the response as JSON automatically. Build the To Table / Expand steps ONCE. Then, forever after: Data > Refresh All → re-calls the API, re-applies every step Right-click query > Properties > "Refresh every N minutes" or "Refresh data when opening the file" For APIs needing a key, add it via the Web connector's "HTTP request header" option (e.g. an Authorization header) rather than pasting the key into the URL.
Build steps against a small sample first. If the API can return a page-size parameter, request just a few rows while you design the expansion steps. Once the shape is correct, raise the page size or remove the limit. Designing against thousands of rows makes every click slow; designing against ten makes it instant.

Example 6: Deeply Nested JSON — Arrays Within Arrays

Some payloads nest several levels deep: a company contains departments, each department contains teams, each team contains members. The approach never changes, no matter the depth. You expand one layer at a time, converting lists to rows and records to columns, until the structure is flat. Patience and the record-becomes-columns, list-becomes-rows rule carry you all the way down.

Deep nesting — expand layer by layer: { "company": "Acme", "departments": [ { "name": "Sales", "teams": [ { "team": "East", "members": ["Ava","Ben"] }, { "team": "West", "members": ["Cara"] } ] } ] } Expansion sequence (one layer per step): 1. Drill into "departments" → [List] of dept records 2. To Table → Expand → name, teams([List]) 3. Expand "teams" to New Rows → team, members([List]) 4. Expand "members" to New Rows → one row per member Final flat table: department │ team │ member ───────────┼──────┼─────── Sales │ East │ Ava Sales │ East │ Ben Sales │ West │ Cara Each expand is the same two-arrow icon. Depth only means more repetitions of the same simple move.

Troubleshooting JSON in Power Query

All three problems below are the most common JSON pitfalls. Each has a clear cause and a quick fix.

Every value shows as [Record] or [List] and will not display

This is normal, not an error. Power Query deliberately shows nested structures as clickable [Record] and [List] placeholders instead of trying to cram them into a cell. Click the two-arrow expand icon on the column header to unfold a record into columns, or use "To Table" then expand for a list. Keep expanding until no [Record] or [List] placeholders remain and every column shows plain values. If you accidentally clicked into a value and drilled too far, delete that step in the Applied Steps pane and try the expand icon instead.

Numbers or dates import as text and will not calculate

JSON has no strict date type and often stores numbers as strings, so Power Query may type a column as Text. After expanding, select the column, then use Transform > Data Type to set it to Whole Number, Decimal, or Date as appropriate. Do this as a deliberate step rather than relying on auto-detection, because auto-detected types can change unexpectedly when the source data varies. Setting types explicitly at the end of the query makes the result stable across every refresh, even when new rows contain slightly different formatting.

The refresh breaks after the API changes its response shape

If an API adds, removes, or renames fields, your saved expansion steps can fail because they reference columns that no longer exist. Open the failing step in Applied Steps to see which column it expected. Where an API frequently changes, avoid hard-coding an expanded column list — instead expand with the "Select all columns" option unticked-then-reticked so new fields flow through, or use a more tolerant expansion. For critical feeds, add a comment step and test refreshes after any known API version change so a shape change is caught early rather than in production.

Frequently Asked Questions

  • How do I convert a JSON file to a table in Excel?+
    Go to Data > Get Data > From File > From JSON and select your .json file. Power Query opens and shows the content as a [List] or [Record]. If it is a list of records, click "To Table" then use the two-arrow expand icon on the column to turn the record fields into columns. If it is a record wrapping a list, drill into the list field first, then apply To Table and Expand. Finally set each column's data type and click Close & Load to send the finished table to a worksheet.
  • What is the difference between a Record and a List in Power Query?+
    A Record is a single set of named fields, shown in JSON with curly braces, like one person's details. Power Query displays it as [Record] and you expand it sideways into columns using the two-arrow icon. A List is an ordered collection of items, shown in JSON with square brackets, like an array of many people. Power Query displays it as [List] and you turn it into rows using "To Table" before expanding the records inside. The simple rule is that records become columns and lists become rows.
  • Can Power Query pull JSON from a live API and keep it up to date?+
    Yes. Use Data > Get Data > From Other Sources > From Web and paste the API endpoint URL. Power Query parses the JSON response and lets you build expansion steps once. Every time you click Refresh, it re-calls the API and re-applies those steps to the fresh data. You can also set the query to refresh on a schedule or when the file opens, via the query's Properties. For APIs that require an authentication key, add it as an HTTP request header in the Web connector rather than embedding it in the URL.
  • How do I handle JSON where each record contains a list of items?+
    This is a one-to-many structure, such as an order containing several line items. After converting to a table and expanding the record, the list field shows as [List] in its column. Click the two-arrow icon on that column and choose "Expand to New Rows". Power Query then creates one row per item and repeats the parent values (like the order number) on each new row, producing a normal flattened table. Alternatively, choose "Extract Values" to join the list into a single delimited cell if you prefer one row per parent.