How to convert json to a markdown api documentation table
- Step 1Shape your field list as a JSON array of objects — Each object is one row, each key is one column. A typical API-doc shape is
[{ "field": "id", "type": "string", "required": true, "description": "UUID v4" }, ...]. Use the same keys in every object for a clean table; if some objects omit a key, that cell is left blank rather than the row breaking. - Step 2Open the tool and drop the JSON file — Drop a
.json,.ndjson,.jsonl, or.txtfile onto the dropzone, or an.xlsx/.xlsspreadsheet (the first sheet is read as rows and converted to JSON objects first). Parsing runs in your browser — nothing is uploaded. - Step 3Pick a column alignment — The single formatting control is alignment: Left (
:---), Center (:---:), or Right (---:). It applies uniformly to every column — there is no per-column alignment in the UI. Left is the conventional choice for text-heavy API-doc tables. - Step 4Click Convert to Markdown — The tool builds a header row, an alignment-separator row, and one row per field object. Columns come from the keys; if you passed a single object instead of an array, you get a one-row table.
- Step 5Review the rendered table in the preview — The output pane shows the raw Markdown (truncated to 5,000 characters in the on-screen preview for very large tables; the full text is still copied/downloaded). Confirm the header order matches your intended Field / Type / Required / Description column order — header order follows the key order of your objects.
- Step 6Copy or download and paste into your docs — Use Copy to grab the Markdown, or Download MD to save a
.mdfile. Paste it under a## Response fieldsheading you write yourself — the tool produces the table, you supply the surrounding prose and headings.
What the converter actually produces
The tool maps a JSON array of objects to one Markdown pipe table. These are the real behaviors, taken from the conversion logic — not aspirational features.
| Input shape | Output | Notes |
|---|---|---|
Array of objects [{...}, {...}] | One header row + separator + one body row per object | The standard API-field-list case |
Single object {...} | A one-row table | The object is wrapped in an array internally |
| Object with a nested object/array value | That cell contains JSON.stringify(value) inline | e.g. enum: ["a","b"] renders as ["a","b"] in the cell, not as sub-rows |
| Objects with different key sets | Columns = union of all keys, first-seen order; missing keys → blank cells | No error — sparse rows are fine |
A null or missing value | Empty cell | null/undefined become an empty string |
Alignment option → separator row
Alignment is the only formatting control and is applied to all columns uniformly. The separator row is what GitHub uses to decide column alignment.
| Alignment (UI) | Separator emitted | When to use it |
|---|---|---|
| Left (default) | :--- | Field names, types, descriptions — the usual API-doc case |
| Center | :---: | Short status columns like Required (✓/✗) when you want them centered |
| Right | ---: | Numeric columns (max length, default value) read better right-aligned |
Limits and tier behavior
Real numbers from the tier configuration. JSON to Markdown is a Pro tool; the free tier is gated by file size.
| Constraint | Free | Pro |
|---|---|---|
| Input file size | 2 MB | 100 MB |
| Rows (field objects) | No tool-level row cap (file-size bound) | Practical limit ~100k rows / browser memory |
| Files per run | 1 | 10 (bulk mode) |
| Processing location | Browser only | Browser only |
Cookbook
Real field-definition arrays from real API docs, with the exact Markdown the tool emits. Internal field names are anonymized.
Endpoint response fields → reference table
ExampleThe canonical case: an array where each object documents one response field. Header order follows the key order of the first object, so put your keys in the order you want the columns.
Input (fields.json):
[
{ "field": "id", "type": "string", "required": true, "description": "UUID v4 of the resource" },
{ "field": "email", "type": "string", "required": true, "description": "RFC 5322 address" },
{ "field": "created_at", "type": "string", "required": false, "description": "ISO 8601 timestamp" }
]
Output (alignment: left):
| field | type | required | description |
| :--- | :--- | :--- | :--- |
| id | string | true | UUID v4 of the resource |
| email | string | true | RFC 5322 address |
| created_at | string | false | ISO 8601 timestamp |A type containing a pipe is escaped, not column-split
ExampleTypeScript-style union types use |. Inside a Markdown pipe table a raw | would create a phantom column. The converter backslash-escapes pipes inside cell values so the table stays intact.
Input:
[{ "field": "status", "type": "string | null", "description": "Active or absent" }]
Output:
| field | type | description |
| :--- | :--- | :--- |
| status | string \| null | Active or absent |
Renders as a single 'type' cell reading: string | nullNested enum value inlined as JSON
ExampleIf a field object holds an array or object value (an enum, an example payload), that value is JSON-stringified into the cell rather than expanded into sub-rows. Good for short enums; for large nested examples, document them in prose below the table.
Input:
[{ "field": "role", "type": "enum", "enum": ["admin", "editor", "viewer"] }]
Output:
| field | type | enum |
| :--- | :--- | :--- |
| role | enum | ["admin","editor","viewer"] |Optional column appears only where present
ExampleAdd a deprecated key to one field and the column appears for all rows, blank where the key is absent. This is the union-of-keys behavior — handy for marking a single deprecated field without restructuring the whole array.
Input:
[
{ "field": "id", "type": "string" },
{ "field": "legacy_id", "type": "number", "deprecated": true }
]
Output:
| field | type | deprecated |
| :--- | :--- | :--- |
| id | string | |
| legacy_id | number | true |Multi-line description collapsed to one row
ExampleA description with an embedded newline cannot live in a pipe table cell. The converter replaces the newline with a space so the field stays on one row. If you need multi-paragraph prose, keep it out of the table.
Input:
[{ "field": "webhook_url", "description": "Callback URL.\nMust be HTTPS." }]
Output:
| field | description |
| :--- | :--- |
| webhook_url | Callback URL. Must be HTTPS. |Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Input is an OpenAPI/Swagger spec, not a field array
Wrong shapeThe tool does not parse OpenAPI structure. A raw openapi.json is a deeply nested object — converting it yields a one-row table with stringified nested values, not a field reference. First extract the field list (e.g. with json-path-extractor against $.components.schemas.User.properties), reshape it into an array of {field,type,...} objects, then convert.
Top-level JSON is an object whose values are field objects
One-row tableA shape like { "id": {...}, "email": {...} } (keys are field names, values are descriptors) becomes a single row with id and email columns holding stringified objects — not one row per field. Convert the object to an array first; json-transposer or a small reshape gets you to [{field, ...}].
Invalid JSON (trailing comma, single quotes)
Parse errorThe input is run through JSON.parse, which is strict: trailing commas, single-quoted keys, comments, and unquoted keys all throw. Run the file through json-format-fixer to repair common syntax issues, or json-validator to see the exact line of the error, then re-convert.
A field value contains a literal pipe
PreservedPipes inside a cell (union types like string | number) are backslash-escaped to \| so the table structure survives. The rendered cell shows string | number correctly. This happens automatically — there is no toggle to turn it off in the UI.
Empty array `[]`
Header-onlyAn empty array produces a table with no columns and no rows — effectively | |\n| :--- | with nothing useful. There are no keys to detect, so there are no headers. Provide at least one object so the converter has keys to build columns from.
Objects with deeply nested example payloads
Inlined JSONIf a field carries a large example object, the whole thing is JSON.stringify-ed into one cell, which can be unreadable in a table. Document large examples in a fenced code block beneath the table instead, keeping the table to scalar columns (field, type, required, short description).
File over the free 2 MB limit
Blocked on freeThe free tier caps input at 2 MB. A field-definition array almost never approaches that, but a file padded with large inline examples can. Upgrade to Pro (100 MB) or trim the example payloads out of the array before converting.
Key order differs between objects
First-seen winsColumn order is the order keys are first encountered across the array, not alphabetical. If object 1 has field,type and object 2 has type,field,notes, the columns are field, type, notes. To force a specific column order, make the first object define all keys in your desired order.
HTML in a description
Passed throughDespite the internal option name, the converter's escaping handles pipes and newlines — it does not strip or escape HTML tags like <code>. GitHub-flavored Markdown renders inline HTML, so a <br> in a description will render as a line break. Keep raw HTML out of cells if your renderer sanitizes it.
Frequently asked questions
Does this generate full API documentation or just a table?
Just a table. It converts a JSON array of objects into one GitHub-flavored Markdown pipe table — one row per object, columns auto-detected from the keys. It does not produce ### Endpoint headings, request/response sections, or prose. You write the surrounding documentation and paste the table into it.
Can it read an OpenAPI / Swagger spec directly?
No. It does not understand OpenAPI structure. Feed it a flat array of field descriptors ([{field, type, required, description}, ...]). To get that array out of a spec, extract the properties with json-path-extractor and reshape into an array first.
How are the table columns chosen?
Automatically, as the union of every key across all objects, in first-seen order. There is no column picker in the UI. If you want columns in a specific order, define all keys in that order in the first object of the array.
Can I set different alignment per column?
No. The only formatting control is a single alignment — Left, Center, or Right — applied to every column at once. The separator row uses :---, :---:, or ---: accordingly for all columns.
What happens to a `type` value like `string | null`?
The pipe is backslash-escaped to \| inside the cell, so the table does not split into an extra column. It renders correctly as string | null on GitHub and other GFM renderers.
What if a field object has a nested object or array value?
It is JSON-stringified inline into that cell (e.g. an enum array becomes ["a","b"]). It is not expanded into sub-rows. For large nested examples, document them in a code block below the table instead.
Why is one of my rows missing a value?
That object didn't include the key for that column, so the cell is blank. Columns are the union of all keys; any object missing a key gets an empty cell rather than an error. Add the key to every object for full coverage.
What input formats are accepted?
.json, .ndjson, .jsonl, and .txt, plus .xlsx/.xls spreadsheets. For a spreadsheet, the first sheet is read row-by-row and converted to a JSON array of objects before the table is built — handy if your field list lives in a sheet.
Is my schema uploaded anywhere?
No. Conversion is 100% client-side — the JSON is parsed and the table is built in your browser tab. Nothing is sent to JAD Apps servers. Only an anonymous run counter is recorded if you are signed in, which you can opt out of.
How big a file can I convert?
Free is capped at 2 MB; Pro raises it to 100 MB. Field-definition arrays are tiny, so free is almost always enough — the cap only matters if you have inlined large example payloads into the array.
Will the table render on Notion / Confluence / Docusaurus?
GitHub-flavored pipe tables render on GitHub, GitLab, Bitbucket, Docusaurus, MkDocs, and Hugo out of the box. Notion supports them on Markdown import; Confluence's native editor needs the Markdown macro or paste-as-Markdown. The output is standard GFM, so anywhere that supports pipe tables will render it.
Can I automate this in a docs build pipeline?
Yes — JSON to Markdown is a Pro tool with API access. Pair the local runner and POST your field array to the tool endpoint to regenerate the reference table whenever the schema changes. The conversion still runs locally on your machine, so the schema never reaches JAD's servers.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.