HTML Table to JSON Converter
Turn a table on a page into data. Paste the markup, pick the table you actually meant, and get an array of objects with one type per column.
To convert an HTML table to JSON, paste the markup or drop a saved .html file above. Every table on the page is found and listed; the one you choose becomes an array of objects keyed by its header cells, with a single type per column and null for empty cells. Colspan and rowspan are resolved into a proper rectangle before anything is typed.
Want to reshape the result? Open the app
The table is on the page and the API is not
The situation is always some version of the same one. The data exists, it is visible in a browser, and there is no export button and no endpoint. A regulator publishes a table. A vendor's status page lists its regions. An internal tool renders a report and offers a print button.
Writing a scraper for a one-off is disproportionate, and pasting into a spreadsheet loses the structure. Pasting the markup here takes ten seconds and produces something a program can use.
Every table on the page, not the first one
A real page has more than one table in it. A Wikipedia article has an infobox before the table you want. A financial report has a summary above the detail. A saved dashboard has a legend rendered as a table.
Taking the first one is the default behaviour of nearly every converter on this term, and on a Wikipedia article it means you get the infobox. Here every table is found, labelled with its caption where it has one, listed with its row and column counts, and one click away.
A table nested inside another table's cell is treated as layout rather than data, which is what it almost always is in older markup. Its text stays in the cell that holds it and a note tells you how many were skipped.
Worked example
A fragment of the sort you would copy out of a page source:
<table>
<caption>Revenue by region</caption>
<thead>
<tr><th>Region</th><th>Code</th><th>Q1</th><th>Manager</th></tr>
</thead>
<tbody>
<tr><td>North Coast</td><td>01</td><td>1840</td><td>Ada Lovelace</td></tr>
<tr><td>Lakeside</td><td>02</td><td>975</td><td></td></tr>
</tbody>
</table>
And the JSON:
[
{ "Region": "North Coast", "Code": "01", "Q1": 1840, "Manager": "Ada Lovelace" },
{ "Region": "Lakeside", "Code": "02", "Q1": 975, "Manager": null }
]
Code stays a string because of the padding, Q1 is numeric because every value in it is, and the empty manager cell is null rather than an empty string. Keys keep their original capitalisation, because a header cell is a label somebody wrote and mangling it into snake case is a decision you should get to make yourself.
Colspan and rowspan, resolved rather than ignored
A cell with colspan="3" occupies three columns, and a converter that ignores that produces rows of different widths that then get padded in the wrong places. The value is repeated across the columns it covers, so the rectangle lines up.
A cell with rowspan="2" fills downward into the row below, which is how a merged category label in the first column is meant to read. rowspan="0" means "to the end of this section" and is handled, as is a rowspan that overshoots the last row, which is capped rather than used to invent empty rows.
This is the difference between reading a hand-built HTML table and reading one generated by a reporting tool. The generated ones are rectangles; the hand-built ones are full of spans, and they are the ones people need to convert.
Header detection, and what happens without one
A first row made entirely of <th> cells is a header row and its text becomes the keys. That is unambiguous and it is what well-formed markup gives you.
When there is no such row, the columns are named column_1, column_2 and so on, and every row of the table is kept as data. The alternative, promoting the first data row into a header, throws away a row on a guess, and on a table of numbers it produces keys like 1840.
Duplicate header cells are renamed rather than dropped. A table with two columns both called 2024 becomes 2024 and 2024_2, with a note. A competing converter on this term leaves them colliding, so the second column silently overwrites the first.
Frequently Asked Questions
The page has four tables. Do I get the right one?
You get all four listed with their captions and their row and column counts, and the first converts immediately. Taking whichever table appears first is what most converters do, and on a Wikipedia article that means you get the infobox.
Does it handle colspan and rowspan?
Yes. A colspan repeats the value across the columns it covers and a rowspan fills it down, so the result is a proper rectangle. rowspan="0" and a rowspan that overshoots the last row are both handled rather than producing phantom empty rows.
What happens if the table has no header row?
Columns are named column_1, column_2 and so on, and every row is kept as data. Promoting the first data row into a header would throw a row away on a guess, and on a table of figures it produces keys like 1840.
Do duplicate headers collide?
No. A second column called 2024 becomes 2024_2, with a note saying so. A competing converter leaves them colliding, which means the second column silently overwrites the first and you lose a column without being told.
Are keys converted to snake case?
No. A header cell is a label somebody wrote, and normalising it is a decision with several reasonable answers. The keys are exactly the header text, so you can rename them the way your consumer wants rather than the way a converter guessed.
Can I drop a whole saved page in?
Yes. Save the page from your browser and drop the .html file, or paste the source. You do not have to isolate the table first, which is the part that usually takes longer than the conversion.
Get the table off the page
Every table found, spans resolved, one type per column. Paste and copy.
Back to the converter