Text & Data

CSV Validate & Format

Verify CSV against RFC 4180, see exactly which rows are broken, view it as a sortable table, and format or convert it. Runs in your browser.

Paste or type CSV, or choose File → Load sample.

Input

CSV options

Keyboard shortcuts

Ctrl/⌘ + Enter
Format (in the input)
← → on the tree divider
Resize the panes (Home/End for the limits; double-click resets)
Esc
Close a menu or overlay, then leave full screen
↓ on a menu
Open it; ↑ ↓ move through its items
← → in the menu bar
Move between menus

Where your data goes

Using this page Using the API, MCP or A2A
Where it runs In your browser. The CSV is parsed, checked, typed and re-formatted by JavaScript on this page; nothing you paste or load is sent anywhere. In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller.
What leaves your device Nothing you enter. After the page and its scripts load, no request carries your input. The browser enforces this: the page's Content-Security-Policy sets connect-src 'none' and form-action 'none', which block fetch, XHR, WebSocket, EventSource, beacons and form posts.

The server receives the CSV text, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential data use the page, which does the same work in your browser and sends nothing.

The server receives the JSON text, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential data use the page, which does the same work in your browser and sends nothing.

Stored Nothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice. Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country.
Logged Nothing (no requests are made). Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies.
Access Open to anyone. Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare.

Details and how to verify this yourself: Security & privacy.

User guide

The studio works like a desktop window: a menu bar (File, Edit, View, Settings, Help) with the full-screen icon at the right of the same row. Settings → Options… opens the options in an overlay (or View → Inline config shows them in the card), and Help → Keyboard shortcuts… lists the keys.

  1. Paste CSV into the Input view, or choose File → Load sample to see one with a few problems. It is checked as you type; line numbers make positions easy to find.
  2. Read the verdict and the Problems view (the button at the right of the status line shows the count). Each problem has a severity, a line and column, and a message saying how to fix it. Click one to jump to it: in the input, the text is selected; in the Table view, the bad row is highlighted.
  3. Look at the Table view: a grid of the data with each column’s inferred type (integer, number, boolean, date, datetime, text, mixed or empty). Click a column heading to sort by it. Sorting only changes the view, not your data.
  4. Edit → Format (or Ctrl+Enter) writes the Formatted view: the same data with one delimiter, one quoting style and one line ending, without blank lines or a byte-order mark, and with exactly one line break at the end.
  5. Copy or Download the result, or File → Clear input to start again. The full-screen icon at the right of the menu bar makes the studio fill the window; Esc returns.

Options.

  • Delimiter: Auto (detected), comma, semicolon, tab or pipe. Detection picks the delimiter that splits the first 50 rows into the same number of fields without breaking any quotes; set it yourself if it guesses wrong.
  • Header: Auto, Yes or No. Auto treats the first row as a header when its values are all text (no numbers, true/false or dates), all different, and none of them appears again in its column below.
  • Quoting (for the formatted output): Minimal quotes only values that need it; All quotes every value; Non-numeric quotes everything except numbers, which some importers use to tell text from numbers.
  • Output delimiter: Same as input, or another delimiter to convert, e.g. semicolons to commas.
  • Line ending: LF (the default; what most tools write) or CRLF (what RFC 4180 specifies and some older Windows software expects). Readers accept either.
  • Trim: remove spaces and tabs around unquoted values. Quoted values are never trimmed: quoting is how a file says the spaces matter.

What the checks catch. The errors are the problems that change how a file is read: an unclosed quote swallows every row after it into one value; a quote in the middle of an unquoted value, or text after a closing quote, is read differently by different programs; a row with a missing or extra field shifts every value after it into the wrong column. The warnings are things that are probably mistakes, such as a, b (the space is part of the value, so the column is " b", not "b"), duplicate or empty header names, and blank lines. The info notes are worth knowing but often deliberate: a column that mixes numbers with N/A, quoting that varies from row to row, a delimiter at the end of every row, and values that a spreadsheet would run as a formula.

Formatting never changes your values. Only the delimiter, quotes, line breaks between rows, blank lines and padding outside quotes change. A value with leading or trailing spaces is quoted in the output, so tools that trim unquoted values keep the spaces. The exception is Trim, which you ask for. Formatting a file twice gives the same result as formatting it once.

Spreadsheet formulas. When a CSV is opened in Excel, LibreOffice or Google Sheets, a value starting with =, +, - or @ can be run as a formula; =HYPERLINK(...) or worse in data from a web form is a known attack (“CSV injection”). The page flags such values, unless they are plain numbers like -5. It does not change them: escaping is for the program that exports data to spreadsheets, usually by putting a ' in front.

Specification

Grammar. RFC 4180, read leniently and reported strictly.

  • A file is a sequence of records separated by line breaks; a record is a sequence of fields separated by the delimiter. The last record may or may not end with a line break.
  • A field is either unquoted (any characters except the delimiter, " and line breaks) or quoted: " … ", where the content may include the delimiter and line breaks, and a quote is written "".
  • Spaces are part of a field. a , b is the two values "a " and " b".

Beyond RFC 4180. These are accepted:

  • Delimiters ;, tab and | as well as ,.
  • LF and lone CR line breaks as well as CRLF, and a mix of them (reported by line-endings).
  • Spaces or tabs between a delimiter and a quoted field (a, "b"): they are ignored and reported by whitespace. A tab is never padding in a tab-separated file.
  • A byte-order mark: removed before parsing, so it never becomes part of the first header name.
  • Rows with different numbers of fields are kept as they are (and reported).
  • After an error that leaves the structure readable, parsing continues so every problem is listed: a stray quote stays in the value, and text after a closing quote is appended to it. Only an unclosed quote stops parsing, because everything after it is part of one value; then there is no formatted output.

Blank lines. A blank line between rows is skipped, with a blank-line warning; blank lines at the end are reported by final-newline. In a file with a single column an empty line is an empty value instead (there is no other way to write one), and the formatter writes it as "" so it cannot be mistaken for a blank line.

Delimiter detection (when Auto). Each candidate (, ; tab |) is used to parse the first 50 records (and at most the first 32,768 characters), quotes respected. Candidates that produce quote errors lose to those that do not; among the rest, the one whose most common field count covers the most records wins, and a tie goes to more fields, then to the order listed. A candidate must give at least two fields. If none does, the file is read as one column with , (and delimiterDetected is false). Limits: a file where several delimiters split every row consistently (a;b,c on every line) is read with the earliest-listed one; a file whose first 50 rows are not typical can mislead it.

Header detection (when Auto). The first row is a header when its non-empty values are all text (see the types below), are all different, at least one is non-empty, and none of them appears again in the same column of the next 49 rows. Limits: a file of text with no header (Ada,London) is read as having one, because the two cannot be told apart; a header containing a year or other number is read as data. Set Header when it matters.

Column count. The header’s length, or without a header the most common row length (ties go to the first seen). Each row with a different length gets a field-count error.

Rules. Every finding has a rule ID, a severity, a message, and the 1-based line and column (in Unicode code points) and 0-based offset (in UTF-16 units) where it starts, with an end position when it covers a range. A line break inside a quoted value starts a new line for positions, as it does in an editor. At most 100 findings are listed per rule, followed by one saying how many more there are.

RuleSeverityFixed by FormatReported when
unclosed-quoteerrornoA quoted field never closes. At the opening quote. There is no formatted output.
stray-quoteerroryesA " appears inside an unquoted field (5" disk). At the first such quote; Format quotes the field and doubles it.
text-after-quoteerroryesText follows a closing quote before the delimiter ("abc"def). From the closing quote to the end of the field; Format writes the joined value.
field-counterrornoA row has more or fewer fields than expected. Covers the row; the message gives its line and both counts.
empty-headerwarningnoA header name is empty or only whitespace (except the extra column from trailing-delimiter).
duplicate-headerwarningnoA header name repeats an earlier one exactly. At the repeat; the message names the first column.
whitespacewarning or infopartlySpaces or tabs around a value. Warning when they touch a delimiter (a, b), make up the whole value, or sit outside quotes (Format removes the last kind); info at the start or end of a line. Not reported inside quotes, or for unquoted values when Trim is on.
blank-linewarningyesAn empty line between rows (except in a one-column file).
trailing-delimiterinfonoEvery row ends with an empty, unquoted field: probably a delimiter after the last value. Format keeps the column, since it is data.
line-endingswarningyesRows end with more than one of LF, CRLF and CR. At the first line break of the least used style. Line breaks inside quotes are values and are not counted.
byte-order-markinfoyesThe text starts with U+FEFF. Info, not a warning: Excel needs it to open UTF-8 CSV correctly. Format removes it; add it back if your users open the file in Excel.
final-newlineinfoyesThe last row has no line break (RFC 4180 allows this), or blank lines follow it.
mixed-typesinfonoA column’s non-empty values have more than one type (integers and decimals count as numbers; dates and datetimes as datetimes). At the first value that differs from the most common type.
inconsistent-quotinginfoyesIn one column (header excluded), some values are quoted although they need not be, while others are unquoted. At the first value of the less common style.
formula-injectioninfonoA value (header included) starts with =, +, -, @, a tab or a CR, is at least two characters long, and is not a number.

The shared text rules for the other formats are adapted: trailing spaces are part of the last value in CSV, so they are reported by whitespace instead of trailing-spaces; line length is not checked, because long rows are normal; mixed line endings and the final newline only count line breaks between rows.

Types. Values are typed after removing surrounding spaces; empty values are ignored, and a column’s type comes from all of its non-empty values.

TypeMatches
integerAn optional sign and digits, without leading zeros: 0, -12, +7. 007 and 00501 are text, because leading zeros mean an identifier (a ZIP code, an account number).
numberAn integer or decimal with an optional exponent: 1.5, .5, -2e10. A column of integers and decimals is number. Not numbers: 1., 1,000, 1_000, 0x1F, NaN, Infinity, $5, 5%.
booleantrue or false, in any case. yes/no and 1/0 are not booleans.
dateYYYY-MM-DD, a real calendar date (2024-02-29 yes, 2023-02-29 no).
datetimeA date, T or a space, HH:MM, optional :SS and fraction, optional Z or ±HH:MM. A column of dates and datetimes is datetime.
textAnything else.
mixedNon-empty values of more than one of the above.
emptyEvery value is empty.

Column statistics count empty values (missing fields in short rows included), distinct non-empty values, and the longest value in code points.

Formatting. The header (if any) and the data rows are written with the output delimiter and line ending, one line break after every row. Minimal quoting quotes a value that contains the delimiter, a quote, CR or LF, or starts or ends with a space or tab. All quotes every value, empty ones too. Non-numeric quotes every value that is not an integer or number (empty values included). Quotes inside values are doubled. A row consisting of one empty value is written as "".

Conversions.

  • CSV → JSON: reads the CSV with the same rules and refuses one with errors. Objects (needs a header) gives one object per row, keyed by header name; an empty name becomes column_N and a repeated one gets _2, _3. Arrays gives one array per row, the header row first. With types, integer and number columns become JSON numbers (every digit kept, even beyond 2^53; +1 and .5 become 1 and 0.5), boolean columns become true/false, and empty values become null; text, date and mixed columns stay strings.
  • JSON → CSV: an array of objects gives a header of every key in first-seen order and one row per object (a missing key gives an empty value); a single object gives one row; an array of arrays gives rows without a header. Strings are written as they are, numbers exactly as written in the JSON, true/false as words, null as an empty value, and nested objects and arrays as compact JSON text, with a note saying which columns contain them.

API.

  • POST /api/csv/format with input and the options as delimiter (auto, comma, semicolon, tab, pipe), header, quoting, output_delimiter, line_ending and trim_fields. It returns valid, a summary of counts, every diagnostic, the dialect that was used, the header, the column count and statistics, a preview of the first 20 rows, and formatted. Invalid CSV is a 200 response with valid: false, not an HTTP error.
  • POST /api/convert/csv-to-json (shape, types) and POST /api/convert/json-to-csv (delimiter, header, quoting, line_ending). They return 422 for CSV with errors or JSON of the wrong shape.
  • input may be at most 100,000 characters for csv_format, 50,000 for csv_to_json (it also builds and serializes JSON) and 200,000 for json_to_csv, which keeps each call within the server’s CPU budget. The page has no limit.

Lint rules

Rule Severity What it checks Fixed by Format
unclosed-quote error A quoted field is never closed: everything after its opening quote is swallowed into one value. no
stray-quote error A double quote inside an unquoted field. Quote the whole field and double the quote ("5"" disk"). yes
text-after-quote error Text between a closing quote and the next delimiter or line break ("abc"def). yes
field-count error A row has more or fewer fields than the header (or, without a header, than most rows). no
empty-header warning A header name is empty. no
duplicate-header warning Two columns have the same header name. no
whitespace warning Spaces or tabs around a value. Warning when they pad a delimiter (a, b) or sit outside quotes; info at the start or end of a line. Format removes padding outside quotes; whitespace inside an unquoted value is data, removed only with the trim option. no
blank-line warning An empty line between rows. Ignored when reading; Format removes it. yes
trailing-delimiter info Every row ends with a delimiter, giving an extra empty column. no
line-endings warning Rows end with a mix of LF, CRLF and CR. Line breaks inside quoted values are data and are not counted. yes
byte-order-mark info The text starts with a byte-order mark. Excel uses it to recognise UTF-8; most other tools read it as part of the first header name. Format removes it. yes
final-newline info The last row has no line break, or is followed by blank lines. RFC 4180 allows either form for the last row; Format always ends with one line break. yes
mixed-types info A column mixes kinds of value, e.g. numbers and text (N/A). no
inconsistent-quoting info In one column, some values are quoted without needing it and others are not. yes
formula-injection info A value starts with =, +, -, @, tab or carriage return and is not a number: spreadsheets may run it as a formula. no

Errors make the document invalid. Warnings and information are style findings; the document still parses.

API & MCP

The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.