Sample files that break things

66 files to download, each one built to exercise something specific. Eight CSVs and a JSONL cover the happy path, broken rows, delimiter sniffing, encoding, width and volume. Below them, one coherent dataset - Acme retail: customers, products and orders joined by real foreign keys - is served in 14 structured formats: a three-sheet Excel workbook, DuckDB and SQLite databases, Parquet, Arrow, Avro, a PDF with two tables, a Word invoice, flat and nested JSON, XML, TSV, and gzip and zip wrappers. Then come the purpose-built pairs and format fixtures, one per tool. All synthetic, all free for any use.

2.2 MB for the whole set. No sign-up, no email, no download counter.

Looking for realistic data rather than broken data? The sample datasets library has 20 subjects, each in CSV, JSON, JSONL, Excel and Parquet, at 100 or 1,000 rows as files and 10,000, 100,000 or 1,000,000 generated in your browser.

Files that exist for a reason

Search for a sample CSV and you get twenty pages offering the same three files: a list of names, a list of cities, and something about superstore sales. They are fine for a screenshot and useless for the thing people usually need test data for, which is finding out what happens when the input is not fine.

So each file below was written to break or exercise one specific behaviour, and the description says which. If you are testing delimiter detection, one of these is semicolon-delimited with decimal commas inside quoted fields. If you are testing type inference, one of them is nothing but leading zeros: ZIP codes, EAN-13 barcodes, and 17 digit account numbers that turn into scientific notation the moment something guesses wrong. If you are testing error paths, one of them is broken in six separate ways at once.

Every file is synthetic. A seeded generator produced them once and the output was committed as it stood, so the bytes never move and a test can assert against them. No real person, company, order or account appears anywhere, and every email address ends in example.com, the domain reserved for exactly this purpose.

Licence: these files are dedicated to the public domain under CC0 1.0. Use them for anything, commercial work included, with no attribution required and nothing to agree to.

Two libraries, two jobs

This page is the awkward one. The files here exist because something about them is wrong, unusual or heavy: a header that repeats a name, a file where every row has a different number of fields, sixty columns, ten thousand rows, a semicolon delimiter with decimal commas inside quoted fields. If you are testing a parser, an importer or an error path, start here.

The other library, at /datasets/, is the opposite. 20 subjects, sales, customers, orders, products, employees, weather, stock prices, sensors, logs, tickets, flights and more, each correct throughout, each with its own page, a preview table, a documented schema and downloads in CSV, JSON, JSONL, Excel and Parquet at 100 or 1,000 rows. Anything larger is generated in your browser from the same seeded code, up to a million rows, so a hundred thousand rows of sales data costs you a few seconds and no bandwidth. That is 192 committed files, and this page does not repeat any of them.

Three of those datasets share keys. Orders reference real customers and real SKUs, and an order's unit price is the catalog price for that SKU, so a three-way join reconciles to the cent. That is the thing almost no public sample data offers, and it is what you need if the demo you are building is about joining rather than about reading one file.

One of the twenty is a bridge between the two libraries: the messy data pack takes the philosophy of this page and scales it, carrying nine defects at once at any size from 100 rows to a million.

The files

8 fixtures, each one named after the problem it demonstrates. Every link is a direct download from this site, and the second link opens the tool that deals with that problem, with the file already loaded. Nothing is zipped, nothing asks for an email.

people-100.csv

6.6 KB (6,798 bytes) · 100 rows x 7 columns

The clean one. Sequential integer ids, first and last names, an email per person, a city, an ISO join date and one of four plan names. Nothing is missing, nothing is quoted, no row is ragged and no value needs escaping. This is the file to reach for when you want to prove the happy path works before you go looking for trouble, and the one to use in a screenshot or a tutorial. Profiled, it comes back as one numeric column, one date column and five text columns, with zero duplicate rows.

Download people-100.csv · open it in the CSV viewer

sales-10k.csv

504.5 KB (516,630 bytes) · 10,000 rows x 7 columns

Enough rows for a measurement to mean something, and typed on purpose so the columns are worth aggregating. An order id, an ISO date spread over two years, one of five regions, one of three channels, a unit count, a decimal amount and a boolean refund flag. Every amount carries non-zero cents so that the column reads back as a genuine number rather than as text. It profiles as 730 distinct dates, five regions, and an amount running from 5.01 to 4800.53 with a median of 2394.21. Pivot it by region and channel and you get a five by three table out of ten thousand rows.

Download sales-10k.csv · pivot it · profile it

messy.csv

13.2 KB (13,468 bytes) · 200 data rows, 6 header names, widest row 8 fields

Broken six ways at once, so one file covers most of an error-handling test. The header uses name twice. Twelve rows carry the wrong number of fields, some short and some long. Six rows are entirely blank. Thirteen lines end in CRLF while 194 end in LF. Fifty cells have leading or trailing whitespace. Two replacement characters sit on line 59, the fingerprint of a file that was decoded as the wrong encoding somewhere upstream. Run it through the validator and all six come back with line numbers. Run it through a parser that pads ragged rows and it reads as 200 rows and 8 columns, with the second name renamed to name_2 and the two unnamed extras called column_7 and column_8.

Download messy.csv · run it through the validator

semicolon-eu.csv

28.5 KB (29,146 bytes) · 500 rows x 7 columns

What Excel writes on a machine whose locale uses a comma for decimals: semicolons between the fields, and amounts like "8.794,14" in quotes with a full stop for thousands and a comma for the decimal. Dates are in DD.MM.YYYY. Customer names carry umlauts and a slash. This is the file that tells you whether a reader sniffs its separator or assumes one, and whether an importer treats a decimal comma as a decimal point, as a thousands separator, or as a reason to give up. A parser that guesses correctly reports seven columns; one that does not reports one.

Download semicolon-eu.csv · change its delimiter

unicode.csv

18.2 KB (18,653 bytes) · 200 rows x 7 columns

Names, cities and short notes in Simplified Chinese, Japanese, Korean, Greek, Russian, Hindi, Arabic and Hebrew, plus accented Latin, a status column carrying emoji, and a column of flag emoji. The two right-to-left scripts matter more than they look: a terminal or a table that measures width in code units will mangle the alignment, and a naive truncation will cut a character in half. The flag column is a good trap on its own, since each flag is two code points and reports a length of 4 rather than 1. Good for testing UTF-8 handling, column widths, sorting and any place where a byte count was used where a character count was meant.

Download unicode.csv · run it through the encoding fixer

wide-60col.csv

173.7 KB (177,833 bytes) · 500 rows x 60 columns

Sixty columns: a row id, a capture date, a segment, then 40 numeric metrics, 12 booleans and 5 label columns. Plenty of tools handle a hundred thousand rows and then lay out badly, scroll strangely or truncate the header once a file goes past a dozen columns. This one is deliberately wider than a screen and narrower than an extreme, so it tests layout and horizontal scrolling rather than a memory limit. It is also a useful pivot input, since the labels give you six column groups against three segments.

Download wide-60col.csv · profile all 60 columns · cut it down to the columns you want

leading-zeros.csv

16.7 KB (17,095 bytes) · 300 rows x 6 columns

The file that catches the single most common CSV bug. A five digit record id, US ZIP codes that start with 0, EAN-13 barcodes, 17 digit account numbers and a part code with a hyphen in it. Open it in a spreadsheet with default settings and watch 01730 become 1730 and a 17 digit account number turn into 1.234E+16, at which point the last digits are gone for good. Only the quantity column is genuinely numeric, and a tool that gets this right will say so: the other five columns should come back as text with their widths intact, 5 characters for the ZIP, 13 for the barcode, 17 for the account.

Download leading-zeros.csv · convert it to Excel and check

orders.jsonl

104.1 KB (106,567 bytes) · 500 records, 8 top-level fields

One JSON object per line, the shape that eval runs, chat exports, event logs and API dumps all arrive in. Each record carries an order id, a timestamp, a status, a nested customer object with four keys, an item count, a decimal total, a currency and a boolean. The nesting is the point: a converter has to decide whether to flatten it, drop it, or write [object Object] into a cell. Flattened with dot notation the 8 top-level fields become 11 columns, with customer.id, customer.name, customer.city and customer.country in the middle.

Download orders.jsonl · convert it to CSV

One dataset, every structured format

The files above test how a tool reads rows. The files below test how it reads formats. All 14 carry the same Acme retail dataset: 60 customers, 24 products and 250 orders joined by real foreign keys, where orders.customer_id points into customers and orders.product_id into products. So whatever your tool does with the Parquet, you can compare it byte for byte against what it does with the workbook. Formats that can hold more than one table hold all three.

acme-workbook.xlsx

97.6 KB · 3 sheets: customers, products, orders

A real Excel workbook with one sheet per table. This is the file to point a sheet picker at.

Download

acme.duckdb

1.0 MB · 3 tables · written by DuckDB v1.4.3

A native DuckDB database file holding all three tables, version-matched to the engine this site runs in your browser.

Download

acme.sqlite

40 KB · 3 tables

The same three tables as a SQLite database, the file format half the world's apps keep their state in.

Download

acme-orders.parquet

5.3 KB · 250 rows · Snappy compression

The orders table in the column store format every data pipeline speaks. Note the size next to the CSV of the same rows.

Download

acme-customers.arrow

6.4 KB · 60 rows · IPC file layout, uncompressed

Customers as an Arrow (Feather) file, deliberately uncompressed, because browser readers reject compressed record batches.

Download

acme-orders.avro

10.8 KB · 250 records · container file with embedded schema

Orders as an Avro object container, the row format Kafka topics and data lakes hand you.

Download

acme-report.pdf

37.4 KB · 2 pages, 2 tables

Products and customers laid out as tables inside a PDF. The worst common way to receive data, and the file to test extraction against.

Download

acme-customers.json

12 KB · array of 60 objects

Customers as a plain JSON array with typed values, so numbers arrive as numbers.

Download

acme-products.xml

4.3 KB · 24 records

Products as element-per-field XML, the shape feeds and legacy exports use.

Download

acme-products.tsv

1.0 KB · 24 rows

Products tab-separated, the delimiter clipboard pastes and database dumps favor.

Download

acme-orders.csv.gz

3.0 KB compressed · 12 KB inflated

The orders CSV gzipped, the way servers hand over logs and exports.

Download

acme-tables.zip

5.6 KB · 3 CSVs inside

All three tables as CSVs in one archive, the file to point a zip picker or a batch importer at.

Download

acme-orders-nested.json

42.1 KB · 250 orders under one "orders" key

The same orders as an API response would send them: wrapped in an envelope, each with a customer object and a line-items array inside it. The file to test a flattener against.

Download · open it in the JSON viewer

acme-invoice.docx

1.7 KB · a Word document with one order table in it

An invoice for one Acme order, as Word writes it. Table extraction from a .docx is its own problem, and this is the smallest honest example of it.

Download · open it in the document viewer

Every one of these opens directly in the editor, and the multi-table files offer a picker for which tables or sheets to load. Need different data in these formats? The test data generator downloads your own schema as Parquet, DuckDB, SQLite, Arrow, Excel and more.

The purpose-built fixtures

The rest of the library is not here to be browsed. Each of these was written for one page, because the Acme data would have shown nothing interesting there: a diff needs two files that differ in exactly the ways the options are about, a fixed-width reader needs a report with a two-word city in it, an outlier detector needs a planted outlier. They are listed here so that nothing on the server is a file you can only find by guessing its name.

Pairs, because one file proves nothing

A comparison needs two inputs that differ in the ways the options are about, and a join needs two tables that really share a key. These come in sets.

diff-before.csv · diff-after.csv537 bytes

Five orders, and between the two files one status change, one that differs only in letter case, one with a trailing space, one row removed and one added. Every toggle on the diff page has something to act on, and one row is byte-identical so "include unchanged" is not empty.

Compare them

diff-before.xlsx · diff-after.xlsx35.9 KB

The same five orders as two-sheet workbooks, with the same sheet names on both sides, so you can see which sheet the comparison picked before you hand it a real workbook.

Compare the workbooks

merge-orders.csv · merge-customers.csv545 bytes

Seven orders and five customers keyed on customer_id, with one customer who never ordered and one order pointing at a customer who is not in the list, so the match report has both kinds of miss in it.

Merge them

orders.csv · customers.csv870 bytes

Ten orders and six customers on customer_id. The clean join: every key matches, which is what you want when you are demonstrating the join itself rather than its failure modes.

Join them

region-targets.csv285 bytes

Five regional targets with a manager and an office, meant to be joined against orders.csv in SQL so a query has a dimension table to reach for.

Query both in SQL

revenue-baseline.csv · revenue-current.csv818 bytes

Twelve rows of revenue by region and channel, then thirteen: one region appears only in the current file, one only in the baseline, and the current file carries an extra column. Variance analysis with real gaps in it.

Run the comparison

sales-q1.csv · sales-q2.csv · sales-q3.csv393 bytes

Three quarters of sales where the headers do not agree: Q2 adds a channel column, Q3 drops region and adds a rep. Stacking them is the whole point, and the union has to decide what to do about that.

Append all three

Every format that is not a CSV

One small, correct file per format, so a reader can be checked against something whose right answer is obvious at a glance.

parks.geojson2.3 KB

A FeatureCollection of points and one polygon, with properties on every feature, so a flattener has to decide what a geometry becomes in a column.

Convert it to CSV

morning-ride.gpx2.8 KB

One GPS track with elevation and timestamps on each point, the shape a watch or a phone actually exports.

Convert it to CSV

store-locations.kml1.9 KB

Placemarks with names, descriptions and coordinates, the format Google Earth and My Maps hand back.

Convert it to CSV

places.csv763 bytes

Twelve cities with latitude, longitude, population and elevation. The other direction: the file to turn into GeoJSON, KML, GPX or WKT, or to drop on a map.

Put it on a map

services.toml1.2 KB

A deployment matrix as TOML, with a table array, nested tables and a mix of types, so a converter has to flatten more than key equals value.

Convert it to CSV

database.ini781 bytes

Sections, comments and duplicate keys across sections, the way a real config file drifts over a few years.

Convert it to CSV

services.yaml875 bytes

The same deployment matrix as YAML, a list of mappings with nested keys, for the YAML readers.

Convert it to CSV

inserts.sql1.6 KB

A dump with two tables in one file, a lookup table and the rows that reference it, which is how a dump usually arrives.

Convert it to CSV

table.md819 bytes

A Markdown table with an alignment row and an escaped pipe inside a cell, sitting under a heading and a paragraph so a parser has to find the table.

Convert it to CSV

sales-table.html963 bytes

One HTML table with a caption, a thead and a colspan in it, the shape a report page produces.

Convert it to CSV

two-tables.html479 bytes

Two tables in one page, so the reader has to offer a choice rather than silently taking the first.

Convert it to CSV

orders-nested.json1.2 KB

Four orders with a customer object holding an address, an array of tags, a null, and a ZIP code whose leading zero has to survive.

Convert it to CSV

fixed-width-report.txt432 bytes

A branch report aligned by padding rather than separators, with New York and San Francisco in it, so a column guesser cannot just split on spaces.

Split it into columns

inventory-report.txt1.1 KB

A longer fixed-width report with a header band and right-aligned numbers, for the dedicated fixed-width reader.

Read the columns

fixed-width-ledger.txt2.7 KB

Thirty-one ledger lines with page breaks and a totals row in them, the sort of print-ready text file that is easier to read than to parse.

Open it in the text viewer

One fixture per tool

Small files shaped so a single tool has something interesting to say about them. Each one loads on the page it was made for.

quality-messy.csv580 bytes

Twelve rows carrying nine defects at once: a duplicate row, an impossible date, two date formats, a currency string, mixed case in a category, padded whitespace, a blank name and the word ninety in a numeric column.

Validate it

ragged-mixed.csv341 bytes

Eight orders where one row is short, one date is not a date and the boolean column arrives as true, TRUE and 1. Enough for a profiler to report mixed types honestly.

Profile it

messy-columns.csv801 bytes

Nine header names with trailing spaces, a space before a comma, and a Legacy Code column that is entirely empty. The file to point a header cleaner at.

Clean the headers

crm-export.csv686 bytes

A CRM export with padded names, an uppercase email, day-first dates and inconsistent company spellings, used by the standardize-a-CRM-export recipe.

Run the recipe

dupes.csv658 bytes

Ten rows with exact duplicates and near-duplicates in them, so both the strict and the fuzzy deduplicator have work to do.

Deduplicate it

contacts-pii.csv1.4 KB

Eight contacts carrying names, emails, phone numbers, a card number, an IP address, a date of birth and a national ID, so a scanner can be judged on what it finds and what it misses.

Scan it for PII

store-metrics.csv2.6 KB

Sixty-two stores with footfall, basket size, revenue and satisfaction, and two rows planted well outside the distribution so outlier detection has a right answer.

Find the outliers

daily-signups.csv3.2 KB

One hundred and fourteen consecutive days of signups, revenue and refunds, with a weekly rhythm in it, for resampling and moving averages.

Roll it up by week

ab-test-checkout.csv1003 bytes

Fifty users split between control and treatment with a conversion flag and a revenue column, a difference big enough to see and small enough to argue about.

Run the test

access-log-lines.csv857 bytes

Twelve raw log lines in one quoted column, one of them malformed on purpose, so a regex has to cope with a line it cannot match.

Test a pattern on it

subscriptions-schema.csv888 bytes

Ten subscriptions covering every type a schema generator has to name: a zero-padded reference, an integer, a decimal price, a date, a timestamp, a boolean and a nullable note.

Generate a schema

sales-wide.csv354 bytes

Region and product with one column per month, including a gap. The wide table to unpivot or transpose.

Unpivot it

sales-long.csv456 bytes

The same figures the other way round, one row per region, product and month, for going back to wide.

Pivot it wide

release-notes.csv286 bytes

Five releases whose notes contain embedded newlines and escaped quotes, which is where a Markdown writer usually falls over.

Convert it to Markdown

qr-products.csv465 bytes

Eight products with a URL each, including a name with a quoted comma in it, so RFC 4180 quoting is visible in the output rather than assumed.

Turn it into QR codes

Three folders of them

Three sets are large enough to live in their own folder. They are not linked file by file here, because each one already has a page that loads it.

/samples/integrations/57 files · 713.0 KB

Exports shaped like the real thing from Stripe, Shopify, HubSpot, Google Analytics, Strava, Jira and fifty-one others, each with that product's own column names and quirks.

Each one is the sample on its own dashboard page: /dashboards/

/samples/templates/14 files · 99.5 KB

One small, well-shaped table per chart and dashboard template: sales, funnel, cohort, KPI, inventory, survey and the rest.

Loaded by the chart and dashboard templates: /templates/

/samples/lessons/5 files · 16.0 KB

The five tables the SQL course runs on, committed as files so a lesson works with no network request and a learner can take the data away.

Used throughout the SQL on CSV course: /learn/sql-on-csv/

Picking the right one

  • Writing a tutorial or taking a screenshot? people-100.csv. Short, readable, nothing surprising in it.
  • Testing error handling? messy.csv. If your code survives that file, it survives most exports.
  • Testing delimiter and locale handling? semicolon-eu.csv. Half of Europe's Excel installs write files that look like this.
  • Testing encoding and text rendering? unicode.csv. Right-to-left text and emoji in the same file.
  • Testing type inference? leading-zeros.csv. Any tool that turns a ZIP code into a number fails visibly.
  • Testing layout? wide-60col.csv. Wider than a screen, so the header handling shows.
  • Testing performance, or want something to aggregate? sales-10k.csv. Ten thousand typed rows.
  • Testing a JSON to CSV path? orders.jsonl. Nested objects, one record per line.

None of these needs to be uploaded to be used here. Every tool on this site reads the file in your own browser, so you can download a sample, drop it straight back into a page, and watch what happens without anything crossing a network.

Frequently Asked Questions

Is any of this real data?

None of it. Every name, address, order, account number and barcode was generated by a script. The people are borrowed from the history of computing, the companies are invented, and every email address ends in example.com, which is the domain reserved by RFC 2606 precisely so that test data cannot reach anybody. There is no personal information in these files and nothing to anonymise.

Can I use these commercially, and do I have to credit you?

Yes, and no. The files are dedicated to the public domain under CC0 1.0. Put them in a course, a paid product, a test suite, a bug report, a screenshot, a conference talk or a book, with or without attribution, and there is nothing to ask for and nothing to sign. A link back is welcome and is not a condition.

Which file should I use to test my CSV parser?

Start with people-100.csv to prove the happy path, then reach for the file that matches the thing you doubt. messy.csv for error handling, semicolon-eu.csv for delimiter sniffing and decimal commas, unicode.csv for encoding and column widths, leading-zeros.csv for type inference, wide-60col.csv for anything that assumes a handful of columns, and sales-10k.csv when you want enough rows for the timing to mean something.

What exactly is wrong with messy.csv?

Six separate things, all of them on purpose. The header uses the name "name" twice. Twelve rows do not carry the header's six fields, some short and some long. Two replacement characters sit on line 59 where an encoding step lost something. Thirteen lines end in CRLF while the other 194 end in LF. Six rows are completely blank. Fifty cells carry leading or trailing whitespace. Run it through the CSV validator and all six come back with line numbers.

Are the files stable, or do they change?

Stable. They were produced once by a seeded generator and committed as fixed files, so the byte count, the row order and every value stay the same. That matters if you are writing a test that asserts a row count or a checksum: the URL will keep serving the same bytes, and any future change would be a new file at a new name rather than a quiet edit to this one.

Why is there a JSONL file on a CSV page?

Because most of the CSVs people are handed today started life as JSON Lines: eval outputs, chat exports, event logs and API dumps all arrive one JSON object per line. orders.jsonl carries a nested customer object inside each record, so it exercises the part of a converter that has to decide what to do with nesting. Flattened with dot notation it becomes eleven columns from eight top-level fields.

Do you have samples in Excel, Parquet, or database formats?

Yes. The Acme retail dataset, 60 customers with 24 products and 250 orders joined by real foreign keys, is served in fourteen structured formats: a three-sheet Excel workbook, DuckDB and SQLite database files each holding all three tables, Parquet, Arrow, Avro, a two-table PDF report, a Word invoice, flat JSON, a nested JSON feed, XML, TSV, a gzipped CSV and a zip of three CSVs. Because every file carries the same data, you can compare what your tool does with each format directly.

Why are there so many small files I have never heard of?

Because a tool is only worth trying if the example shows it doing something. A diff needs two files that differ in exactly the ways its options are about, an outlier detector needs a planted outlier, a fixed-width reader needs a report with a two-word city in it, and a schema generator needs one column of every type. Each of those fixtures was written for one page and is the sample that page loads. They are all listed above so nothing on the server is a file you can only find by guessing its name.

Where are the integration and dashboard samples?

Under /samples/integrations/, which holds 57 exports shaped like the real thing from Stripe, Shopify, HubSpot, Google Analytics, Strava, Jira and the rest, each with that product's own column names. They are not listed file by file here because each one is the sample on its own dashboard page. Two smaller folders sit next to it: /samples/templates/ holds the 14 tables the chart and dashboard templates load, and /samples/lessons/ holds the 5 tables the SQL course runs on.

Take what you need

66 files, 2.2 MB in total, public domain, no sign-up. Download one and drop it straight into any tool on this site.

Back to the files