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.
By Vertex Solutions Editorial
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.
Quick answer
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.
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
Related tools
- JSON Formatter — Format and inspect JSON documents
- JSON Validator — Check syntax before deploy
- Base64 Encode — When JSON carries encoded binary fields
Key takeaways
- What is JSONL: JSON Lines (JSONL or NDJSON) is a format where each line is a separate valid JSON value, usually one object per line.
- Can JSONL contain arrays: Each line can be any valid JSON value — object, array, string, or number.
- Is JSONL valid JSON: No.
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.