What each format actually is
CSV, short for Comma-Separated Values, is a plain-text format for tabular data. Each row of the table is one line of text, and the columns within a row are separated by commas. The format predates the internet; it was in use on early mainframes and spreadsheet software by the 1970s. The formal specification, RFC 4180, was published only in 2005, by which time the format was already everywhere. The spec is short — about eight pages — and largely codifies what everyone was already doing.
JSON, short for JavaScript Object Notation, is a plain-text format for hierarchical data. It was specified by Douglas Crockford in the early 2000s as a subset of JavaScript's literal syntax, formalized as RFC 4627 in 2006, and updated as RFC 8259 in 2017. JSON supports six data types: strings, numbers, booleans, null, arrays, and objects. The format became the default for web APIs because it parsed natively in every browser without a library, and because it could represent nested structures that CSV could not.
The two formats solve different problems and have different sweet spots. CSV is for tables: rectangular data with one row per record and one column per field. JSON is for trees: arbitrary nested structures with named keys. Most data that lives in a CSV could be represented in JSON, but the reverse is not true. The choice between them is rarely about raw performance; it is about whether your data is rectangular or not.
The data model behind each
CSV has no real data model beyond rows and columns of strings. Every value in a CSV file is text. If a column contains numbers, dates, or booleans, the application reading the file decides how to interpret them. This is why opening the same CSV in Excel, a database loader, and a Python script can produce three different results: Excel may auto-convert 1-2 to a date, the database may reject it as not a number, and Python may treat it as a string. The CSV itself carries no type information.
The CSV quoting rules are the most error-prone part of the format. A field that contains a comma, a newline, or a double quote must be wrapped in double quotes. A double quote inside a quoted field is escaped by doubling it. So the value Hello, "world" becomes "Hello, ""world""". Many CSV producers get this wrong, and many CSV parsers are lenient about errors, which means that malformed files often work in one tool and fail in another. RFC 4180 specifies CRLF line endings, but most parsers also accept LF alone, and some accept CR alone, which produces three mutually inconsistent behaviors.
JSON has a strict, formal grammar with one canonical interpretation. A string is a sequence of Unicode characters wrapped in double quotes. A number is a decimal number with optional sign, exponent, and fractional part. Booleans are the literals true and false. Null is the literal null. Arrays are ordered lists wrapped in square brackets. Objects are unordered collections of key-value pairs wrapped in curly braces, with keys always being strings. There is no ambiguity: a JSON parser either produces the exact same structure from the same input every time, or it is broken.
When CSV wins
CSV wins on three things: size, tooling, and human readability for tabular data. A CSV file is typically 30 to 50 percent smaller than the equivalent JSON, because JSON repeats the field names in every object while CSV defines them once in the header. For a million-row data export, this difference matters. A 200 MB CSV becomes a 300 MB JSON file, which affects storage, transfer time, and parsing memory.
CSV wins on tooling. Every spreadsheet application, every database loader, every statistical package, and every business intelligence tool reads CSV natively. If you export data from a SQL database to a CSV file and open it in Excel, you see the data immediately, with column headers and types inferred from the values. The same data exported to JSON would need a transformation step before most of these tools could use it. Pandas, R, Stata, SPSS, and SAS all have CSV as a first-class input.
CSV wins on human readability for simple tables. A small CSV file can be read in a text editor and understood at a glance, because the comma alignment makes the structure obvious. The same data in JSON has more syntax noise — braces, brackets, quotes around every key — that obscures the values. For ad-hoc inspection of small datasets, CSV is the right format. For data that will be consumed by humans and tools that expect tables, CSV is the right format.
When JSON wins
JSON wins on structure. If your data has nested objects, arrays of mixed types, optional fields, or a schema that varies between records, CSV cannot represent it without contortions. A list of orders where each order has a customer object, a list of line items, and an optional shipping address fits naturally in JSON. To represent it in CSV, you either flatten everything (one row per line item, with order and customer fields repeated) or split it into multiple CSV files with join keys. Both options lose information and add complexity.
JSON wins on types. A JSON number is a number, not a string that might be a number. A boolean is a boolean, not the string "true" or the integer 1. A null is a null, not an empty string. This matters for any application that does arithmetic, filtering, or comparison on the data. It also matters for round-tripping: if you export data to CSV and import it back, you have lost type information unless your tool re-infers it correctly, and the re-inference is often wrong.
JSON wins on APIs. Modern web APIs return JSON by default because it maps directly to the data structures of every modern programming language. A fetch call in JavaScript returns a parsed object without any additional parsing. The same call returning CSV requires a CSV parser, which is a non-trivial piece of code if you want to handle quoting correctly. For internal service-to-service communication, the JSON ecosystem of schema validators (JSON Schema, Ajv, Pydantic), document databases (MongoDB, CouchDB), and search engines (Elasticsearch) is unmatched.
Performance and tooling
Parsing performance depends on the data shape and the parser implementation. For tabular data, CSV is typically faster to parse than JSON, because the format is simpler — split on commas, handle quoting, done. JSON parsing involves a state machine that distinguishes six data types and handles nested structures. The difference is rarely the bottleneck in a real application; network and disk I/O usually dominate. For very large files (gigabytes), streaming CSV parsers are easier to write than streaming JSON parsers, because CSV is naturally row-oriented while JSON is naturally tree-oriented.
Memory consumption follows a similar pattern. A CSV parser can yield one row at a time, so the memory footprint is constant regardless of file size. A JSON parser typically builds the entire tree in memory before returning, although streaming JSON parsers (like the SAX-style libraries and tools like jq, NDJSON, and JSON Lines) exist for large datasets. NDJSON, where each line is a separate JSON object, combines the row-oriented streaming of CSV with the structured typing of JSON, and is a good choice for large log and event streams.
Tooling maturity differs by ecosystem. The Python data ecosystem (pandas, NumPy, scikit-learn) treats CSV as the primary interchange format, with JSON as a second-class citizen that requires normalization before analysis. The JavaScript ecosystem and most web APIs treat JSON as the primary format, with CSV as an export option for spreadsheet users. Choose the format that your downstream tools handle best, and convert at the boundary if needed.
Making the call, with a decision checklist
Choose CSV when the data is naturally tabular (one row per record, one column per field), when the consumers are spreadsheets or data analysis tools, when file size matters more than type safety, and when the schema is stable. Choose JSON when the data is hierarchical, when types matter, when the consumers are programs rather than humans, and when the schema may vary between records.
Choose NDJSON when you have a stream of records that are individually complex but unrelated to each other, like log lines or event records. Each line is a complete JSON object, so you get the structure of JSON with the streaming simplicity of CSV. Choose Parquet or Arrow when you have very large tabular data and need column-oriented storage with type preservation; these binary formats outperform both CSV and JSON on analytical workloads.
Avoid mixing the two in a single pipeline unless you have a clear boundary. A common anti-pattern is to embed JSON strings inside CSV cells, which gives you the worst of both worlds: the type opacity of CSV plus the parsing complexity of JSON, with no tool support for either. If you need to send structured data through a CSV pipeline, flatten it into separate columns and document the mapping, or switch the pipeline to JSON entirely.