JSONL vs NDJSON: What's the Difference?
A complete guide comparing JSONL (JSON Lines) and NDJSON (Newline Delimited JSON) β two names for nearly the same format, with subtle differences worth knowing.
Last updated: February 2026
Quick Comparison: JSONL vs NDJSON
| Feature | JSONL (JSON Lines) | NDJSON |
|---|---|---|
| Full Name | JSON Lines | Newline Delimited JSON |
| File Extension | .jsonl | .ndjson |
| Specification | jsonlines.org (informal) | github.com/ndjson/ndjson-spec (informal spec) |
| Origin | Community convention, popularized by data science tools | Proposed by Chris Olah, Thorsten Ball, and others |
| Line Delimiter | \n (newline) | \n (newline) |
| MIME Type | application/jsonl (unofficial) | application/x-ndjson |
| Streaming Support | Yes, line-by-line | Yes, line-by-line |
| Adoption | OpenAI, Hugging Face, ML/AI ecosystem | Elasticsearch, Apache Spark, data engineering |
What is JSONL (JSON Lines)?
JSONL, short for JSON Lines, is a text-based data format where each line contains a single valid JSON value. Lines are separated by newline characters (\n). The format was popularized by the machine learning and data science communities, and is defined informally at jsonlines.org.
JSONL files use the .jsonl file extension. The format has become the standard for AI and ML training data β OpenAI requires .jsonl files for fine-tuning, and Hugging Face datasets commonly use JSON Lines format.
{"id": 1, "text": "Hello world", "label": "greeting"}{"id": 2, "text": "How are you?", "label": "question"}{"id": 3, "text": "Goodbye", "label": "farewell"}
What is NDJSON (Newline Delimited JSON)?
NDJSON, or Newline Delimited JSON, is a data format where each line is a valid JSON value separated by newline characters. The specification is hosted at github.com/ndjson/ndjson-spec and was formalized to provide a more structured standard for line-delimited JSON data.
NDJSON files use the .ndjson file extension and have a registered MIME type of application/x-ndjson. The format is widely adopted in data engineering, log processing, and streaming applications β Elasticsearch, Apache Spark, and many HTTP streaming APIs use NDJSON.
{"id": 1, "text": "Hello world", "label": "greeting"}{"id": 2, "text": "How are you?", "label": "question"}{"id": 3, "text": "Goodbye", "label": "farewell"}
Are JSONL and NDJSON the Same Format?
In practice, yes β JSONL and NDJSON are functionally identical formats. Both store one JSON value per line, separated by newline characters (\n). A .jsonl file and a .ndjson file with the same data are completely interchangeable, and any tool that reads one format can read the other.
The differences are purely in naming, community adoption, and metadata conventions. JSONL (JSON Lines) emerged from the data science and machine learning community, while NDJSON (Newline Delimited JSON) was formalized by the data engineering and web streaming community. Think of it like "movie" vs "film" β same thing, different name preferences in different circles.
The most meaningful difference is the file extension (.jsonl vs .ndjson) and the MIME type. NDJSON has a more widely recognized MIME type (application/x-ndjson) that is used in HTTP streaming contexts, while JSONL files are the standard in AI/ML workflows. When choosing between them, follow the convention of your ecosystem.
Key Differences Between JSONL and NDJSON
1. Specification & Authority
Defined at jsonlines.org with a concise, informal specification. The spec is straightforward: each line is a valid JSON value, lines are separated by '\n', and UTF-8 encoding is recommended.
Defined at github.com/ndjson/ndjson-spec with a slightly more formal specification. NDJSON explicitly requires that each line is a valid JSON value and that the line separator is '\n' (not '\r\n'), with a trailing newline recommended.
2. File Extension
Uses the .jsonl file extension. This is the standard for OpenAI fine-tuning files, Hugging Face datasets, and most ML/AI tools. GitHub and VS Code recognize .jsonl files with syntax highlighting.
Uses the .ndjson file extension. This is common in data engineering tools, Elasticsearch bulk APIs, and streaming HTTP responses. Many editors also support .ndjson syntax highlighting.
3. MIME Type
No officially registered MIME type. Common unofficial types include application/jsonl and application/json-lines. In practice, many systems use application/jsonl or fall back to application/json.
Uses application/x-ndjson as its MIME type. This is more widely recognized in HTTP contexts and is used by Elasticsearch, the Fetch API streaming standard, and various web frameworks for streaming responses.
4. Community & Ecosystem Adoption
Dominant in the AI/ML ecosystem. OpenAI, Anthropic, Google Gemini, Hugging Face, and most ML frameworks use .jsonl. The term 'JSONL' is more commonly searched and recognized in the developer community overall.
Preferred in data engineering and backend systems. Elasticsearch, Apache Spark, PostgreSQL COPY, and many log aggregation tools use NDJSON. The ndjson npm package has millions of weekly downloads.
When to Use JSONL vs NDJSON
- Preparing training data for OpenAI, Anthropic, or other LLM fine-tuning
- Working with Hugging Face datasets
- Building ML pipelines and data preprocessing
- Your team or documentation refers to 'JSON Lines'
- Uploading files to AI/ML platforms that expect .jsonl extension
- Working in Python data science environments
- Streaming JSON data over HTTP (Server-Sent Events, fetch streaming)
- Working with Elasticsearch bulk API
- Building data pipelines with Apache Spark or similar tools
- Setting Content-Type headers (application/x-ndjson)
- Your infrastructure or team uses the NDJSON convention
- Working with Node.js streaming libraries
Code Examples: Reading JSONL and NDJSON
# Reading JSONL - exactly the same as reading NDJSONimport jsonwith open('data.jsonl', 'r') as f:for line in f:record = json.loads(line.strip())print(record['text'])# Output:# Hello world# How are you?# Goodbye
// Reading NDJSON - exactly the same as reading JSONLimport { createReadStream } from 'fs';import { createInterface } from 'readline';const rl = createInterface({input: createReadStream('data.ndjson')});for await (const line of rl) {const record = JSON.parse(line);console.log(record.text);}// Output:// Hello world// How are you?// Goodbye