jsonlkit.com
JSONL (JSON Lines) utilities, in the browser
Say hi →

What is NDJSON?

Newline-Delimited JSON · reference docs · updated 22 May 2026 · vs JSONL · spec · validator · normalizer

NDJSON — Newline-Delimited JSON — is a streaming text format where each line is one independent, valid JSON value, separated by \n. There is no enclosing array, no comma between records, no surrounding document. NDJSON is the same format as JSON Lines (JSONL); the two names are interchangeable. NDJSON is the spelling preferred in the JavaScript / Node ecosystem and pushed by ndjson.org; JSONL is the spelling preferred in Python, ML, and data engineering.

The shape

{"id":1,"event":"signup","ts":"2026-05-22T08:14:01Z"}
{"id":2,"event":"click","ts":"2026-05-22T08:14:03Z","path":"/pricing"}
{"id":3,"event":"checkout","ts":"2026-05-22T08:14:11Z","amount":29}

Each line is parseable on its own with JSON.parse (or any RFC 8259 parser). The file as a whole is not a single JSON document — you must split on \n first and then parse each line. This is the entire format. There is no header, no schema, no metadata.

Quick facts

PropertyValue
Format nameNewline-Delimited JSON
Common nicknamesNDJSON, JSONL, JSON Lines, LDJSON, line-delimited JSON
File extension.ndjson (also accepts .jsonl, .ldjson)
MIME typeapplication/x-ndjson
Text encodingUTF-8
Line separator\n (LF). Most parsers also accept \r\n.
CommentsNot allowed
Originndjson.org / github.com/ndjson family of npm packages, ~2013
Equivalent toJSON Lines (.jsonl), LDJSON (.ldjson)

Why NDJSON exists

Regular JSON is a single document. To send a sequence of records, you have to either:

NDJSON solves the streaming problem with one rule change: delete the array brackets and the commas, separate records with newlines. Everything else falls out of that: you can stream, append, tail, grep, and split the file with normal Unix tooling, and one corrupt record doesn't poison the rest.

Where NDJSON is used

NDJSON rules

  1. One JSON value per line.
  2. Lines separated by \n (LF).
  3. Each line, in isolation, is valid JSON per RFC 8259.
  4. UTF-8 encoded, without BOM.
  5. No comments, no trailing commas, no NaN / Infinity (same as JSON).
  6. Blank lines are not in the spec; most parsers tolerate them, strict parsers reject.
  7. Trailing \n after the last record is recommended but not required.

That's the entire specification. For the formal rules and the edge cases parsers disagree on — these apply to NDJSON identically — see the JSONL specification.

NDJSON vs JSONL — the relationship

NDJSON and JSONL are the same format. The difference is purely the name and which ecosystem promotes it:

If you receive an .ndjson file and your tool expects .jsonl (or vice versa), just rename it — the content is identical. Use the NDJSON ↔ JSONL normalizer if you also need to strip a BOM or normalize line endings in the same pass. For a deeper side-by-side, see NDJSON vs JSONL.

NDJSON over HTTP

The canonical streaming pattern:

HTTP/1.1 200 OK
Content-Type: application/x-ndjson
Transfer-Encoding: chunked

{"id":1,"event":"signup"}
{"id":2,"event":"click"}
{"id":3,"event":"checkout"}

Each chunk ends at a \n boundary so the client can parse complete records as bytes arrive. Producers should flush after each record (or every N records). Consumers should accumulate bytes until the next \n, then parse and emit.

Minimal Node consumer:

const res = await fetch(url);
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = '';
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });
  let i;
  while ((i = buf.indexOf('\n')) >= 0) {
    const line = buf.slice(0, i).trim();
    buf = buf.slice(i + 1);
    if (line) handle(JSON.parse(line));
  }
}

NDJSON gotchas

NDJSON in three languages

Node (streaming):

const { createReadStream } = require('fs');
const readline = require('readline');
const rl = readline.createInterface({ input: createReadStream('events.ndjson') });
rl.on('line', l => { if (l.trim()) handle(JSON.parse(l)); });

Python (streaming):

import json
with open('events.ndjson', encoding='utf-8') as f:
    for line in f:
        line = line.strip()
        if line:
            handle(json.loads(line))

Go (streaming):

scanner := bufio.NewScanner(f)
scanner.Buffer(make([]byte, 64*1024), 16*1024*1024)
for scanner.Scan() {
    var r Record
    if err := json.Unmarshal(scanner.Bytes(), &r); err == nil {
        handle(r)
    }
}

Tools on this site that work on NDJSON

  1. NDJSON Validator — per-line syntax check.
  2. NDJSON ↔ JSONL — strip BOM, normalize CRLF, drop blank lines, download with either extension.
  3. Viewer — record-by-record tree / grid view.
  4. Auto-fixer — repair trailing commas, single quotes, smart quotes, comments.
  5. Formatter — pretty-print or minify each record individually.
  6. Query (jq-style) — slice, filter, reshape with jq syntax.
  7. NDJSON → CSV — flatten nested keys and download a spreadsheet-ready file.

All tools accept both .ndjson and .jsonl; the file picker explicitly allows both extensions.

FAQ

Is NDJSON different from JSONL?

No. Same format, different name. NDJSON is the name preferred by the JavaScript / Node / observability ecosystem; JSONL is preferred in Python, ML, and data engineering. Both mean "one JSON value per line, separated by \n." See NDJSON vs JSONL for the side-by-side.

Can I parse NDJSON with JSON.parse?

Not the whole file at once — JSON.parse expects a single document. Split on \n first, then call JSON.parse on each non-empty line.

What MIME type should I use?

application/x-ndjson. It's the most widely recognized by HTTP clients (curl, httpie, Postman) and CDNs. Avoid application/json for NDJSON — clients will try to parse the whole body as one document.

Can NDJSON records span multiple lines?

No. Records are delimited by line breaks. Any newline inside a string value must be escaped as \n.

Is NDJSON the same as JSON Streaming?

NDJSON is one variant of JSON streaming. Another variant is "JSON text sequences" (RFC 7464), which uses RS (0x1E) as the record separator. NDJSON is by far the more common variant in real-world systems.

What about gzipped NDJSON?

.ndjson.gz compresses very well because repeated keys deduplicate efficiently. Gzip typically shrinks NDJSON by 80–95%. DuckDB, jq, Node's zlib, and Python's gzip all read compressed NDJSON natively.

— S., [email protected]