NDJSON

On this page 7

NDJSON, short for newline-delimited JSON, is a text data format in which each line holds one complete JSON value, usually an object, and lines are separated by a newline character. Among data formats it occupies the space between plain JSON and CSV: every record keeps the nested structure of JSON, while the file as a whole can be processed line by line like a log or a CSV file.

NDJSON is essentially the same convention as JSON Lines, commonly saved with the .jsonl extension, and the two names are used interchangeably in practice. Its main value is streaming: a producer can append records indefinitely, and a consumer can parse each record as soon as its line arrives, without loading the entire dataset into memory.

How the NDJSON format is structured

The rules of NDJSON are deliberately minimal. Each line must be a valid JSON text on its own, and the line feed character (LF) separates records. Because JSON escapes newlines inside strings as \n, a serialized value never needs a raw line break, so the newline can safely mark record boundaries.

RuleRequirementPractical consequence
One value per lineEach line is complete JSONRecords can be parsed independently
Line separatorLF; parsers usually accept CRLFWorks with standard line-reading tools
EncodingUTF-8No byte order mark expected
No enclosing arrayNo brackets or commas between linesRecords can be appended at any time
Media typeCommonly application/x-ndjsonSignals streaming JSON over HTTP
Core rules of the NDJSON format

A small NDJSON file therefore looks like three independent objects, one per line, such as an object with an id of 1, another with an id of 2 and a third with an id of 3. A trailing newline after the last record is recommended, because it makes appending safe and lets tools detect an incomplete final line.

NDJSON compared with other data formats

NDJSON is best understood as JSON optimized for record streams. The comparison with neighboring formats shows where it fits and where a binary or columnar format is the better choice.

FormatStructureStreamableTypical role
NDJSONOne JSON value per lineYesLogs, exports, API streams
JSONOne document, often an arrayNot easilyAPI payloads, configuration
CSVFlat rows with delimitersYesSpreadsheets, simple tables
ParquetBinary, columnar, compressedNo, file-basedAnalytics storage
Apache AvroBinary rows with a schemaYesEvent pipelines, Kafka
NDJSON and neighboring data formats

Against a single JSON array, NDJSON wins whenever data is large or arrives continuously: a standard parser must read the closing bracket before it knows the array is valid, while an NDJSON reader handles each record immediately. Against CSV, NDJSON supports nested objects, typed numbers and booleans, and missing fields without ambiguous quoting rules.

Against Parquet or ORC, NDJSON loses on size and query speed. Repeating every field name on every line makes NDJSON verbose, and text parsing is slower than reading binary columns. A common architecture therefore lands raw events as NDJSON and converts them to a columnar format for analytics.

Where NDJSON is used

NDJSON appears wherever systems exchange many independent records. Its line orientation makes it compatible with Unix tools such as split, head, tail and grep, and with record-by-record JSON processors such as jq.

  • Structured logging: services write one JSON object per log event, which log shippers forward line by line.
  • Bulk APIs: search engines such as Elasticsearch accept NDJSON bodies for bulk indexing, alternating action and document lines.
  • Data warehouse loading: cloud warehouses load newline-delimited JSON files directly into tables.
  • Streaming HTTP responses: servers send one JSON line per result, so clients can render partial output early.
  • Machine learning datasets: training and evaluation examples are often stored as one JSON object per line.

Because each line is independent, NDJSON files can be split for parallel processing at any newline boundary, which distributed engines exploit when reading large uncompressed files.

Practical choices when producing NDJSON

Producing correct NDJSON is simple, but a few configuration choices decide whether files remain easy to consume at scale. The table summarizes the most frequent decisions.

SituationRecommended choiceReason
Large archive filesCompress with gzip or zstdField names compress very well
Parallel reading neededSplit into many filesCompressed streams are hard to split
Evolving recordsKeep field names stable, add fieldsReaders can ignore unknown fields
Precise numbersStore large IDs as stringsSome parsers lose integer precision
Pretty printingNever indent across linesIndentation breaks the format
Decisions when writing NDJSON

The precision issue matters in practice: JavaScript and many JSON libraries represent numbers as 64-bit floating point, so integers larger than about 9 quadrillion lose exactness. Storing such identifiers as strings avoids silent corruption.

Common mistakes and limitations

Most NDJSON problems come from treating it like ordinary JSON. A pretty-printed object spread over several lines is no longer valid NDJSON, and a standard JSON parser rejects an NDJSON file as a whole because it contains several top-level values.

Other pitfalls include a missing final newline, which can merge the last record with the next appended one, mixed CRLF and LF endings from different tools, and schema drift, where the same field holds a number in some lines and a string in others. NDJSON has no built-in schema, so teams that need guarantees pair it with JSON Schema validation or a data contract.

A robust reader should also decide how to handle a corrupt line: skip and log it, send it to a dead-letter file, or stop the whole job. Line independence makes the first two options possible, which is a real advantage over a single large JSON document.

The short version

NDJSON is a way of writing data one record per line. Each line is a small, complete description of one thing, such as one order, one log message or one sensor reading.

Think of a notebook where every line is a separate, finished note. You can keep adding notes at the bottom, read them one at a time, or tear the notebook into parts and share the work. Nothing on one line depends on another line, which makes the format easy to stream and process in pieces.

Where it makes a difference

A data engineer receives a nightly export of about 40 million customer events from an application. The original export was one huge JSON array of roughly 30 gigabytes, and the import job regularly ran out of memory because the parser tried to build the whole array before processing it.

The application team switches the export to gzip-compressed NDJSON split into files of about 500 megabytes. The pipeline now reads each file line by line with constant memory use, processes files in parallel on several workers, and writes the cleaned records to Parquet for analysts. A malformed line from a buggy client version goes to a quarantine file instead of failing the job, and the nightly load finishes in a fraction of the previous time.