JSON vs JSONL — When to Use Each Format
JSON arrays suit APIs and config files. JSON Lines fits logs, streaming, and big data. Compare structure, parsing, and trade-offs for your pipeline.
We had a 4 GB "JSON file" that crashed every parser in the pipeline. Open it in an editor and the first line was { and the last was } — classic array wrapping millions of records. Switch the export to JSONL and the same data streamed through in batches without a single out-of-memory error.
JSON and JSONL are both JSON — per RFC 8259 rules for each value. The difference is packaging: one tree vs many independent values on separate lines.
Standard JSON: one document, one tree
A JSON file or API response is typically one value:
{
"users": [
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Grace" }
],
"total": 2
}
Or a top-level array:
[
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Grace" }
]
Properties:
- Single parse operation loads entire structure
- Nested hierarchy expresses relationships naturally
- Standard for REST APIs, config files, package manifests
- Pretty-printing aids human review — use JSON Formatter
Limits:
- File must be complete and valid before parsing starts (streaming parsers exist but complexity rises)
- One syntax error breaks the whole document
- Large arrays consume memory proportional to file size
JSONL: one JSON value per line
JSON Lines (.jsonl, .ndjson) puts independent JSON values on separate lines:
{"id": 1, "name": "Ada"}
{"id": 2, "name": "Grace"}
{"id": 3, "name": "Linus"}
No wrapping array. No commas between lines. Just newline-delimited objects.
Properties:
- Stream-friendly: read line → parse → process → discard
- Append new records by adding lines (log rotation, incremental export)
- Corrupt line doesn't necessarily invalidate entire file
- Parallel processing: split file by line ranges across workers
Limits:
- No standard for nested file-level metadata (workaround: first line as header object, or sidecar file)
- Not valid as a single
JSON.parse()input - Human readability suffers on minified one-liners
Side-by-side comparison
| Aspect | JSON (array/document) | JSONL | |--------|----------------------|-------| | Top-level structure | Object or array | Sequence of values | | Streaming | Harder | Native | | Append records | Rewrite file or use JSON Patch | Append lines | | Error isolation | One error fails all | Bad line skippable | | API commonality | Very common | Niche (streaming exports) | | Memory for 1M records | Often entire tree | One record at a time | | Schema metadata | Natural in root object | Convention-based |
When JSON wins
REST API responses — clients expect { "data": [...], "meta": {...} }
Configuration files — package.json, tsconfig.json, single-document semantics
Small datasets — under a few MB, simplicity beats streaming
Nested relationships — graph-like data with cross-references
Human editing — formatted JSON in git diffs
Validate structure with JSON Validator before deploy.
When JSONL wins
Application logs — one event per line, ship to Elasticsearch, BigQuery, Splunk
ML training data — millions of labeled examples, stream into PyTorch/TensorFlow loaders
Database exports — COPY TO style dumps, CDC streams
ETL pipelines — map-reduce workers each take line ranges
Incremental sync — append new records without rewriting history
Large API exports — vendors offering "download all records" as .jsonl to avoid timeout
Parsing JSONL in practice
Pseudocode pattern:
for each line in file:
line = strip whitespace
if line is empty: continue
try:
record = JSON.parse(line)
process(record)
catch:
log bad line number, continue or abort based on policy
Language libraries:
- Python: read line by line,
json.loads(line) - Node:
readlineinterface +JSON.parse jq:jq -c .for compact;while read line; do echo "$line" | jq .; done
Never JSON.parse(entireFile) on JSONL.
Converting between formats
JSON array → JSONL:
const arr = JSON.parse(fs.readFileSync("data.json"));
arr.forEach(obj => console.log(JSON.stringify(obj)));
JSONL → JSON array:
const lines = fs.readFileSync("data.jsonl", "utf8").trim().split("\n");
const arr = lines.map(line => JSON.parse(line));
For huge files, stream both directions — don't load arrays into memory.
Pretty-print converted JSON with JSON Formatter for smaller exports.
Common JSONL mistakes
Trailing commas between lines — JSONL is not a JSON array; commas between lines are invalid per line (each line parses alone, so ,{ on next line fails)
Multi-line JSON objects — one logical record split across lines breaks line-based parsers. Keep each record on a single line (minified) or use a different format
Mixing formats — file starts as [ array then switches to JSONL mid-file
UTF-8 BOM on first line — breaks first record parse; strip BOM on read
Unescaped newlines inside strings — rare but fatal; strings must escape \n
See Common JSON formatting errors for shared pitfalls.
MIME types and tooling
| Format | Common Content-Type |
|--------|---------------------|
| JSON | application/json |
| JSONL | application/x-ndjson, application/jsonlines |
GitHub recognizes .jsonl. Some tools label it NDJSON (Newline Delimited JSON) — same idea.
Compression considerations
Gzip works on both. JSONL compresses well when keys repeat across lines (columnar feel). Some pipelines use .jsonl.gz for archival.
JSON pretty-print with whitespace compresses worse — minify before gzip for storage.
Schema and validation
JSON Schema validates single documents. For JSONL:
- Validate each line against the same schema
- Or use first line as schema version header:
{"_schema": 2}
JSON Validator helps spot per-line issues during development.
APIs streaming JSONL
Some endpoints return:
HTTP/1.1 200 OK
Content-Type: application/x-ndjson
{"id":1,"status":"pending"}
{"id":2,"status":"complete"}
Clients read the body as a stream, parsing line by line — useful for long-running job progress.
Most CRUD APIs still return single JSON objects — don't assume JSONL support without documentation.
Choosing for your next project
Ask:
- Will the file exceed available RAM? → JSONL
- Do consumers need random access to nested structure? → JSON document
- Will you append records over time? → JSONL
- Is this a public API contract? → JSON (unless streaming documented)
- Do humans edit this in git? → formatted JSON
When in doubt for logs and exports, JSONL. For APIs and config, JSON.
Related articles
- JSON Formatting Guide — pretty-print and validate standard JSON
- Common JSON Formatting Errors — syntax pitfalls in both formats
- Case Conversion for API Data Cleanup — normalize field names in exports
Conclusion
JSON packages data as one tree — ideal for APIs, configs, and human-edited files. JSONL packages data as one value per line — ideal for logs, streams, and datasets too large for memory. Same syntax rules per value; different container philosophy. Pick based on how you'll read, write, and recover from errors — not on which name sounds newer.
Frequently Asked Questions
Common questions answered to help you get the most from this tool.
Vertex Solutions Editorial Team
Guides and articles are produced under this collective byline — not attributed to invented individual experts. We research tool workflows, check steps against live tools where practical, and avoid fabricated personal stories, client anecdotes, or invented test results.
- Content research — Topics come from real tool workflows, common questions, and gaps in existing guides.
- Technical review — Steps, tool behavior, and examples are checked against the live tools on this site before publication when practical.
- Fact checking — Claims about formats, browser behavior, and calculator outputs are verified against documentation and tested sample inputs where practical.
- Updates — Pages may be revised when tools, official guidance, or browser behavior changes. There is no fixed review calendar for every URL.
- Corrections — Report factual errors via Contact.
Full policy: Editorial Standards. Tool checks: How we verify tools.