# Fun Data Playground — full text > Every indexable page of https://fundata.dev as plain text, generated from > the site itself. /llms.txt is the index of what exists; this is what it > says. Content is CC0 where marked and otherwise free to quote with > attribution to https://fundata.dev. ============================================================================ # About Fun Data Playground — Free Synthetic Test Data URL: https://fundata.dev/about ============================================================================ Home / About About Fun Data Playground A free synthetic test data generator that runs entirely in your browser. This page covers what it is, the three decisions that shape it, and the limits it keeps on purpose. What this is Fun Data Playground generates synthetic test data. You describe a schema — columns, each with a type and options — and it produces up to 100,000 rows and exports them as CSV, TSV, JSON, NDJSON, SQL or XML. There are 68 field types, and the values look like real data without being anyone's real data. It exists because the alternatives are all worse in a specific way. Production data in a development database is a breach waiting for a misconfigured backup. Hand-written fixtures are twelve rows of test1@test.com that never exercise a Unicode bug or a null. Hosted generators want an account and meter the row count. And faker in a loop is fine until you need the same thousand rows again next week. The three decisions that shape it Everything runs in your browser There is no generation API and no server that sees your schema. This is not a privacy posture bolted on afterwards — it is why there are no limits. A row that costs nothing to produce needs no tier, no quota and no account, and a tool that never receives your data cannot leak it. It also means the site keeps working offline once loaded, and that the converters are safe for files you would not paste into a hosted tool. The same seed gives the same data Enter any string as a seed and the output is byte-identical every time, forever. That single property is what makes generated data usable as a test fixture: a CI run that regenerates its data on every run is a CI run that fails differently every Tuesday. It is also why the sample files can be committed and linked — the bytes behind those URLs are fixed. What the data cannot do is documented Generated IBANs fail mod-97 validation. Generated UUIDs are seeded, so they are not cryptographically random. Card numbers are the four published test PANs and nothing else. Every one of those is a deliberate limit, and each page says so where you would otherwise find out the hard way. The methodology page covers the rest: where each dataset comes from, which reserved ranges are used, and what none of it is safe for. How it is built It is a static site: hand-written HTML, CSS and vanilla JavaScript, no framework, no build step and no dependencies. That is unusual enough to be worth stating, because it is what the privacy claim rests on — there is no bundler, no third-party script and no analytics tag that has not been opted into, so "runs in your browser" is inspectable rather than promised. The checks are the same shape: dependency-free scripts that run before every deploy. They assert that the generator produces what the pages claim it produces, that the sitemap, llms.txt, the cache rules and the footer all still list the same pages, and that every structured-data date agrees with the sitemap. Those lists used to be maintained by hand across forty files, which is the kind of thing that is correct on the day you write it and wrong a month later. The sibling sites Fun Data Playground is one of three, sharing a design language and a purpose — practice and test material that is free and needs no signup: funui.dev — UI patterns to practise test automation against. funapi.dev — mock REST endpoints to develop and test against. Every interactive control here carries a stable data-testid, on purpose: the site doubles as a target for practising UI automation, and renaming those would break someone's tutorial. What it will not do Ask you to sign in to generate. Accounts sync settings across devices; they are never a gate. Put your generated data anywhere. There is no upload path in the code. Claim ratings it does not have. There is no review markup on this site, because there are no reviews. Generate anything meant to pass as real. Fake IBANs fail validation and test cards are the published ones, both deliberately. Common questions Who is this for?Developers, QA engineers, data engineers, analysts and students — anyone who needs realistic-looking rows and cannot or should not use production data. It is used for seeding development databases, mocking API responses, building fixtures for automated tests, filling spreadsheet and BI demos, and teaching. Is it really free, with no limits?Yes. Generation runs in your browser, so there is no per-row server cost to meter and nothing to paywall. There is no account requirement, no row-count tier and no watermark. The only cap is 100,000 rows per export, which is a practical browser-memory limit rather than a commercial one. Why should I trust the data is safe to use?Because of what it is made of. Email addresses use domains reserved by RFC 2606 and RFC 6761 that cannot receive mail; IP addresses come from documentation ranges; card numbers are the published payment-gateway test PANs; IBANs are deliberately invalid. The methodology page documents every one of those choices and what each value cannot be used for. Does my data ever leave my machine?No. The generator, the converters and the exports are all client-side JavaScript. Nothing you type into a schema and nothing generated from it is transmitted anywhere. Aggregate visit analytics are separate, load only after an explicit opt-in, and never include generated data. Do I need an account?No. Signing in is entirely optional and exists only to sync saved schemas, datasets and settings across your own devices. Everything works without one, saved to your browser’s local storage. Related /methodology — how each value is generated, and what it is not safe for. /changelog — what has changed, most recent first. /guide — the practical walkthrough, if you would rather start using it. /types — all 68 field types, with options and examples. Last updated 6 September 2026 ============================================================================ # Mock API Data Generator & JSON Fixtures — Fun Data URL: https://fundata.dev/api-mock-data ============================================================================ Mock API data generator and JSON fixtures Give frontend and integration work realistic typed responses before a production backend or stable test environment exists. Generate JSON data →JSON format details Model the response contract Name fields after the API contract and select generators with matching JSON types. Use Integer or Decimal rather than numeric-looking text, Boolean for flags, Date or Datetime for timestamps and Blank % for nullable properties. Patterns and custom lists cover status codes, SKU shapes and finite enums. Build useful response states A happy-path list is only the beginning. Save separate schemas for active users, incomplete profiles, cancelled orders and sparse search results. Keep a fixed seed for visual regression baselines, then change the seed when exploratory testing needs fresh combinations. JSON array or NDJSON stream? JSON returns one array and is the natural fit for REST fixtures, browser imports and intercepted responses. NDJSON writes one object per line and is better for log ingestion, bulk endpoints and streaming consumers. Serve the fixture locally // Express example import users from './fixtures/users.json' with { type: 'json' }; app.get('/api/users', (req, res) => res.json(users)); You can also return the file from Playwright route interception, a service worker, json-server or the sibling Fun API Playground. Wrapping the array in your envelope Few APIs return a bare array. The export is flat on purpose — reshaping is one jq step, and doing it in your fixture pipeline keeps the transformation visible rather than hidden in a generator option: # A paginated envelope jq '{ data: ., page: 1, per_page: length, total: length }' users.json > users-page-1.json # JSON:API style jq '{ data: [ .[] | { type: "users", id: (.id|tostring), attributes: . } ] }' users.json # Key by id for a mock store or MSW handler jq 'INDEX(.id)' users.json > users-by-id.json The response states worth generating Most mock fixtures cover the case the UI was designed around and nothing else, which is why empty states and long values are where the bugs are found later. Each of these is a separate seed against the same schema: Empty collection. Set the row count to 1, export, and replace the contents with []. Skeleton loaders, pagination controls and "no results" copy all fail differently here. Exactly one item. Catches pluralisation, grid layouts that assume a second column, and carousels that need two slides. A full page plus one. If the page size is 20, generate 21. This is the smallest fixture that exercises pagination honestly. Sparse objects. Push Blank % to 40–60% on optional fields. Every unguarded property access surfaces at once. Long and international values. Realistic names in several scripts, plus a long free-text field, find truncation and layout breakage that foo never will. A large list. 10,000 rows tells you whether the table virtualises or the browser stalls. Keep them in one directory, one file per state, and the fixture set doubles as documentation of what the component is expected to survive. Contract-test checklist Property names and JSON types match the documented contract. Nullable properties include both values and null. Dates use the expected timezone and representation. IDs are unique where clients use them as keys. Strings include realistic long and international values. Fixtures contain empty-list and partial-object variants. Keep fixtures safe Use generated documentation-domain emails and test-only network/payment values instead of copying real user data. Read the methodology and safety notes, then store the exported schema with the consuming test so future changes remain intentional. Common questions How do I create mock JSON data for an API? Name the fields the way the response names them, using dots for nesting — user.name, user.email — then export JSON. The result is an array of records with real types: numbers stay numbers, booleans stay booleans, and Blank % produces the nulls your client code has to survive. How do I mock a response envelope around the array?Generate the array, then paste it into the envelope your API actually returns. The generator produces the collection; the wrapper — a data key, a pagination block, a status field — is a constant, and writing it once by hand is more honest than pretending it was generated. Should I use JSON or NDJSON for API fixtures?JSON for a response body, because that is what a client parses. NDJSON when something consumes records one at a time — a stream, a log pipeline, a bulk import endpoint — since each line is a complete record and nothing has to hold the whole array in memory. How do I test error and empty states with generated data?Generate them as separate fixtures rather than trying to express them in one. An empty array, a single record, a page-sized batch, and a batch with nulls in every optional field cover most of what a client gets wrong, and each is a one-line change to the row count or a Blank % setting. Can I type my API client from the same data?Yes. Paste the generated JSON into the TypeScript, Zod, Pydantic or JSON Schema generator and the declaration is inferred from every record rather than the first, so a key missing from some records comes out optional rather than required. Last updated 6 September 2026 ============================================================================ # Changelog — Fun Data Playground URL: https://fundata.dev/changelog ============================================================================ Home / Changelog Changelog What has changed, newest first. Also available as an Atom feed. Every section has an address, every claim has a source 2026-09-06 Every field type has its own anchor. /api/types.json publishes a documentation link per type, and 67 of the 68 pointed at a fragment that did not exist — they all landed at the top of the reference. They resolve now, and each type carries a real definition in the page's structured data instead of just a name. Section anchors and a contents list. 337 headings had no id, so a page could only be linked as a whole. Every content section is addressable now, and the longer pages open with an "On this page" list. The standards are linked. Pages that mention RFC 4180, RFC 2606 and 6761, RFC 4122, ISO 8601, E.164 or JSON Schema draft 2020-12 now link the document that defines them, rather than asking you to take the claim on trust. Questions answered where they are asked. Use cases, database, mock API, QA, spreadsheet, guide and methodology pages gained an FAQ; the five "alternative to" pages gained a side-by-side table. Two counts were wrong. The site advertised a vague “60+” in 24 places and the exact number in a handful of others; there are 68 field types. It also claimed nine name locales where there are eight locales — the ninth entry in the dropdown is “any”, which is not one. Both counts are asserted by the test suite now, against the generator itself. Every page shows when it was last updated, and there is a full-text copy of the site in one file alongside llms.txt. Format converters, and files you can actually download 2026-09-05 Five format converters. CSV to JSON, JSON to CSV, CSV to SQL, JSON to SQL and XML to JSON. They run in your browser like everything else here, so nothing is uploaded. Each page documents the decisions the conversion forces — what happens to a leading zero, to a nested object, to a repeated XML element. Static sample files. The sample pages now offer real files under /samples/: users, employees, orders and sensor readings, as CSV, JSON and SQL, at 100 and 1,000 rows. They are generated from a fixed seed and committed, so a link to one keeps returning the same bytes. Public domain, no attribution needed. A generator on every generator page. UUIDs, names, emails and the rest now generate a live example on the page instead of describing one. The button beside it opens the full builder on that page's schema — it used to open the default Users template, which was unhelpful on a page about IBANs. A machine-readable field catalogue at /api/types.json, generated from the generator itself. ============================================================================ # CSV to JSON Converter — Free, Private, In Your Browser URL: https://fundata.dev/csv-to-json-converter ============================================================================ Home / CSV to JSON CSV to JSON converter Paste a CSV file and get a JSON array back. The conversion happens in this tab — nothing is uploaded, so it is safe for data you would not paste into a hosted tool. Other languages:TürkçeEspañol The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Type detection, and the leading-zero rule Every cell in a CSV file is text. JSON has real types, so a converter has to guess which text was meant to be a number, a boolean or a null — and that guess is where CSV-to-JSON conversions quietly corrupt data. Turn detection off with the checkbox and every value comes through as a string, which is the safe default when you are not sure. With detection on, the rule that matters most is that a leading zero is never a number. 007 stays the string "007", and so do 01234, +1 555 0100 and 0000-0002-1825-0097. This is not a nicety: zip codes, phone numbers, SKUs, ISBNs, ORCIDs and German postal codes are all digit strings whose leading zeros carry meaning, and the classic spreadsheet bug is exactly this conversion done carelessly. CellWith detectionWhy 4242Plain integer. -3.5-3.5Plain decimal. 00A bare zero has no leading zero to lose. 007"007"Leading zero is significant. 1.50"1.50"The trailing zero would be lost. 1e5"1e5"Round-tripping would rewrite it as 100000. true / TRUEtrueCase-insensitive boolean. nullnullCase-insensitive null. (empty)nullAn empty cell is an absent value, not an empty string. +1 555 0100"+1 555 0100"Not a bare number. The general test is round-tripping: a value is converted only if writing the resulting number back out produces the original text exactly. That single rule is what keeps 1.50, 1e5 and 007 intact. What the converter handles Quoted fields — commas, quotes and newlines inside "…", with "" as an escaped quote. Tabs or commas — the delimiter is taken from the header row, so TSV works without changing anything. CRLF or LF — Windows and Unix line endings both parse. Ragged rows — a short row gets empty values for its missing columns rather than shifting them. Unnamed columns — a blank header cell becomes col_3 rather than an empty key. What it deliberately does not do It does not nest. A CSV column called user.name becomes a JSON key literally called "user.name", not a nested object — the reverse direction on the JSON to CSV page flattens with dots, but reading dots back as structure would silently reshape any file whose column names happen to contain a full stop. It also does not stream. The whole file is parsed in memory, so keep inputs under about 2 MB; past that, split the file or use a command-line tool. And it does not repair broken CSV: a file with an unclosed quote will parse to something, but not to what you meant. Common questions Is my file uploaded anywhere?No. The conversion is JavaScript running in your own tab — the file never leaves your machine, and the page works offline once it has loaded. That is also why there is no file size limit imposed by a server, only by your browser’s memory. Why is my zip code a string?Because it starts with a zero. A value is converted to a number only when writing that number back out reproduces the original text exactly, which keeps 007, 01234 and 1.50 intact. Turn off type detection if you want every column as a string. Can I convert TSV instead of CSV?Yes. The delimiter is detected from the header row, so a tab-separated file converts with no change in settings. Does it handle commas inside a field?Yes, if the field is quoted, which is what RFC 4180 requires. "Smith, Ada" comes through as one value; the parser also handles escaped quotes ("") and newlines inside quoted fields. What happens to an empty cell?With type detection on it becomes null, because an empty cell in a CSV file almost always means "no value" rather than "the empty string". With detection off it stays an empty string. Related /json-to-csv-converter — the same conversion in the other direction, including how nesting is flattened. /csv-to-sql-converter — skip JSON and go straight to CREATE TABLE and INSERT statements. /json — generating typed JSON test data from scratch rather than converting a file you have. /sample-csv-files — ready-made CSV files to try the converter on. Last updated 6 September 2026 ============================================================================ # CSV to SQL Converter — CREATE TABLE and INSERT Statements URL: https://fundata.dev/csv-to-sql-converter ============================================================================ Home / CSV to SQL CSV to SQL converter Turn a CSV file into a CREATE TABLE and a batched INSERT, with column types read from the data rather than guessed as TEXT. Runs in this tab; nothing is uploaded. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. How column types are inferred A converter that emits forty columns of TEXT has not saved you any work. This one reads every value in a column and picks the narrowest type that fits all of them. Nulls and empty cells are ignored when deciding, so one blank row does not push a numeric column back to text. When every value is…Column type true / falseBOOLEAN a whole number, at most 15 digitsBIGINT numeric with a decimal pointDECIMAL(18,6) YYYY-MM-DDDATE YYYY-MM-DD followed by a timeTIMESTAMP a UUIDUUID text, longest value ≤ 255VARCHAR(n) text, longest value > 255TEXT all emptyTEXT Why VARCHAR widths are rounded up A column whose longest value is 41 characters does not become VARCHAR(41). It becomes VARCHAR(64): the width is the longest value plus half again, rounded up to a multiple of 16 and capped at 255. Sizing a column to the sample is the mistake that makes the schema work perfectly on the file you converted and reject the next one — the sample tells you the order of magnitude, not the limit. Quoting and injection Identifiers are quoted for the dialect you pick — "double quotes" for PostgreSQL, `backticks` for MySQL — so a column called order or group does not collide with a keyword. Single quotes inside values are doubled, which is the escaping that matters here: the output of this page is, by definition, text someone is about to paste into a database console. What it produces One CREATE TABLE followed by a single multi-row INSERT. A batched insert is dramatically faster than one statement per row — on most engines the difference is an order of magnitude on a few thousand rows, because it is one parse and one transaction rather than thousands. Empty cells become NULL, not ''. If your loader needs the opposite, turn type detection off and the empty strings come through literally. Before you run it There is no primary key. Nothing in a CSV file says which column is one. Add the constraint yourself, or add an identity column. There are no indexes. Add them after the insert, not before — building an index while loading is slower than building it once at the end. Dates are not validated. A column that looks like a date is typed as one; a row with 2024-02-31 in it will be rejected by the database, which is the right place for that to fail. Very large files belong in a bulk loader. Past a few thousand rows, COPY or LOAD DATA will beat any INSERT. Common questions Which SQL dialects are supported?PostgreSQL and MySQL. The difference is identifier quoting — double quotes versus backticks — and both outputs are close enough to standard SQL to run on SQLite and SQL Server with small edits. Does it create a primary key?No. Nothing in a CSV file identifies which column is the key, so guessing would be wrong as often as it was right. Add the constraint after the CREATE TABLE, or add an identity column. How are column types chosen?By reading every value in the column and picking the narrowest type all of them fit — boolean, bigint, decimal, date, timestamp, uuid, varchar or text. Empty cells are ignored when deciding, so one blank row does not force a numeric column to text. Are values escaped safely?Single quotes inside values are doubled, which is the SQL string escape. Identifiers are quoted for the dialect, so a column named order or group does not collide with a keyword. Is the file uploaded to a server?No. Everything runs in your browser, which matters more here than usual — the CSV files people convert to SQL tend to be the ones they cannot paste into a hosted tool. Related /sql — generating SQL inserts from a schema instead of converting a file. /postgresql-test-data — COPY, sequences and deferred constraints when the insert gets large. /mysql-test-data — LOAD DATA, utf8mb4 and AUTO_INCREMENT notes. /json-to-sql-converter — the same output, starting from JSON. Last updated 6 September 2026 ============================================================================ # CSV to TypeScript Interface Generator — In Your Browser URL: https://fundata.dev/csv-to-typescript ============================================================================ Home / CSV to TypeScript CSV to TypeScript interface generator Paste a CSV file and get the interface describing one parsed row. Column types come from the values, not from the header. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. The type depends on how you parse, not on the file A CSV file has no types. Every cell is text, so the interface that describes a parsed row depends entirely on what your parser did — and this is the mismatch that produces a type which is correct on this page and wrong in your code. With type detection on, the interface describes rows from a parser that converts values: signups: number, active: boolean. That matches csv-parse with cast: true, Papa Parse with dynamicTyping: true, or pandas. With detection off, every field is string — which is what a plain split(','), a default Papa Parse, or csv-parse without casting actually gives you. If your runtime types disagree with your compile-time types, this switch is usually the reason. The leading-zero rule applies here too Even with detection on, 01234 stays a string, because a value is converted only when writing the number back out reproduces the original text exactly. So a zip code column is typed string while a quantity column is typed number — which is right, and is what a naive cast gets wrong in both directions. One interface, not a nested tree A CSV row is flat, so the output is a single interface with one property per column. A column named user.name becomes a quoted key 'user.name' rather than a nested object: dots in a CSV header are part of the name, and reading them as structure would reshape any file whose columns happen to contain a full stop. If you want the nested version, convert to JSON first with the CSV to JSON converter and paste that into the JSON generator. Empty cells With detection on an empty cell is null, so a column that is empty anywhere is typed string | null. That is usually what you want and occasionally not — if your parser produces '' for an empty cell rather than null, turn detection off and the whole row is strings. Paste enough rows Types come from the values present, so a five-row paste describes those five rows. The column that is empty only on row 900, or numeric until someone typed "N/A", will not show up. Paste the header plus a genuinely representative sample — including the rows you know are awkward. Common questions Why is my number column typed as string?Either type detection is off, or the values are not plain numbers — a leading zero, a trailing zero after a decimal point, thousands separators or a currency symbol all keep a column as text, deliberately. Which parser does the output match?With type detection on it matches a parser that casts values: csv-parse with cast: true, Papa Parse with dynamicTyping: true, or pandas. With detection off it matches a plain split or a default Papa Parse, where every field is a string. How do I get nested interfaces from a CSV?You cannot directly, because a CSV row is flat. Convert to JSON first, then paste that into the JSON to TypeScript generator. What happens to a column with empty cells?With type detection on an empty cell becomes null, so the column is typed as a union with null. With detection off it stays an empty string and the column is just string. How many rows should I paste?Enough to be representative. Types are inferred from the values present, so a column that is only awkward on row 900 will not be reflected in a five-row sample. Related /csv-to-json-converter — converting the file itself rather than describing it. /json-to-typescript — nested interfaces, from JSON. /csv-to-sql-converter — a table definition instead of an interface. /sample-csv-files — sample CSV files to try it on. Last updated 6 September 2026 ============================================================================ # CSV Test Data Generator — Fun Data Playground URL: https://fundata.dev/csv ============================================================================ CSV test data generator Build a schema from 68 realistic field types and download it as clean, spreadsheet-ready CSV — up to 100,000 rows, no signup, 100% client-side. Generate CSV data → Browse field types What the output looks like id,first_name,last_name,email,city,registered_at 1,Elena,Rossi,elena.rossi7@example.org,Vienna,2025-03-18T09:41:26Z 2,Marco,Schneider,marco.s@example.com,Izmir,2024-11-02T14:07:52Z 3,Aisha,Yılmaz,aisha.yilmaz@example.net,Oslo,2025-01-27T08:15:03Z Why CSV? CSV is still the lingua franca of tabular data: every spreadsheet, BI tool and database import path understands it. It is the format to reach for when the data ends up in Excel or Google Sheets, a Postgres COPY or MySQL LOAD DATA statement, or a colleague's inbox. CSV-specific options Header row — toggle the first line of column names on or off to match your importer. Automatic quoting — values containing commas, quotes or newlines are quoted and escaped correctly, so full addresses and free-text sentences won't break your parser. Blank % — any field can emit empty cells at a rate you choose, which is the honest way to test how your pipeline copes with missing values. Seed — set one and every export is byte-identical, so a fixture checked into your repo stays stable. Loading CSV into your tools Every one of these expects the same file. Keep the header row on and let the tool infer column names, or turn it off when you are appending to a table that already exists. # PostgreSQL — client-side, so no server file permissions needed \copy customers FROM 'fundata_1000_rows.csv' WITH (FORMAT csv, HEADER true); # MySQL / MariaDB LOAD DATA LOCAL INFILE 'fundata_1000_rows.csv' INTO TABLE customers FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS; # SQLite .mode csv .import --skip 1 fundata_1000_rows.csv customers # pandas — keep IDs as text so leading zeros survive df = pd.read_csv('fundata_1000_rows.csv', dtype={'id': str}) # DuckDB — types inferred, no table needed up front SELECT * FROM read_csv_auto('fundata_1000_rows.csv'); CSV compatibility and edge cases Output uses commas as delimiters and standard double-quote escaping. A value is quoted only when it contains a comma, quote or newline; embedded quotes are doubled. UTF-8 preserves international names such as Yılmaz or Sørensen. Blank fields stay empty, so you can test how Excel, database imports and ETL jobs distinguish an empty cell from a populated value. For typed values and explicit nulls, use JSON instead. The awkward parts of CSV are not in the specification — they are in what the receiving tool does with a perfectly valid file. These are the ones worth deliberately generating data for: Leading zeros disappear. A postcode or product code like 00123 is a valid string, but a spreadsheet that auto-detects types will store it as the number 123. Generate a batch of zero-padded IDs and you can prove whether your importer preserves them. Long digit strings turn into scientific notation. A 16-digit card number or a large account number often lands in Excel as 1.23457E+15, silently losing the last digits. Dates get re-interpreted. 03/04/2025 is March 4th or April 3rd depending on the reader's locale. ISO 8601 timestamps avoid the ambiguity, which is why the date fields default to them. Empty is not the same as null. CSV has no null: an empty cell is an empty string, and each importer decides whether that becomes NULL, '' or a validation error. Blank % exists to make that decision visible before production data does. Line endings and the byte-order mark. Files are written with UTF-8 and \n line endings, no BOM. Some Windows tools expect \r\n or a BOM before they will treat the file as UTF-8; if accented characters arrive mangled, that is almost always the cause rather than the data. Line endings, BOMs and joining files Three CSV details cause most of the trouble that is not about quoting, and none of them are visible when you open the file. Line endings. RFC 4180 specifies CRLF, and plenty of real-world CSV uses bare LF. Almost every modern parser accepts both, but a naive split on \r\n will produce a stray \r at the end of every field when handed a Unix file — and a naive split on \n leaves one when handed a Windows one. If a parser you wrote is producing values with an invisible trailing character, this is why. The byte-order mark. Excel on Windows writes a UTF-8 BOM and uses its presence to decide the file is UTF-8 rather than the system code page. Parsers that do not strip it hand you a first column name of id instead of id, which is the cause of the classic "my first column is undefined" bug. Exports here are plain UTF-8 without a BOM, which is correct and occasionally means telling Excel the encoding explicitly during import. Joining exports. A single export is capped at 100,000 rows. For more, export several times with a different seed each time and concatenate — remembering that every file carries its own header: # keep the header from the first file only head -n 1 part-1.csv > all.csv tail -q -n +2 part-*.csv >> all.csv Use a different seed per part. The same seed produces the same rows, so concatenating identical seeds gives you one dataset repeated rather than a larger one. Common CSV questions Can I open the generated CSV in Excel or Google Sheets?Yes. Keep the header row enabled, download the UTF-8 file and import it with comma as the delimiter. The same schema can also produce spreadsheet sample data as TSV when tabs are a better fit. Can I reproduce the same CSV later?Yes. Enter a seed before exporting. The same seed and schema produce byte-identical output, which is useful for committed fixtures and repeatable QA runs. Can I change the delimiter to a semicolon or pipe?The CSV export is comma-delimited. If you need another separator, TSV covers the tab case directly; for anything else, exporting CSV and replacing the delimiter downstream is safer than generating it, because a semicolon file still needs comma-aware quoting rules applied to the values themselves. How do I stop Excel mangling IDs and postcodes?Don't double-click the file — that path gives you no type control. Use Data → From Text/CSV, then set the affected columns to Text in the import preview. Generating a column of zero-padded values first is a quick way to confirm the setting actually took effect. What is the largest CSV I can generate?100,000 rows per export. Generation and download both happen in your browser, so wide schemas at the top of that range take a moment to serialize. For a bigger fixture, export several times with different seeds and concatenate — strip the header from every file after the first. Is the data safe to commit to a repository?Yes. Every value is synthetic: emails use reserved documentation domains, card numbers use the published test prefixes, and no row is derived from a real person. The methodology page sets out exactly what each generator draws from. Need a database-ready fixture? See the database test-data guide. Other formats The same schema exports to all six formats — switch with one dropdown: TSV, JSON, NDJSON, SQL, XML. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # Cypress Test Data — Fixtures, Intercepts & Seeding URL: https://fundata.dev/cypress-test-data ============================================================================ Cypress test data Drop a generated JSON file into cypress/fixtures and every spec gets realistic, identical data — locally, in CI and on the machine of whoever reviews the failure. Generate JSON fixture →QA test data guide The fixtures folder Export JSON with a seed, save it as cypress/fixtures/users.json, and it is available by name: describe('user list', () => { beforeEach(() => { cy.fixture('users').as('users'); }); it('renders every user', function () { cy.visit('/users'); cy.getByTestId('user-row').should('have.length', this.users.length); cy.contains(this.users[0].full_name).should('be.visible'); }); }); Because the file was generated with a seed, users[0].full_name is a fixed value. That is the difference between an assertion you can read and a snapshot you have to trust. Stubbing the network The fixture pairs directly with cy.intercept, which is where generated data earns its keep — arbitrary response states become one line each: cy.intercept('GET', '/api/users*', { fixture: 'users.json' }).as('list'); cy.intercept('GET', '/api/users*', { statusCode: 500 }); cy.intercept('GET', '/api/users*', { body: [] }); // empty state cy.intercept('GET', '/api/users*', { forceNetworkError: true }); cy.intercept('GET', '/api/users*', (req) => { req.reply({ delay: 3000, fixture: 'users.json' }); // slow response }); Generate a large fixture for the pagination and virtual-scroll tests, a three-row one for layout, and an empty array for the empty state. All three come from the same schema with a different row count, which keeps them consistent with each other. Data-driven specs import cases from '../fixtures/signup-cases.json'; cases.forEach((c) => { it(`rejects ${c.label}`, () => { cy.visit('/signup'); cy.getByTestId('email').type(c.email); cy.getByTestId('submit').click(); cy.getByTestId('form-error').should('have.text', c.expected); }); }); Note the import rather than cy.fixture: the loop has to run while the spec file is being evaluated, before any Cypress command has executed, so the file needs to be available synchronously. Using cy.fixture here silently produces zero tests, which is a confusing hour if you have not hit it before. Seeding a real database For tests that go all the way through, export SQL and run it from a task, since specs cannot touch the filesystem or a database directly: // cypress.config.js setupNodeEvents(on) { on('task', { seedDb() { require('node:child_process').execSync('psql $TEST_DB -f cypress/fixtures/seed.sql'); return null; } }); } // spec beforeEach(() => { cy.task('seedDb'); }); Reseed rather than append. A suite that passes only on a database with three previous runs' worth of rows in it is not testing what you think. Fixtures and encoding Generated data contains non-ASCII names by design, and cy.fixture reads JSON as UTF-8, so those arrive intact. This is worth keeping rather than filtering out — a fixture of ASCII-only names will never tell you that your application mangles Yılmaz somewhere between the API and the DOM. If you also want to check binary handling, that is what the encoding argument on cy.fixture is for. A practice target that does not change If you are learning Cypress or building a training exercise, this site is designed for it: every control carries a stable data-testid that survives deploys. The schema builder covers text inputs, selects, checkboxes, dialogs, drag-and-drop reordering, dynamically added rows, file downloads and a preview table that updates as you type — a fuller surface than most practice applications, and it needs no environment of your own. Common questions Where do generated fixtures go in a Cypress project?Save the JSON export as cypress/fixtures/name.json and load it with cy.fixture('name'), or import it directly when you need the data synchronously. Why does my forEach over cy.fixture produce no tests?cy.fixture is a command that runs after the spec file has been evaluated, so the loop body never registers any tests. Import the JSON file directly at the top of the spec instead. How do I test error and empty states?Pair the fixture with cy.intercept — statusCode: 500 for errors, body: [] for empty, forceNetworkError for connection failures, and req.reply with a delay for slow responses. How do I seed a real database from Cypress?Specs cannot reach the filesystem, so register a task in setupNodeEvents that runs the generated SQL, then call cy.task from beforeEach. Reseed each run rather than appending. Can I practise Cypress against this site?Yes. Every interactive control has a stable data-testid, covering forms, dialogs, drag-and-drop, downloads and dynamically rendered rows. Related Playwright test data — the same patterns with page.route. QA test data guide — data classes worth covering. Sample JSON data — typed fixture output. Mock API data — response states and envelopes. Last updated 6 September 2026 ============================================================================ # Database Test Data Generator & Seeding Guide — Fun Data URL: https://fundata.dev/database-test-data ============================================================================ Database test data generator and seeding guide Create realistic rows for PostgreSQL, MySQL or SQLite without copying production records into development and CI. Generate SQL data →SQL format details Start from table constraints Translate each required column into a generator field: Row Number or UUID for primary keys, realistic person and location fields for customers, prices and currencies for orders, and controlled custom lists for status columns. Match minimums, maximums and date ranges to the application rules instead of generating arbitrary values. Make fixtures repeatable Use a named seed such as checkout-schema-v3 and export the schema alongside the data. The same seed and schema produce identical rows on every machine, which makes failed tests reproducible and keeps screenshots and demos stable. Test nullability and uniqueness Set Blank % on optional fields to produce NULL in SQL or empty cells in CSV. Enable Unique on collision-sensitive fields such as usernames and external IDs, but keep the requested row count within the field's possible value space. Explicitly add boundary rows for constraints that random generation cannot guarantee. Choose SQL or CSV SQL inserts are convenient for small and medium fixtures, source-controlled seed scripts and a database that already matches the inferred types. CSV is faster for bulk loaders such as PostgreSQL COPY and MySQL LOAD DATA, and separates generation from database-specific DDL. Example PostgreSQL workflow # Generate a seeded CSV with a header, then load it psql mydb -c "TRUNCATE customers RESTART IDENTITY" psql mydb -c "\copy customers FROM 'customers.csv' CSV HEADER" Generating related tables Each column is generated independently, so a foreign key will not match a parent row by accident. Building a related set is a two-pass job, and it is simpler than it sounds: Generate the parent table first — say 500 customers with Row Number as the primary key — and export it. Feed those keys back in. Create the child schema with a Custom List field for customer_id, pasting in the parent IDs. Every generated order then references a customer that exists. Weight the list if you want realistic skew. A custom list can repeat values, so listing your busiest customers several times produces the hot-key distribution a uniform draw never will. Load parents before children, or defer constraint checking for the duration of the load: # PostgreSQL — one transaction, constraints checked at COMMIT BEGIN; SET CONSTRAINTS ALL DEFERRED; \copy customers FROM 'customers.csv' CSV HEADER \copy orders FROM 'orders.csv' CSV HEADER COMMIT; Loading 100,000 rows quickly Seeding that feels slow is usually the schema's fault, not the file's. In rough order of impact: Use COPY or LOAD DATA, not INSERT. Bulk loaders bypass per-statement overhead entirely and are commonly an order of magnitude faster. That is the main reason to prefer CSV over SQL at this size. Create indexes after the load. Every index is maintained on every inserted row. Dropping non-essential indexes, loading, then recreating them is usually far quicker overall. Load inside one transaction. Autocommit forces a flush per statement; a single transaction lets the database write once. Reset identity sequences afterwards. Explicit primary keys don't advance a serial sequence, so the next application insert collides. TRUNCATE … RESTART IDENTITY before loading, or setval() after it. Keep synthetic and production data separate Generated values are for testing, not identity, payment or address verification. Use a clearly labeled development database, avoid production credentials and review the privacy and safety methodology before sharing fixtures. Schema checklist Primary and foreign-key strategy is explicit. Required columns never receive blanks. Optional columns receive a realistic null rate. Status values come from the application's accepted set. Dates cover past, present and boundary conditions. The seed and exported schema are versioned with the test. Common questions How do I generate test data that satisfies my foreign keys? Generate the parent table first, then paste its ID column into a Custom List field on the child schema. Every child row then references a parent that exists. Repeating some IDs in that list produces realistic skew rather than a uniform one-to-one spread. How do I seed a PostgreSQL database with test data?Export as SQL and run the file, or export CSV and use \copy from the psql client, which needs no server-side file permissions. The SQL export can include a CREATE TABLE, so an empty database goes from nothing to populated in one file. Will the generated data respect NOT NULL and UNIQUE constraints?Only if you tell it to. Blank % is what emits nulls, so leave it at zero for NOT NULL columns and raise it deliberately for the nullable ones you want to test. For a UNIQUE or primary-key column, enable the Unique toggle rather than relying on collisions being unlikely. How many rows can I generate for a database?Up to 100,000 per export. For a larger table, export several times with a different seed each time and concatenate — the same seed produces the same rows, so repeating a seed gives you one dataset twice rather than a bigger one. Can I generate data that matches an existing table?Yes. Export a few rows of the real table as CSV with the values removed or replaced, upload it, and the builder infers a schema from the header and sample values. Then correct the types it guessed wrong before generating. Last updated 6 September 2026 ============================================================================ # Employee Sample Data — Free HR Test Dataset URL: https://fundata.dev/employee-sample-data ============================================================================ Employee sample data A realistic HR dataset for dashboards, tutorials, pivot tables and database seeding — without lifting a single row from a real payroll system. Generate employee data →Excel & Sheets guide Ready-made files to download Generated from the schemas below with a fixed seed and committed to the site, so a link to one of these files keeps returning the same bytes. They are public domain (CC0) — use them in a tutorial, a test suite or a course without asking. Need different columns, more rows or another format? Build it above. FileRowsColumnsFormatSize employees-100.csv1008CSV9 KB employees-100.json1008JSON26 KB employees-100.sql1008SQL12 KB employees-1000.csv1,0008CSV93 KB employees-1000.json1,0008JSON259 KB employees-1000.sql1,0008SQL118 KB The schema An employee table that actually exercises a dashboard needs a mix of identifiers, categories, dates and numbers. This one takes about a minute to build in the generator: Column Field type Options ------------------------------------------------------------ employee_id Row Number full_name Full Name locale: any email Email Address unique job_title Job Title department Custom List Engineering, Sales, Marketing, Finance, HR, Support, Operations hire_date Date from 2015-01-01, sequential salary Integer min 38000, max 185000 is_active Boolean city City country Country manager_id Integer min 1, max 40 performance Custom List Exceeds, Meets, Developing employee_id,full_name,job_title,department,hire_date,salary,is_active 1,Elena Rossi,QA Engineer,Engineering,2015-02-14,71000,true 2,Marco Schneider,Account Executive,Sales,2015-04-03,64500,true 3,Aisha Yılmaz,Data Analyst,Finance,2015-06-21,79200,false Choices that make the data useful A few decisions separate a dataset that demos well from one that falls apart on the first pivot: Departments as a Custom List, not a random text field. Grouping and filtering only mean something if the same seven values repeat. Repeat a value in the list to weight it — listing Engineering three times gives you a realistically large engineering team instead of seven equal departments. Hire dates sequential. Set the Date field's order to sequential and headcount grows smoothly over time, which is what a hiring chart should look like. A random draw produces a jagged mess. Salary as an Integer with a real range. Anchor the minimum and maximum to your actual bands. If you want a realistic long tail rather than a flat spread, use the Number (Normal Dist.) field instead. Blank % on optional columns. Manager, end date and secondary email should be empty for some rows — the CEO has no manager, and a dashboard that assumes otherwise breaks on row one. Manager hierarchies A manager_id drawn at random points at an arbitrary row, which is fine for a shape test and wrong for an org chart — it will produce cycles, and some employee will manage themselves. For a real hierarchy, generate the managers first as a small export, then feed their IDs into a Custom List on the employee schema so every report points at somebody who exists and is senior. The two-pass technique is the same one used for foreign keys in the database guide. Where this data goes Spreadsheets and BI. Export CSV, or TSV if you want to paste straight into a sheet. Watch employee IDs with leading zeros — see the Excel guide. HR product demos. Generate once with a fixed seed so every screenshot and every demo shows the same people. Database seeding. SQL export gives inserts plus an optional CREATE TABLE; PostgreSQL and MySQL notes cover bulk loading. Training material. A fixed seed means every student works with identical data, so an exercise answer is the same for everyone. Why not anonymised real data Anonymising a payroll export is harder than it looks. Salary, job title, hire date and department together re-identify people in a small company even with names stripped, and an HR dataset is exactly the kind of thing that should never sit in a demo environment or a public repository. Generating from scratch avoids the problem rather than mitigating it — there is nothing to re-identify. The methodology page covers what each field draws from. Common questions Is this real employee data?No. Every value is synthetic — names are recombined from public-domain pools, emails use reserved documentation domains, and salaries are drawn from a range you set. Nothing derives from a real payroll system. How do I get realistic department sizes?Use a Custom List and repeat the values you want more of. Listing Engineering three times and HR once produces roughly a three-to-one split instead of an even spread. Can I build a proper manager hierarchy?Not in a single pass — a random manager_id creates cycles. Generate the managers first, then paste their IDs into a Custom List field on the employee schema. How do I make salaries cluster realistically?Use the Number (Normal Dist.) field rather than Integer. A uniform range gives you as many people at the top of the band as in the middle, which no real organisation looks like. Can I use this dataset in a public tutorial?Yes. The data is synthetic and safe to publish or commit. Use a fixed seed so every reader sees the same rows. Related Sales sample data — orders, revenue and a time series. Excel sample data — pivot tables and clean imports. Random name generator — the people behind the rows. Database test data — seeding and foreign keys. Last updated 6 September 2026 ============================================================================ # Excel Sample Data Generator (CSV & TSV) — Fun Data URL: https://fundata.dev/excel-sample-data ============================================================================ Excel and spreadsheet sample data generator Create realistic tabular data for formulas, pivot tables, dashboards, imports, tutorials and BI prototypes without exposing real customer records. Generate CSV data →Generate TSV data Build a useful sample table Start with a Row Number or UUID, then add dimensions such as customer, country, product and department. Add numeric measures such as quantity and price plus a Date or Datetime column for trends. A controlled Custom List works well for order status, channel or region. CSV or TSV? CSV is the safest download when a workbook, BI tool or import wizard expects a file. Values containing commas, quotes or newlines are escaped automatically. TSV is convenient for copying raw output and pasting directly into Excel or Google Sheets because each tab becomes a new column. Import cleanly Keep the header row enabled. Import as UTF-8 so international names and city values remain intact. Tell the spreadsheet whether the delimiter is a comma or tab, then set date and decimal interpretations explicitly when regional settings differ from the exported representation. Test missing and messy-looking data Apply Blank % to optional columns instead of filling every cell. Use longer names, addresses and product descriptions to expose clipping in reports. Generate several currencies or statuses with Custom List, but keep each column semantically consistent so formulas and charts remain meaningful. Example sales schema order_id Row Number order_date Date (sequential) customer Full Name country Country product Product Name quantity Integer (1–8) unit_price Price (5–500) status Custom List Stop Excel rewriting your data A spreadsheet guesses a type for every cell it imports, and the guesses are occasionally destructive. All three of these are silent — the file was fine, the workbook is not: Leading zeros vanish. A postcode of 01234 becomes the number 1234. Generate a zero-padded ID column and you can prove in one glance whether your import path preserved it. Long numbers become scientific notation. A 16-digit card or account number shows as 1.23457E+15, and the trailing digits are genuinely gone, not merely hidden. Text that resembles a date is converted. The classic case is a gene name like SEPT1 becoming a September date — but any 1-2 or 3/4 style value is at risk. The fix is the same in every case: import rather than open. In Excel use Data → From Text/CSV and set the affected columns to Text in the preview; in Google Sheets use File → Import and turn off Convert text to numbers, dates and formulas. Double-clicking the file gives you no such control. Regional settings change the file's meaning Two things differ by locale and both will quietly corrupt an import: The decimal separator. Much of Europe writes 129,99. Opened under those settings, a comma-delimited file containing 129.99 may split the value across two columns or read it as 12999. TSV removes the delimiter collision entirely, which is the main reason to prefer it here. Date order. 03/04/2025 is 3 April or 4 March depending on where the workbook thinks it is. The date fields default to ISO 8601 (2025-04-03) precisely because it is unambiguous — keep that format through the import and convert once, deliberately, at the end. Sizing a sample for pivots and charts More rows are not better. A sample should be the smallest one that still behaves like the real thing: enough rows per category that a pivot table has something to aggregate — roughly 30 or more — across enough categories to make a chart legible, which usually means five to eight, not fifty. A few hundred to a few thousand rows covers almost every tutorial, dashboard mock-up and formula test. Reserve five-figure exports for testing whether the workbook itself holds up. Spreadsheet checklist Headers are short, unique and formula-friendly. Date and number columns use one consistent format. Blank rates reflect the scenario being demonstrated. A seed keeps tutorial screenshots and expected totals stable. The sample contains enough rows for filters and pivot tables, but not more than the workbook needs. For bulk database imports, continue with the database seeding guide. For how values are generated and kept private, read the methodology. Common questions How do I get sample data into Excel without it mangling the values? Use Data → From Text/CSV rather than double-clicking the file, and set ID and postcode columns to Text in the import preview. Double-clicking hands the file to Excel's own guesser, which strips leading zeros and reads some codes as dates. Should I use CSV or TSV for a spreadsheet?TSV when the data contains commas — free text, addresses, prices written in a locale that uses a comma decimal separator — because a tab is far rarer inside a value than a comma is. CSV otherwise, since more tools accept it without being asked. Why does my CSV open as one column, or split in the wrong places?Because the delimiter the spreadsheet expects depends on its regional settings: in locales where the comma is the decimal separator, Excel expects semicolons and reads a comma-delimited file as a single column. Importing rather than opening lets you state the delimiter instead of guessing at it. How much sample data do I need for a pivot table?A thousand rows is usually enough to make a pivot behave like a real one — several values per category, a date range wide enough to group by month, and numbers with enough spread that a chart is not a straight line. A hundred rows makes every group too small to be interesting. Can I get a real .xlsx file rather than a CSV?Yes. The Excel export writes an actual workbook rather than a renamed CSV, so column types survive the round trip and Excel opens it without an import dialogue at all. Last updated 6 September 2026 ============================================================================ # Fake Address Generator — Random Streets & Postcodes URL: https://fundata.dev/fake-address-generator ============================================================================ Fake address generator Street addresses, cities, states, postcodes, countries and coordinates — generated in bulk and kept consistent within each row, so a city never lands in the wrong country. Generate addresses →All field types Row coherence is the whole trick Most address generators draw each column independently, which produces rows like "Paris, United States, 90210". It looks fine in a database and absurd the moment it reaches a UI, a shipping-label mock or a map pin. Here the location fields agree with each other within a row. Put City, Country and Country Code in one schema and the city genuinely belongs to that country: street_address,city,country,country_code,zip 4821 Juniper Lane,Lyon,France,FR,64183 118 Sycamore Court,Izmir,Türkiye,TR,30947 7302 Rosewood Drive,Osaka,Japan,JP,55210 Note what is not claimed: the postcode is a plausibly shaped number, not a real postcode for that city, and the street exists nowhere. That is deliberate — see below. The fields available Street Address — house number plus a street name and suffix. City, Country, Country Code (ISO) — coherent within the row. State (US) and State Abbrev (US) — for US-shaped schemas. Zip / Postal Code — pattern-driven, default #####. Change the format to match the country you are modelling: ### ##, ?? ##-###, or whatever shape your validator expects. Full Address — the whole thing in one string, for a single-column address field. Latitude and Longitude — six-decimal coordinates. Postcodes are a format test, not a lookup The postcode field generates values matching a pattern you choose, where # is a digit, ? an uppercase letter and ~ a lowercase one. That is the right tool for testing an input mask, a column width or a regex validator — and the wrong tool for testing address verification, because a generated postcode will not resolve to the generated city. If your application calls a real address-verification API, generated addresses will fail that call. That is correct behaviour and worth testing explicitly: the interesting question is what your checkout does when verification fails, and a fixture full of unverifiable addresses answers it. Coordinates Latitude and Longitude are drawn across the full valid ranges (−90 to 90, −180 to 180) at six decimal places, independently of the city column. They are ideal for exercising a map component, a bounding-box query or a distance calculation at volume; they are not the coordinates of the city in the same row. If you need points near a specific place, a Decimal field with a narrow min and max is the more honest tool. Address shapes that break forms Once the bulk list is generated, add the awkward cases as a Custom List. The ones that reliably find bugs: addresses with no house number, addresses where the "street" is a PO box, multi-line addresses, addresses longer than the database column, single-line addresses in countries that put the postcode before the city, and countries with no postal code at all. Set Blank % on the state field to check that a form which requires "state" does something sensible for the many countries that do not have one. Exporting Address columns export to every format. CSV is the usual choice for bulk-loading a customers table — note that Full Address contains commas and is therefore quoted, which makes it a good smoke test of your CSV parser. JSON suits a nested shipping-address object via dot-notation field names, and SQL gives you a seed script directly. Common questions Are these real addresses?No. Street names and house numbers are synthetic and do not correspond to real buildings. City and country names are real places and stay consistent within a row, but the full address is fictional by design. Will the postcodes validate?They match the pattern you configure, not a real postal database. They are built for testing input masks, column widths and format validators; they will not pass a real address-verification service. Do the city and country match?Yes. City, Country and Country Code are coherent within each row, so you never get a city placed in the wrong country. Latitude and longitude are independent and are not the coordinates of that city. Can I generate addresses for one country only?The location fields draw from a mixed international pool. To constrain to one country, use a Custom List field for the city and set the postcode pattern to that country's format. How do I get a multi-line address?Use separate Street Address, City, State, Zip and Country fields and join them in your application, or use the single-string Full Address field if one column is enough. Related generators Random name generator — the people these addresses belong to. Random phone number generator — national number formats to match. Sales sample data — orders with shipping addresses attached. Location field reference — every location field and its options. Last updated 6 September 2026 ============================================================================ # Test Credit Card Numbers — Safe Fake Card Data URL: https://fundata.dev/fake-credit-card-numbers ============================================================================ Test credit card numbers The published payment-gateway test card numbers, generated as a column alongside card type, expiry and currency — so a checkout fixture is one export rather than a copy-paste job. Generate card data →All field types What the field emits The Credit Card # field draws from the four canonical test numbers that payment gateways publish for exactly this purpose: 4111111111111111 Visa 5555555555554444 Mastercard 378282246310005 American Express 6011111111111117 Discover These are not randomly generated numbers that happen to satisfy the Luhn check — they are the specific values Stripe, Braintree, Adyen and others document as test cards. Every payment sandbox recognises them; no real account exists behind any of them. That is a deliberate limitation. A generator that produced arbitrary Luhn-valid numbers in real issuer ranges would be producing numbers that could belong to somebody, and there is no legitimate testing reason to want that. Four values is enough to exercise a card-type detector, a form validator and a checkout flow. Building a checkout fixture Card number alone is rarely the whole test. A useful payment fixture pairs it with: Credit Card Type — the brand label, for checking your detection logic agrees with the number. Date with a future range and MM/yyyy-style handling — expiry. Pattern ### — CVV, or #### for Amex. Price and Currency Code — the amount being charged. Full Name — cardholder, which should not match the account name in every row if you want to test that path. Export the lot as JSON for a mock payment endpoint or CSV for a data-driven checkout test. Declines are the interesting half A checkout that works with a successful card is the easy case. What breaks in production is the decline path: insufficient funds, expired card, incorrect CVV, 3-D Secure challenge, gateway timeout. Gateways publish specific card numbers that trigger each of those, and they differ between providers. Those numbers are not built into the generator, because they are provider-specific and change. Add them as a Custom List field, taken from your gateway's own test-card documentation, and you get a fixture that walks every branch of your payment code rather than the happy one. Weight the list by repeating the values you want more of. Validation edge cases For testing the card form rather than the payment, add a Custom List with the shapes that break input handling: a number with spaces every four digits, a number with hyphens, a 15-digit Amex where the form expects 16, a number one digit short, and a number that fails the Luhn check. Set Blank % on the CVV field to check required-field behaviour. What this is not for These numbers work in sandbox environments. They are rejected by live payment processing, which is the correct outcome and not a bug to work around. Nothing here generates a number that would function as a real payment instrument, and no request to do so would be reasonable — the value of a test card is precisely that it is known, published and inert. The same principle runs through the rest of the data: IBANs are the right length but fail their checksum, emails land on reserved domains, and IP addresses use documentation ranges. The methodology page lays out each one. Common questions Are these real credit card numbers?No. They are the four test card numbers that payment gateways publish for sandbox testing — Visa 4111111111111111, Mastercard 5555555555554444, Amex 378282246310005 and Discover 6011111111111117. No real account exists behind any of them. Will they work in a live payment system?No, and that is the point. They are recognised by gateway sandboxes and rejected by live processing. Can I generate random Luhn-valid card numbers instead?The generator deliberately does not do this. Arbitrary numbers in real issuer ranges could correspond to somebody's card, and the published test numbers cover every legitimate testing need. How do I test declined payments?Use a Custom List field populated with the decline-trigger numbers from your own gateway's test documentation. Those values are provider-specific, so they are not built in. Do you generate CVV and expiry dates?Use a Pattern field with ### (or #### for Amex) for CVV, and a Date field with a future range for expiry. Combine them with the card number field in one schema. Related generators Fake IBAN generator — bank-transfer test data with the same safety design. Sales sample data — orders, prices and currencies. Mock API data guide — building a fake payment endpoint. Methodology — what every field draws from and why. Last updated 6 September 2026 ============================================================================ # Fake Email Generator — Safe Test Addresses in Bulk URL: https://fundata.dev/fake-email-generator ============================================================================ Fake email generator Bulk test email addresses on reserved documentation domains — deliverable to nobody, coherent with the row's name, and identical every time you use the same seed. Generate emails →All field types Why the domain matters more than the address The dangerous part of a seeded user table is not the fake name — it is the domain. Seed ten thousand users on @gmail.com with invented local parts and eventually something sends. A misconfigured staging job, a forgotten cron, a marketing tool pointed at the wrong database: now you are mailing strangers, and a few of those addresses turn out to belong to real people. Every address this generator produces lands on a domain that is reserved by standards bodies precisely so it cannot: example.com example.org example.net (RFC 2606) example.test example.invalid (RFC 2606 / RFC 6761) These domains have no MX records and are not available for registration. Mail to them fails immediately rather than reaching an inbox. That is the whole point: a fixture that leaks is a bug, not a catastrophe. What the addresses look like The local part is derived from the same row's name rather than drawn at random, so the table reads like a real one: full_name,email Elena Rossi,elena.rossi7@example.org Marco Schneider,marco_schneider@example.com Aisha Yılmaz,aisha.yilmaz@example.net Non-ASCII characters are transliterated for the address (Yılmaz becomes yilmaz) while the name column keeps its original spelling — which is what most real systems do, and a useful thing to have in a fixture if you are testing that behaviour. Separators vary between a dot, an underscore and nothing, and roughly two in five addresses carry a numeric suffix, so you get the mix of shapes a real signup table has. Uniqueness Email is the classic column with a UNIQUE constraint, and random generation does not guarantee uniqueness on its own — with a few thousand rows drawn from a finite name pool, collisions are likely rather than possible. Enable the Unique toggle on the field and the generator enforces distinct values, which is what you want before a bulk load into a table that will reject duplicates. Keep the row count sensible relative to the pool. Asking for more unique values than the field can produce is the one way to make this fail, and it fails loudly rather than silently emitting duplicates. Testing the unhappy paths A validation test needs addresses that are wrong in specific ways, and those are better written by hand than generated. Add a Custom List field with the cases you actually care about — a missing @, a leading dot, consecutive dots, a trailing hyphen in the domain, an address at the length limit, an address with a plus tag. Then set Blank % on the email field to produce empty values, and check that the required-field path behaves. Plus-addressing deserves its own test. elena.rossi+staging@example.org is a valid address that a surprising number of validators reject, and it is the one users notice. Exporting Combine the email field with names, addresses and a Row Number primary key, then export as SQL inserts for a seed script, CSV for a bulk loader, or JSON for a mock API. The database seeding guide covers loading a user table end to end. Common questions Can these addresses receive email?No. Every generated address uses a domain reserved by RFC 2606 or RFC 6761 (example.com, example.org, example.net, example.test, example.invalid). These domains have no mail servers and cannot be registered, so mail to them fails instead of reaching a real person. Are the addresses unique?Only if you ask for it. Enable the Unique toggle on the field and the generator enforces distinct values, which is what a column with a UNIQUE constraint needs. Without it, repeats are expected at higher row counts. Do the emails match the names in the same row?Yes. The local part is built from that row's First and Last Name, transliterated to ASCII, so the table stays coherent when a human reads it. Can I use a different domain?The built-in field is deliberately limited to reserved domains. If you need your own domain, use a Formula or Pattern field to build the address, and make sure the domain you pick is one you control. Is this a disposable or temporary email service?No. This generates addresses for populating test databases and fixtures; it does not create inboxes and cannot receive messages. It is the opposite of a temp-mail service. Related generators Random name generator — the names these addresses are derived from. UUID generator — stable primary keys for the same user table. Sample JSON data — ready-made user fixtures using these fields. QA test data guide — building fixtures that fail for the right reasons. Last updated 6 September 2026 ============================================================================ # Fake IBAN Generator — Test Bank Account Numbers URL: https://fundata.dev/fake-iban-generator ============================================================================ Fake IBAN generator IBAN-shaped values with the correct length and grouping for seven countries — enough to test a form, a column width and a display format, and deliberately not enough to move money. Generate IBANs →All field types What it generates The IBAN field takes a country option and emits a value of the correct total length for that country, grouped in fours the way IBANs are conventionally displayed: DE 22 chars DE84 9137 2056 4180 3925 71 GB 22 chars GB19 4820 7315 9064 2837 15 FR 27 chars FR76 3018 4927 5061 8340 9271 583 TR 26 chars TR42 0619 3748 2059 1637 4820 NL 18 chars NL63 8241 5907 3618 42 ES 24 chars ES91 7204 8316 5029 4718 36 IT 27 chars IT38 5019 2746 3085 1927 4630 517 The country prefix is correct, the length is correct, and the grouping matches how banks print them. That covers the majority of what an IBAN column is used for in a test: does the input accept 34 characters, does the column not truncate, does the UI group the digits, does the layout survive France and Italy as well as the Netherlands. They will not pass a checksum This needs stating plainly, because it is the one thing that surprises people. A real IBAN carries two check digits in positions 3 and 4, computed over the rest of the value with a mod-97 algorithm. The generated values have random digits in those positions, so a validator that implements the ISO 13616 check will reject essentially all of them. That is intentional. An IBAN that passes mod-97 and uses a real bank code is a plausible account identifier, and generating those in bulk is not something a test-data tool should do. If your form validates the checksum, generated IBANs will fail — which is a genuine test of your error path, and a signal to use a small hand-written list of known-valid test IBANs (banks and payment providers publish them) for the happy path. The practical split: use generated IBANs for volume, layout, storage and rejection testing; use a published test IBAN when you specifically need one that validates. Storing and displaying IBANs are up to 34 characters, alphanumeric, and conventionally stored without spaces and displayed with them. The generator emits the spaced form, which means it is directly useful for testing display and for checking that your input handling strips whitespace before comparison. If you need the compact storage form, remove spaces downstream — a good reminder to test that two IBANs differing only in spacing are treated as equal. Do not store an IBAN as a number. The leading country letters make that impossible anyway, but the same instinct that turns a phone number into an integer will happily strip a leading zero from a bank code. Building a payments fixture An IBAN on its own is rarely the whole record. Pair it with Full Name for the account holder, Company Name for business accounts, Currency Code, Price for the amount and a Date for the value date. Set Blank % on optional reference fields, and enable Unique on the IBAN column if it backs a unique constraint. For card payments rather than transfers, the test card number field follows the same safety principle from the other direction: published, inert values rather than plausible ones. Common questions Are these valid IBANs?No. They have the correct country prefix, total length and grouping, but the check digits are random, so any validator implementing the ISO 13616 mod-97 check will reject them. That is deliberate. Which countries are supported?Germany (DE), the United Kingdom (GB), France (FR), Türkiye (TR), the Netherlands (NL), Spain (ES) and Italy (IT), each at its correct official length. Can I get IBANs that pass validation?Not from the generator. If you need a checksum-valid value for a happy-path test, use one of the test IBANs published by banks and payment providers, added as a Custom List field. Can money be sent to these?No. They do not correspond to real accounts and will fail validation before any transfer is attempted. Should I store the spaces?Generally no — store the compact form and add grouping for display. The generator emits the spaced form, which makes it useful for testing that your input handling normalises whitespace. Related generators Test credit card numbers — the card-payment equivalent. Random name generator — account holders. Business field reference — prices, currencies, companies, barcodes. Methodology — the safety design behind every field. Last updated 6 September 2026 ============================================================================ # Faker.js Alternative — Test Data Without Writing Code URL: https://fundata.dev/faker-js-alternative ============================================================================ Home / Faker.js alternative A Faker.js alternative that needs no code Faker is a library you call from code. This is a page you open. Both produce realistic synthetic data, and the difference decides which one fits your problem. Library or file: the difference that decides it Faker generates at runtime, inside your program. You install it, import it, and call faker.person.fullName() where you need a value. That is exactly right when the data is consumed by the code that generates it — a unit test that wants a fresh user each run, a seed script that fills a local database, a demo server making up rows on request. This generates a file, once. You describe columns, press download, and get CSV, JSON, SQL, XML or a spreadsheet you can commit, attach, email or paste. That is right when the data is consumed by something that is not your JavaScript — a database import, a spreadsheet, a QA colleague, a bug report, a tutorial, a language with no faker binding you want to depend on. Neither is a substitute for the other, and plenty of projects want both: a file for the fixtures that are checked in, a library for the values a test invents as it runs. The reproducibility difference is smaller than it looks Faker supports seeding — faker.seed(123) — so "reproducible" is not a distinguishing claim. What differs is what the seed is attached to. With a library, the seed lives in code and the data is reconstructed on every run, so a faker version bump can change the values under a passing test. With a generated file, the bytes are the artefact: the file in your repository is the same file next year regardless of what any dependency did. The methodology page covers how the seeding here works. Side by side How a downloaded file and a runtime library differ Fun Data PlaygroundFaker.js Where generation happensIn your browser, on this pageIn your program, at run time What you end up withA file — CSV, TSV, JSON, NDJSON, SQL, XML or .xlsxValues returned by a function call SetupOpen the pageAdd a package to a JavaScript project What the seed pinsThe bytes of a file you commitOutput rebuilt on every run from code Custom generation logicThe field types and options the builder offersAny code you can write Projects that are not JavaScriptA file imports anywhereNeeds a JavaScript runtime Generating inside CINo API to call — commit the file insteadRuns in the pipeline like any dependency Structural differences only — how each tool is used, not what it currently offers. Check the other tool's own documentation before deciding. Where Faker.js is the better answer Being straightforward about this is more useful than a feature table. Reach for the library when you need: Fresh data on every test run, generated in-process, without a file in the way. Generation logic you write yourself — a value derived from three others, a conditional, a loop that stops when a total is reached. Locale coverage past what a UI can present. A library can carry dozens of locales because nobody has to browse them in a dropdown. A build step that produces data. If data generation belongs in CI, it belongs in code. Anything running server-side at request time. This tool has no API to call; it is a page. Tools change. This page sticks to structural differences — how each one is used rather than what it currently offers — but check the current documentation of whichever you are comparing rather than trusting any comparison page, including this one. Where a generated file wins You are not in a JavaScript project. A CSV imports into anything; a JS library does not. The consumer is a human. Nobody attaches a faker call to a bug report. The fixture should be reviewable. A committed file shows up in a diff; a generator call does not show what it produced. You want it now. No install, no project, no dependency added to something you were not planning to change. You need a spreadsheet. Excel and Sheets take a file, and a real .xlsx keeps its column types where a renamed CSV does not. Using both: generate the fixture, type it from the same schema The two combine better than they compete. Generate a fixture file here, commit it, and take the schema straight to a type declaration so the code consuming it is typed from the same source — a TypeScript interface, a Zod schema or a Pydantic model. Then keep faker for the values an individual test invents. The builder can also emit the fixture as a JavaScript module with a link that reproduces it, which is usually the fastest way to move a shape you designed here into a project that already uses a library. Common questions Is this a drop-in replacement for Faker.js?No, and it is not trying to be. Faker generates values inside your program at runtime; this produces a file you download. If your data is consumed by the same JavaScript that generates it, the library is the right tool. Can I get reproducible data like faker.seed()?Yes — enter any string as a seed and the same schema produces byte-identical output every time. The practical difference is that a generated file is fixed bytes in your repository, while seeded library output is reconstructed on each run and can shift when the library version does. Does it support as many locales as Faker?No. A library can carry dozens of locales because nobody has to browse them; here names come in eight locales and other fields are pattern-driven. If broad locale coverage is the requirement, that is a reason to use the library. Can I call this from CI?Not directly — there is no generation API, because everything runs in the browser. For CI, either commit a generated file as a fixture or use a library in the pipeline. Which formats can I get that a library does not give me directly?CSV, TSV, SQL INSERT statements with a CREATE TABLE, XML, NDJSON and a real .xlsx workbook. All of those are possible from code too, of course — they just are not one click. Related /mockaroo-alternative — the same comparison against a hosted generator rather than a library. /qa-test-data — fixtures for automated tests, and when to commit them. /json-to-typescript — typing the fixture from the data itself. /playwright-test-data — committed fixtures versus generated ones in an E2E suite. /generatedata-alternative — and against an application you host yourself. Last updated 6 September 2026 ============================================================================ # generatedata.com Alternative — No Install, No Server URL: https://fundata.dev/generatedata-alternative ============================================================================ Home / generatedata alternative A generatedata alternative with nothing to install generatedata is an open-source application you can host yourself. This is a static page that generates in your browser. The choice is mostly about where the code runs. Self-hosted application versus a static page generatedata's defining property is that you can run your own copy. That is genuinely valuable: an instance inside your network is auditable, modifiable and yours, which is the answer when policy says data tooling cannot be a third-party website. The property here is that there is nothing to run. The generator is client-side JavaScript on a static page, so the data is produced on your machine whether or not you trust the site — there is no server that could receive a schema, because generation never leaves the tab. It is the same privacy goal reached from the other end: instead of controlling the server, there isn't one. Which argument wins depends on what your policy is actually about. "Our data must not reach a third party" is satisfied by both. "All tooling must run on infrastructure we control" is satisfied only by self-hosting. "We need this working in ten minutes" is satisfied only by the page. Side by side Where the code runs, and who has to run it Fun Data Playgroundgeneratedata Where generation happensYour browser, on a static pageA copy of the application you host SetupOpen the pageInstall the application and its database Who operates the infrastructureNobody has to — there is no serverYou do Modifying the generatorNot possible; it is a pageOpen source, so anything Working fully offlineAfter the first visit, from the service worker cacheYes, once installed on your network Server-side or scripted generationNot availableAvailable Data leaving your machineIt cannot — generation never leaves the tabIt does not, because the server is yours Structural differences only — how each tool is used, not what it currently offers. Check the other tool's own documentation before deciding. Where self-hosting is the better answer Air-gapped or restricted networks, where the browser cannot reach an external site at all. You need to modify the generator — add a data type specific to your domain, or swap the reference data for your own. Compliance requires infrastructure you control, independently of where computation happens. You want generation on a server, driven by a script rather than a person. Tools change. This page sticks to structural differences — how each one is used rather than what it currently offers — but check the current documentation of whichever you are comparing rather than trusting any comparison page, including this one. Where no install wins Nothing to maintain. A self-hosted instance is a thing to update, patch and eventually migrate. It works offline anyway. After the first visit the page is cached by a service worker, so a flaky connection is not a blocker. Shareable schemas. A schema encodes into the URL, so sending a colleague the exact setup is a link rather than an export and an import. Nothing to explain to anyone. No hostname to justify, no service to add to a diagram. What transfers directly The vocabularies are close: named columns, a data type per column, options per type, a row count and a format. Rebuilding a schema is a few minutes of picking types. Two things shorten it further — the builder can infer a schema from a CSV sample, so exporting a small file from your existing instance and uploading it reproduces the column names and guessed types in one step; and the templates cover the usual shapes (users, orders, employees, sensor readings, transactions) if you were using something similar. What you give up Everything runs in one browser tab, which sets real limits: 100,000 rows per export rather than millions, no server-side generation, and no multi-table relational output resolved in one pass. That last one is a two-pass job here — generate parents, feed their IDs into a Custom List for the children — which the database guide walks through. Common questions Do I need to install or host anything?No. It is a static page; the generator is client-side JavaScript. That is also why your schema and generated rows never reach a server — there is no server to reach. Is this open source?The generated data is public domain and the site is inspectable in your browser like any static page, but it is not currently a self-hostable distribution. If running your own copy is the requirement, that is a reason to choose a self-hosted tool. Can I use it offline?Yes. After the first visit a service worker caches the page, so it keeps working without a connection. How do I move an existing schema over?Export a small sample as CSV from your current tool and upload it — the builder infers column names and types from it, which is usually faster than rebuilding by hand. Review the guessed types afterwards. What are the limits compared to a self-hosted instance?Everything runs in one browser tab: 100,000 rows per export, no server-side generation, and no single-pass multi-table output with foreign keys resolved for you. Related /mockaroo-alternative — the comparison against a hosted service. /faker-js-alternative — the comparison against a code library. /database-test-data — multi-table data, and the two-pass approach to foreign keys. /methodology — exactly what runs where, and what the data is made of. /randomuser-api-alternative — and against fetching records from a service at run time. /json-generator-alternative — and against a template language for JSON. Last updated 6 September 2026 ============================================================================ # How to Generate Test Data — Fun Data Playground URL: https://fundata.dev/guide ============================================================================ Getting started Three steps from empty page to a realistic dataset — plus a few tricks (seeds, blank percentages, patterns) that make the data genuinely useful for testing. 1 · Design a schema The schema builder is a list of fields. Each field has a name (which becomes the JSON key, CSV column or SQL column), a type from the field type reference, per-type options, and a Blank % that injects nulls at that rate — perfect for testing how your app handles missing data. Not sure where to start? Use Load a template… for ready-made schemas: users, e-commerce orders, employees, IoT sensor readings or bank transactions. Your schema is auto-saved to localStorage, so it survives a refresh. 2 · Configure rows, format and seed Pick 1 to 100,000 rows and one of six formats. A quick cheat sheet: CSV / TSV — spreadsheets, BI tools, COPY imports. Optional header row. JSON — a pretty-printed array; drop it into a mock API or fixture file. Field names containing dots nest: address.city becomes {"address":{"city":…}}. NDJSON — one object per line (dot-nesting applies here too); streams nicely into jq, Elasticsearch or BigQuery. SQL — INSERT statements with an optional CREATE TABLE whose column types are inferred from your fields. Pick a dialect (PostgreSQL double quotes or MySQL backticks) and optionally batch 250 rows per INSERT for much faster imports. XML — … for legacy integrations. The Seed field is the power feature: any string (a ticket number, a build id) makes generation deterministic. Same seed + same schema = byte-identical output, today and in six months. Leave it empty for fresh random data on every run. 3 · Export Download ↓ saves a file named like fundata_1000_rows.csv; Copy data puts the full output on your clipboard; Copy JS fixture copies the rows as a paste-ready export const rows = […] module with a link that reproduces the exact dataset; and Copy schema link encodes your entire schema into a URL you can send to a teammate — opening it recreates your exact setup. To keep the schema itself rather than a sample of data, use Export schema ↓ to save it as a JSON file (and Import schema ↑ to load one back — handy for checking a fixture definition into a repo). Or save it under a name in My saved schemas to switch between several schemas in this browser without losing any of them. Already have real-shaped data? Import CSV sample reads a CSV/TSV file and infers a schema from it — column types, numeric ranges, date ranges, small value lists and blank rates are guessed from the actual values, ready to tweak. Optional account Everything above works with no account at all — schemas, datasets and history stay in this browser's localStorage. Sign in with an email and password (top-right) to sync all of it to your account instead, so it follows you to another device: My saved schemas and My datasets — reusable value lists for the My Dataset field type, one value per line. Recent generations — the last 20 downloads/copies, restorable with one click. Settings — rows, format, seed, table name and theme. Cloud share links — a short, revocable /#g=… URL for a schema, listed under “More schema actions” so you can pull it back at any time. Recipes Seed a Postgres table -- 1. Choose format: SQL, table name: customers, "Create table" checked -- 2. Download and run: psql -d mydb -f fundata_1000_rows.sql Data-driven tests (Playwright) // Export JSON with seed "sprint-42" so every CI run uses identical fixtures import users from './fixtures/fundata_100_rows.json'; for (const user of users.slice(0, 10)) { test(`signup works for ${user.email}`, async ({ page }) => { await page.goto('/signup'); await page.fill('#email', user.email); await page.fill('#first-name', user.first_name); // … }); } Stream NDJSON into jq jq -s 'group_by(.country) | map({country: .[0].country, users: length})' \ fundata_10000_rows.ndjson Good to know Privacy: generation is 100% client-side — the rows themselves are never uploaded anywhere. Your schema stays in this browser unless you sign in, which is entirely optional. Sensitive-looking fields are fake: credit card numbers use official payment-gateway test PANs and IBANs are country-length-shaped but not bank-valid. Emails are safe: generated addresses use reserved documentation domains like example.com, so accidental sends can't reach real people. Selectors for automation: every control carries a stable data-testid — this site doubles as a practice target, just like its siblings funui.dev and funapi.dev. Works offline: after your first visit the generator is cached by a service worker, so schema building and exports keep working without a connection (account sync naturally needs one). Next, choose a complete pattern from the test-data use cases, or review how synthetic values are produced in the privacy and safety methodology. Common questions How do I generate test data with this tool? Add fields to the schema, choose a type for each one, set the row count and format, then press generate and download. Nothing else is required — there is no account, and the rows are produced in your browser rather than fetched. What does the seed do?It fixes the output. The same seed and the same schema produce byte-identical data every time, on any machine, so a fixture can be regenerated instead of stored and two people can look at the same row. Leave it empty and every generation is different. How do I generate nested JSON?Name the field with dots. A field called user.email becomes an email key inside a user object, and nesting goes as deep as the dots do. What it will not build is an array of nested objects of varying length inside each record. How do I produce empty or null values?Set Blank % on the field. That percentage of rows get an empty value — null in JSON, NULL in SQL, an empty cell in CSV — which is how you test the paths that only run when data is missing. Do I need an account?No. Everything works signed out, and the schema is saved in your browser. An account only syncs saved schemas, datasets, history and settings between your own devices, and lets you create revocable share links. How many rows can I export at once?Up to 100,000. Generation and the download both happen on your machine, so the practical limit is your own memory rather than a server allowance. Last updated 6 September 2026 ============================================================================ # Test Data Generator (CSV, JSON, SQL) — Fun Data Playground URL: https://fundata.dev/ ============================================================================ JavaScript is required to build schemas and generate data. Enable JavaScript, then reload this page. Realistic test data generator — CSV, JSON & SQL Design a schema and export up to 100,000 realistic rows. Private and reproducible — directly in your browser. Choose from 68 field types and export CSV, JSON, SQL, XML or NDJSON. No signup required; generated rows never leave your machine. CSVJSONSQL XMLNDJSON seeded & reproducible100k rows client-side only Generated rows stay on this device. Aggregate visit analytics are separate and never include generated data. Privacy details Start generating data 1 · Design your schema Define each column, choose how its values are generated, then fine-tune the options. Users template 0 fields Schemas & files Named schemas Save My saved schemas… Current schema changes are saved automatically in this browser. Signed in — named schemas, datasets, history and settings sync to your account on every device. Step 1 of 2 · Schema — continue to export Quick start templates Load a ready-made schema Choose a starting point, then adjust any field. Field nameData type Settings + Add field Browse field types Load a template… My datasets Reusable value lists for the My Dataset field type — one value per line. Sign in to sync them across devices. Save My datasets… More schema actions Export schema Import definition Import CSV sample Undo Redo Reset to default My cloud share links (revocable): Continue to generate 2 · Generate & export Rows (max 100,000) 100 1k 10k 100k Format JSON NDJSON CSV TSV SQL XML Seed (optional) Table name Dialect PostgreSQL / standard MySQL / MariaDB Create table Multi-row INSERT Header row Download JSON · 100 rows Copy 100 rows Regenerate dataset More export actions Download .xlsx Copy JS fixture Copy schema as Copy Copy schema link Copy cloud link Recent generations Table Raw output Expand preview ← scroll sideways for more columns → ← scroll sideways for more columns → Why Fun Data Playground? Fun Data Playground is a free synthetic test data generator that runs entirely in the browser. You describe a table as a list of fields — each one drawn from 68 field types such as names, emails, addresses, dates, UUIDs and prices — and it produces up to 100,000 rows of fictional but realistic data, downloadable as CSV, TSV, JSON, NDJSON, SQL or XML. There is no account and no upload step: the rows are computed on your own machine and never sent anywhere. Give it a seed and the same schema returns byte-identical data every time, which is what makes the output usable as a committed test fixture rather than a one-off sample. 68 realistic field types Names, emails, phone numbers, street addresses, companies, products, prices, IPs, UUIDs, dates and more — organized into groups. See the full field type reference. Six export formats CSV and TSV for spreadsheets, JSON and NDJSON for APIs and pipelines, SQL inserts (with CREATE TABLE) for databases, and XML for legacy systems. Reproducible with a seed Type any seed string and every run produces byte-identical data. Share the seed with your team or pin it in CI for stable, repeatable test fixtures. Private by construction Generation is 100% client-side JavaScript — generated rows never touch a server. Your schema lives in your browser's localStorage, or optionally syncs to your account if you sign in. Nullable columns & patterns Give any field a blank percentage to simulate missing data, or build custom formats with pattern fields (# digit, ? A–Z, ~ a–z) and custom value lists. Templates to start fast One click loads ready-made schemas: users, e-commerce orders, employees, IoT sensor readings or bank transactions — then tweak from there. Built for real test workflows Use practical patterns for database seeding, mock APIs, QA automation and spreadsheet samples. Documented and test-safe Reserved domains and network ranges keep synthetic values away from real recipients. Read the generation, privacy and safety methodology. How it works Design a schema. Add fields, name them, pick a type and fine-tune options like min/max, date ranges or custom value lists. Choose rows, format and seed. Anything from 1 to 100,000 rows; leave the seed empty for fresh random data or set one for reproducible output. Download or copy. Preview updates live as you edit. Export the full dataset as a file or copy it straight to the clipboard — or share your whole schema as a link. Need a mock backend to serve this data? That's what our sibling Fun API Playground is for. Practicing UI automation instead? Head to Fun UI Playground. Or choose a complete workflow from our synthetic test-data use cases. Popular generators and recipes Every field lives in the builder above, but some come with a page of their own explaining what they produce, where the output is safe to use and what it deliberately does not do. By field Random names · fake emails · UUIDs · addresses · phone numbers · dates · test card numbers · IBANs Ready-made datasets Sample CSV files · sample JSON data · employee data · sales & orders data For your stack PostgreSQL · MySQL · MongoDB · Playwright · Cypress · Excel & Sheets FAQ Where does my data go? The generator is 100% client-side, so generated rows never leave your machine. Your schema is saved to your own browser's localStorage by default; signing in (optional) syncs your schemas, datasets, history and settings to your account instead. Can I reproduce the exact same dataset twice? Yes. Enter any string in the Seed field — every run with the same seed and schema produces byte-identical output. Great for repeatable fixtures in CI. Which export formats are supported? CSV, TSV, JSON (array), NDJSON (one object per line), SQL INSERT statements with optional CREATE TABLE, and XML. How many rows can I generate? Up to 100,000 rows per download. Because everything runs locally, large exports depend only on your machine — there is no server-side cap below that. Are the credit card numbers and IBANs real? No. Card numbers are official payment-gateway test PANs, and IBANs are country-length-shaped but fake — they're for form validation and UI testing only. Last updated 6 September 2026 ============================================================================ # JSON Generator Alternative — Schema Builder, Not a Template URL: https://fundata.dev/json-generator-alternative ============================================================================ Home / JSON generator alternative A JSON generator without a template language Template-based generators ask you to learn a small language. This asks you to name your fields and pick their types. Each approach buys something the other does not. Template language versus typed fields A template generator hands you the whole JSON document and a placeholder syntax to fill it: you write the structure literally, with expressions where the values go. The structure is therefore anything you can type — arrays of arrays, objects whose keys differ per branch, values computed from a loop index. A schema builder inverts that. You declare typed fields and the tool assembles records from them. You give up arbitrary structure and get three things back: the shape is data rather than a string, so it can be shared as a link, saved, diffed and re-exported in six other formats; every field carries a real type, so the same schema can emit a TypeScript interface or a CREATE TABLE; and there is nothing to learn before the first row appears. Nesting without a template The one structural thing most people actually need from a template is nesting, and that does not require one. A field named with dots becomes a nested object: Fields: id, user.name, user.email, address.city { "id": 1, "user": { "name": "Ada", "email": "ada@example.test" }, "address": { "city": "Kyoto" } } Nesting goes as deep as the dots do. What it will not produce is an array of nested objects of varying length inside each record — that is where a template is genuinely the better tool. Side by side Describing a shape as fields, or as a template Fun Data Playgrounda template-based JSON generator How you describe the shapeTyped fields in a builderA JSON document with placeholder expressions What you have to learn firstNothingA small template syntax What the shape itself isData — shareable as a link, saved, diffedA string you author Nested objectsDot-notation field names (user.email)Written out literally Arrays of varying length inside a recordNot supportedSupported Other outputs from the same shapeCSV, TSV, SQL, XML, TypeScript, Zod, PydanticUsually JSON only Structural differences only — how each tool is used, not what it currently offers. Check the other tool's own documentation before deciding. Where a template generator is the better answer Arrays inside records — an order with a variable number of line items, a post with a list of comments. Structure that varies between records, where different branches have different keys. Values computed across the document, rather than per field. A document you already have that you want to keep verbatim and only substitute values into. Tools change. This page sticks to structural differences — how each one is used rather than what it currently offers — but check the current documentation of whichever you are comparing rather than trusting any comparison page, including this one. Where a schema wins Six formats from one definition. The same schema exports JSON, NDJSON, CSV, TSV, SQL, XML and a real spreadsheet — a template describes one document shape. The schema is shareable and reviewable. It encodes into a link, so a colleague opens the exact setup rather than pasting a template. Typed fields produce typed output. Numbers are numbers and booleans are booleans in JSON, and the same field types produce a matching SQL column type or code declaration. Nothing to learn. The failure mode of a template DSL is a syntax error in a language you use twice a year. Typed values, not stringly-typed ones A common frustration with template output is that everything arrives as a string, because the template is text and the substitution is textual. Here the field type decides the JSON type: an Integer field emits 42, a Boolean emits true, and a field with a blank percentage emits null rather than "". That matters the moment the fixture meets a validator — which is exactly what a generated Zod schema or JSON Schema will do to it. Common questions Can it produce nested JSON?Yes. A field named with dots becomes a nested object — user.name and user.email produce a user object with two keys — and nesting goes as deep as the dots do. Can it produce an array inside each record?No. A variable-length array of nested objects per record is the case where a template generator is genuinely the better tool. Are numbers and booleans real JSON types?Yes. The field type decides the JSON type, so an Integer field emits 42 rather than "42", a Boolean emits true, and a field with a blank percentage emits null rather than an empty string. Do I have to learn a syntax?No. You name fields and pick types from a list. The two places syntax appears are optional: a pattern field for shaped identifiers, and a formula field for values derived from other fields. Can I share the setup with a colleague?Yes — the schema encodes into the URL, so a link opens the exact setup. Signed in, you can also save named schemas and create revocable share links. Related /json — what the JSON export actually produces, including nesting and types. /sample-json-data — ready-made JSON files if you do not need a custom shape. /api-mock-data — using generated JSON as a mock API response. /json-to-json-schema — describing the JSON you just generated. /mockaroo-alternative — the same question against a hosted schema builder rather than a template. /faker-js-alternative — and against a library you call from code. Last updated 6 September 2026 ============================================================================ # JSON to CSV Converter — Free, Private, In Your Browser URL: https://fundata.dev/json-to-csv-converter ============================================================================ Home / JSON to CSV JSON to CSV converter Paste a JSON array — or NDJSON, one object per line — and get a CSV file back. It runs in this tab, so nothing is uploaded. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. What happens to nested objects and arrays CSV is flat and JSON is not, so this is the decision that defines the conversion. Two rules, both chosen so that the output keeps the same shape no matter which rows you feed in: Nested objects become dotted columns. {"user":{"name":"Ada"}} produces a column called user.name. Nesting goes as deep as your data does, and the notation matches the dot notation the builder uses to produce nested JSON, so a file can round-trip through both. Arrays are JSON-encoded in place. {"tags":["a","b"]} produces a single tags column containing ["a","b"], not tags_0 and tags_1. Exploding an array into columns sounds friendlier until you notice that the number of columns then depends on the longest array in the file: add one row with three tags and every previously-written file has a different header. Keeping the array in one cell means the header is a function of the keys alone. Rows with different keys Real JSON from an API rarely has identical keys in every object. The column set is the union of every key seen, in the order first encountered, and a row that lacks a key gets an empty cell. Nothing is dropped and nothing shifts: [{"a": 1}, {"b": 2}] a,b 1, ,2 Input shapes it accepts An array of objects — the normal case, and what most APIs return. A single object — converted to a one-row file. NDJSON — one JSON object per line, the shape of most log files and BigQuery exports. Detected automatically when the whole input is not valid JSON but every line is. An array of scalars — [1, 2, 3] — is rejected rather than guessed at. There are no field names in it, so there is no honest header row to write. Escaping in the output Values containing a comma, a double quote, a carriage return or a newline are wrapped in quotes, with inner quotes doubled, per RFC 4180. That is what makes the output safe to feed back into a parser — including the reverse converter, which is the quickest way to check a conversion round-trips. Opening the result in a spreadsheet Excel decides the delimiter from your system locale, so on a machine set to a locale that uses commas as decimal separators a comma-separated file lands in one column. Use Data → From Text/CSV rather than double-clicking the file, or convert to TSV instead — tabs are unambiguous. The Excel guide covers the rest of that particular argument. Common questions How are nested objects handled?They flatten to dotted column names: {"user":{"name":"Ada"}} becomes a column called user.name. Nesting goes as deep as the data does. Why is my array in a single cell?Because exploding it into tags_0, tags_1 and so on would make the column count depend on the longest array in the file, so adding one row could change the header. The array is JSON-encoded into one cell instead. Can it convert NDJSON?Yes. If the input is not valid JSON as a whole but every line parses on its own, it is read as NDJSON — one object per line. What if my objects have different keys?The header is the union of all keys, in the order they are first seen, and a row missing a key gets an empty cell. No row is dropped and no value shifts into the wrong column. Is anything uploaded?No. The conversion runs in your browser; the JSON never leaves your machine. Related /csv-to-json-converter — the reverse conversion, and the type-detection rules it applies. /json-to-sql-converter — the same flattening, but writing CREATE TABLE and INSERT instead. /csv — generating CSV test data from a schema rather than converting existing JSON. /excel-sample-data — getting a converted file to open correctly in Excel and Sheets. Last updated 6 September 2026 ============================================================================ # JSON to JSON Schema Generator — Draft 2020-12, Free URL: https://fundata.dev/json-to-json-schema ============================================================================ Home / JSON to JSON Schema JSON to JSON Schema generator Paste a JSON array and get a draft 2020-12 schema describing it — required keys, nested objects and recognised string formats, all worked out from the records you paste. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Why it reads every record, not the first one The usual failure of a JSON-to-type tool is that it looks at one object. The first record's null becomes the type null; a key that happens to be absent from it never appears at all; a field that is 7 in the first row and 7.5 in the fortieth is typed as an integer. Each of those compiles, passes review and breaks on the second page of results. This reads every record you paste and merges what it finds: A key missing from any record is optional — the marker is on the key, not the value. A key that is sometimes null is nullable — a different statement from optional, and both can be true. Mixed numbers collapse to one number type. A field holding 7 and 9.5 is a number, not "an integer or a number". Recognised string shapes keep their meaning where the target can express it — UUIDs, ISO timestamps, dates, emails and URLs. An empty array says nothing about its elements, so it stays unknown rather than being guessed from a sibling record. So paste more than one record. Paste the awkward ones — the row with the null, the row missing the optional field, the one from the second page. The output is only as good as the range of the sample, and that is a property of the input, not of the tool. Required, optional and nullable JSON Schema keeps these three apart, and the generated schema uses each for what it means: Required — a key present in every record you pasted is listed in required. Optional — a key absent from any record is simply left out of required. There is no "optional" keyword; that absence is the mechanism. Nullable — a key whose value is sometimes null gets a type array, ["string", "null"]. This is the draft 2020-12 spelling; OpenAPI 3.0's nullable: true is a different, older dialect. Formats it recognises Where every value in a field matches, the schema records a format: uuid, date-time, date, email and uri. Bear in mind that format is annotation-only by default — most validators do not enforce it unless you turn format assertion on. It is a statement of intent that tooling can read, not a constraint you get for free. What is not in the output No $id, no $defs and no additionalProperties: false. Each is a decision about how the schema will be used rather than something a sample can tell you: whether the document has a canonical URL, whether repeated shapes should be factored out and referenced, and whether unknown keys are an error or forward compatibility. Add them deliberately. Also absent: minimum, maxLength, pattern and enum. All four are constraints a sample can only guess at, and a guessed constraint is a schema that rejects valid data the first time real input arrives. Common questions Which draft does it generate?Draft 2020-12, declared in the $schema keyword. Nullable fields use a type array, which is the 2020-12 spelling — OpenAPI 3.0 nullable: true is a different, older dialect. How does it decide what is required?A key present in every record you paste is required; a key absent from any of them is not. There is no optional keyword in JSON Schema — being left out of required is the mechanism. Does format actually validate anything?Not by default. In JSON Schema, format is annotation-only unless the validator has format assertion enabled. It records intent that tooling can read rather than a constraint you get automatically. Why is there no additionalProperties: false?Because whether unknown keys are an error or forward compatibility is a decision about your API, not something a sample can reveal. The same goes for $id and $defs. Why are there no minimum or enum constraints?A sample cannot tell a real constraint from a coincidence. A guessed minimum or a guessed enum produces a schema that rejects valid data the first time real input arrives. Related /json-to-typescript — the same shape as TypeScript interfaces. /json-to-zod — a runtime validator in TypeScript. /json-to-pydantic — a runtime validator in Python. /api/types.json — this site’s own field catalogue, published as JSON. Last updated 6 September 2026 ============================================================================ # JSON to Pydantic Model Generator — Free, In Your Browser URL: https://fundata.dev/json-to-pydantic ============================================================================ Home / JSON to Pydantic JSON to Pydantic model generator Paste a JSON array and get the Pydantic models that parse it — nested models, Optional fields, and aliases wherever a JSON key is not a legal Python attribute name. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Why it reads every record, not the first one The usual failure of a JSON-to-type tool is that it looks at one object. The first record's null becomes the type null; a key that happens to be absent from it never appears at all; a field that is 7 in the first row and 7.5 in the fortieth is typed as an integer. Each of those compiles, passes review and breaks on the second page of results. This reads every record you paste and merges what it finds: A key missing from any record is optional — the marker is on the key, not the value. A key that is sometimes null is nullable — a different statement from optional, and both can be true. Mixed numbers collapse to one number type. A field holding 7 and 9.5 is a number, not "an integer or a number". Recognised string shapes keep their meaning where the target can express it — UUIDs, ISO timestamps, dates, emails and URLs. An empty array says nothing about its elements, so it stays unknown rather than being guessed from a sibling record. So paste more than one record. Paste the awkward ones — the row with the null, the row missing the optional field, the one from the second page. The output is only as good as the range of the sample, and that is a property of the input, not of the tool. Keys that are not legal Python names JSON keys can contain spaces, dots and hyphens; Python attributes cannot. A key like "full name" becomes an attribute named full_name with an alias back to the original, so parsing the untouched payload still works: class User(BaseModel): full_name: str = Field(alias="full name") Populate by alias when constructing from JSON — model_config = ConfigDict(populate_by_name=True) in Pydantic v2 if you also want to construct by the Python name. Without the alias the field would simply never populate, and it would do so silently. Imports are emitted only where used Optional, List, Union, UUID, date, datetime and Field each appear in the import block only if the models below actually use them. An unused import is a lint error in most Python projects, which turns a generated file into something you have to edit before it passes CI — and a generator whose output needs editing is one people stop using. Model order and forward references Nested models are emitted before the models that annotate fields with them. A class annotation naming a class defined further down the file is a NameError at import time, so the ordering is correctness rather than preference. What is deliberately left to you EmailStr is not used. It requires the email-validator package; the generated file has no dependencies beyond Pydantic itself. Swap it in if you already have that installed. No validators. Ranges, regexes and cross-field rules are business logic, and a sample cannot see them. No enums. A field holding only two values in the sample may hold a third tomorrow. Pydantic v2 syntax. Optional[X] with Field(default=None) works on v1 too, but model_config and the v2 methods do not exist on v1. Common questions Does this target Pydantic v1 or v2?The generated models use syntax valid in both: Optional[X] with Field(default=None), aliases via Field(alias=...), and standard typing constructs. Configuration you add on top — model_config versus class Config — differs between the versions. Why is a JSON key with a space turned into an attribute with an underscore?Because a Python attribute cannot contain a space. The field gets an alias back to the original key so parsing the untouched payload still works — without it the field would silently never populate. Why is EmailStr not used for email fields?EmailStr requires the email-validator package. The generated file is meant to run against Pydantic alone; swap str for EmailStr if you already depend on it. Why are nested models defined first?A class annotation that names a class defined further down the file raises a NameError when the module is imported. Emission order is correctness, not style. Are unused imports included?No. Optional, List, Union, UUID, date, datetime and Field are each imported only if the models use them, so the output does not fail a lint run before you have touched it. Related /json-to-zod — the same job in TypeScript, with runtime validation. /json-to-typescript — static types only. /json-to-sql-converter — loading the same JSON into a table instead of a model. /types — generating JSON that matches a model you already have. Last updated 6 September 2026 ============================================================================ # JSON to SQL Converter — CREATE TABLE and INSERT Statements URL: https://fundata.dev/json-to-sql-converter ============================================================================ Home / JSON to SQL JSON to SQL converter Turn a JSON array or an NDJSON log into a table definition and a batched insert. Nested objects become columns. Everything runs in this tab. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Flattening before loading A relational table has no room for a nested object, so the structure has to go somewhere before the insert. Nested objects flatten to a path, and because a dot is not something a column name should carry into a database, the path is joined with underscores in the SQL: {"user": {"name": "Ada", "email": "ada@example.test"}} CREATE TABLE "records" ( "user_name" VARCHAR(32), "user_email" VARCHAR(48) ); Arrays are not flattened. {"tags":["a","b"]} becomes one column holding ["a","b"] as text — which is honest, and on PostgreSQL is one ALTER TABLE … TYPE jsonb USING … away from being queryable. Splitting an array into tags_0 and tags_1 would make the table's column count depend on the longest array in the file, and the second file would not fit the first file's schema. When flattening is the wrong answer If the nesting is a real one-to-many — orders with line items, posts with comments — flattening produces a wide table with repeated parent values, and you wanted two tables with a foreign key. This converter will not invent that split for you. Convert the arrays separately, or model the tables first and use the database guide. Column types JSON already carries types, but a converted column still has to satisfy every row, so types are decided from the values actually present: booleans become BOOLEAN, whole numbers BIGINT, decimals DECIMAL(18,6), ISO dates DATE, ISO timestamps TIMESTAMP, UUID strings UUID, and everything else VARCHAR(n) or TEXT. Nulls are ignored when deciding — a key that is null in some rows still gets a useful type from the rest. NDJSON and log files One JSON object per line is the shape of most application logs, BigQuery exports and Elasticsearch bulk data. Paste it as-is: if the whole input is not valid JSON but every line parses on its own, it is read as NDJSON. That makes this the shortest path from a log file to a queryable table, which is usually what you actually wanted when you opened the log. Missing keys The column set is the union of every key across every object. A row that lacks a key gets NULL — which is exactly right here, because in JSON an absent key genuinely means "not present" rather than "empty". Common questions How are nested objects turned into columns?They flatten to a path joined with underscores: {"user":{"name":"Ada"}} becomes a column called user_name. Arrays are kept as text in a single column rather than split across numbered columns. Can it read NDJSON?Yes. If the input is not valid JSON as a whole but each line parses on its own, it is read as one object per line — the shape of most log files and BigQuery exports. What happens to a key that is missing from some objects?It becomes a column, and the rows without it get NULL. In JSON an absent key means "not present", so NULL is the accurate translation. Which dialects does it target?PostgreSQL and MySQL, differing in identifier quoting. Both outputs run on SQLite and SQL Server with minor edits. Should I use this for a one-to-many relationship?No. If the nesting represents orders with line items, flattening gives you a wide table with repeated parent values when you wanted two tables and a foreign key. Convert the arrays separately instead. Related /csv-to-sql-converter — the same output, starting from a CSV file. /json-to-csv-converter — flattening JSON without the SQL. /mongodb-test-data — keeping the nesting instead, and loading it into a document store. /database-test-data — designing the tables before you load anything into them. Last updated 6 September 2026 ============================================================================ # JSON to TypeScript Interface Generator — Free Online URL: https://fundata.dev/json-to-typescript ============================================================================ Home / JSON to TypeScript JSON to TypeScript interface generator Paste a JSON array and get the interfaces that describe it. Nesting is kept, keys that are sometimes absent are marked optional, and it all runs in this tab. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Why it reads every record, not the first one The usual failure of a JSON-to-type tool is that it looks at one object. The first record's null becomes the type null; a key that happens to be absent from it never appears at all; a field that is 7 in the first row and 7.5 in the fortieth is typed as an integer. Each of those compiles, passes review and breaks on the second page of results. This reads every record you paste and merges what it finds: A key missing from any record is optional — the marker is on the key, not the value. A key that is sometimes null is nullable — a different statement from optional, and both can be true. Mixed numbers collapse to one number type. A field holding 7 and 9.5 is a number, not "an integer or a number". Recognised string shapes keep their meaning where the target can express it — UUIDs, ISO timestamps, dates, emails and URLs. An empty array says nothing about its elements, so it stays unknown rather than being guessed from a sibling record. So paste more than one record. Paste the awkward ones — the row with the null, the row missing the optional field, the one from the second page. The output is only as good as the range of the sample, and that is a property of the input, not of the tool. Nesting becomes named interfaces A nested object gets its own interface rather than an inline literal. Inline is technically the same type and unreadable past two levels, and an unreadable type is one nobody edits — so the generated code stops being the source of truth the first time the shape changes. export interface User2 { name: string; email: string; nickname?: string; } export interface User { id: number; user: User2; tags: string[]; score: number; created: string; } Names come from the key, so a property called user produces a User-ish name; when that collides with the root name it is numbered rather than silently merged. Rename them — the output is a starting point, not a lockfile. Optional and nullable are different nickname?: string means the key may be absent. nickname: string | null means the key is there and its value may be null. TypeScript treats these differently under exactOptionalPropertyTypes, and an API that returns explicit nulls is not the same as one that omits the field. Both are inferred separately, and a key that is both sometimes-absent and sometimes-null gets both. What it does not infer Literal unions. A status field holding only "open" and "closed" is typed string, not 'open' | 'closed' — a sample cannot tell a closed set from an open one, and guessing wrong produces a type that rejects valid data. Branded or nominal types. A UUID is string; TypeScript has no built-in for it. Dates. An ISO timestamp is string, because that is what JSON.parse gives you. Convert deliberately rather than typing it as Date and being wrong at runtime. Recursive shapes. A tree that nests into itself produces nested interfaces rather than a recursive one. Common questions Does it read the whole array or just the first object?The whole array. That is the difference that matters: inferring from the first object alone marks nothing optional, types a first-row null as null, and calls a field an integer because the first value happened to be whole. How are nested objects handled?Each gets its own named interface rather than being inlined. Inline object literals are the same type and become unreadable past two levels, and generated code nobody can read stops getting maintained. What is the difference between the ? and | null in the output?The question mark means the key may be absent from the object. The union with null means the key is present and its value may be null. They are different things, TypeScript treats them differently, and both are inferred separately. Why is my status field string rather than a union of its values?Because a sample cannot distinguish a closed set from an open one. If your data only ever contains "open" and "closed", that may be a coincidence of the sample — and a literal union that is wrong rejects valid data at compile time. Is my JSON uploaded anywhere?No. Inference and generation run in your browser, so the JSON never leaves your machine. That matters here more than on most converters, because the JSON people want typed is usually a real API response. Related /json-to-zod — the same inference, emitted as a runtime validator. /json-to-json-schema — the language-independent version of the same description. /json-to-csv-converter — flattening the same JSON into columns instead. /api-mock-data — generating JSON that matches a shape you already have. Last updated 6 September 2026 ============================================================================ # JSON to Zod Schema Generator — Free, In Your Browser URL: https://fundata.dev/json-to-zod ============================================================================ Home / JSON to Zod JSON to Zod schema generator Paste a JSON array and get the Zod schema that validates it — nesting kept, optional keys marked, and string formats recognised where Zod can check them. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Why it reads every record, not the first one The usual failure of a JSON-to-type tool is that it looks at one object. The first record's null becomes the type null; a key that happens to be absent from it never appears at all; a field that is 7 in the first row and 7.5 in the fortieth is typed as an integer. Each of those compiles, passes review and breaks on the second page of results. This reads every record you paste and merges what it finds: A key missing from any record is optional — the marker is on the key, not the value. A key that is sometimes null is nullable — a different statement from optional, and both can be true. Mixed numbers collapse to one number type. A field holding 7 and 9.5 is a number, not "an integer or a number". Recognised string shapes keep their meaning where the target can express it — UUIDs, ISO timestamps, dates, emails and URLs. An empty array says nothing about its elements, so it stays unknown rather than being guessed from a sibling record. So paste more than one record. Paste the awkward ones — the row with the null, the row missing the optional field, the one from the second page. The output is only as good as the range of the sample, and that is a property of the input, not of the tool. String formats become refinements Zod can check more than "this is a string", so where every value in a field matches a recognised shape the generated schema says so: Every value looks likeEmitted as a UUIDz.string().uuid() an ISO 8601 timestampz.string().datetime() an email addressz.string().email() an http or https URLz.string().url() a whole numberz.number().int() Every value, not most — one value that does not match drops the refinement, because a validator that rejects your own data is worse than one that checks less. Declaration order is not cosmetic Nested schemas are emitted before the schema that references them. A const used above its own declaration is a ReferenceError the moment the module is imported — not a type error, a crash — so the file is written innermost-first even though that puts the schema you came for at the bottom. Getting the type back out The generated file ends with an array schema for the whole payload. Add z.infer where you need the static type, so the schema stays the single source of truth rather than being kept in step with a hand-written interface: export type User = z.infer; export type UserList = z.infer; If you only want the static type and no runtime validation, the TypeScript generator emits interfaces directly. Common questions Which Zod version does the output target?The output uses z.object, z.array, z.union, .optional(), .nullable() and the string refinements .uuid(), .datetime(), .email() and .url() — all of which are present in Zod 3 and Zod 4. Why are nested schemas defined before the one that uses them?Because a const referenced above its declaration throws a ReferenceError when the module is imported. Order is correctness here, not style, so the file reads innermost-first. When does it add .email() or .uuid()?Only when every value in that field matches the shape. One value that does not drops the refinement, because a validator that rejects your own data is worse than one that checks less. How do I get a TypeScript type from the schema?Add z.infer. Keeping the schema as the source of truth is the point — a hand-written interface next to a schema is two things to keep in step. Does the JSON get uploaded?No. Inference and generation happen in your browser. Related /json-to-typescript — the static type without the runtime validator. /json-to-pydantic — the same job on the Python side. /json-to-json-schema — a validator description that is not tied to a language. /api-mock-data — generating fixtures that satisfy the schema you just made. Last updated 6 September 2026 ============================================================================ # JSON Test Data Generator — Fun Data Playground URL: https://fundata.dev/json ============================================================================ JSON test data generator Design a schema and export a pretty-printed JSON array with proper types — numbers stay numbers, booleans stay booleans, blanks become null. Perfect for fixtures and mock APIs. Generate JSON data → Browse field types What the output looks like [ { "id": 1, "full_name": "Elena Rossi", "email": "elena.rossi7@example.org", "is_active": true, "last_login": null } ] Why JSON? JSON is the default interchange format of the web, and unlike CSV it keeps your types: integers and decimals are emitted as numbers, booleans as true/false, and fields with a Blank % produce genuine nulls. That makes the output drop-in ready for unit test fixtures, seed files, Storybook data and mock API responses. Ideas for the output Test fixtures — export with a seed (e.g. sprint-42) and commit the file; every regeneration is byte-identical. Mock backends — serve the array from our sibling Fun API Playground or from json-server for an instant fake REST API. Frontend development — import the file directly and build UI against realistic names, addresses and prices instead of foo/bar. Quick recipe: data-driven tests import users from './fixtures/fundata_100_rows.json'; for (const user of users.slice(0, 10)) { test(`signup works for ${user.email}`, async ({ page }) => { /* … */ }); } Reshaping the array The export is a flat array of objects, which is the shape most fixtures want. When a consumer expects something else, jq gets you there without regenerating: # Wrap in an envelope, as many REST APIs do jq '{ data: ., meta: { total: length } }' fundata_100_rows.json # Key the array by id, for a lookup table or a mock store jq 'INDEX(.id)' fundata_100_rows.json # Keep only the fields a component actually renders jq '[.[] | {id, full_name, email}]' fundata_100_rows.json # Split into per-record files for a fixture directory jq -c '.[]' fundata_100_rows.json | split -l 1 - fixture- Types, escaping and compatibility The export is a valid UTF-8 JSON array. Numeric fields remain JSON numbers, Boolean fields remain booleans and blank values become null; strings are escaped with JSON.stringify-compatible rules. That makes one file usable from JavaScript, Python, Java, .NET and any standards-compliant parser. Choose NDJSON when a pipeline needs one independently parseable record per line. JSON removes CSV's ambiguity about types, but it introduces questions of its own. These are the ones that tend to surface once the fixture meets a real parser: Large integers lose precision in JavaScript. JSON numbers have no size limit, but JSON.parse produces IEEE-754 doubles, so an integer beyond 253 silently changes value. If your IDs are that large, generate them as a text field so they stay strings. Decimals are binary floats. A price of 129.99 parses to a value that is not exactly 129.99. That is correct JSON and a genuine source of penny-rounding bugs — worth generating prices to test against rather than engineering around. null, missing and empty are three different things. Blank % emits null, and the key is still present. A schema validator that treats a missing key the same as a null one will pass data your API would reject; this is the fastest way to find that out. Key order is not guaranteed by the spec. The export preserves your column order and repeats it for every record, so byte-comparison of two seeded exports works — but don't rely on order surviving a round trip through an arbitrary parser. Dates are strings. JSON has no date type. Timestamps are emitted as ISO 8601 strings, which every parser reads as text until your code converts it. Common JSON questions Can I use this output as a mock API response?Yes. Export a seeded JSON array and serve it from a fixture route, json-server or a test interceptor. See the mock API data guide for schema ideas. Does JSON preserve nulls and number types?Yes. Integer, decimal and Boolean fields are emitted as native JSON values, while a field selected by Blank % becomes null rather than an empty string. Can I generate nested objects or arrays?Not directly — the schema is a flat list of columns, so the export is a flat array of objects. Nesting is a reshaping step: pipe the file through jq as shown above, which keeps the generator simple and the transformation visible in your fixture pipeline. Is the JSON pretty-printed or minified?Pretty-printed with two-space indentation, so a committed fixture produces a readable diff when the schema changes. If you need it compact, jq -c . file.json minifies it; for one compact record per line, export NDJSON instead. Why is my large ID coming back as a different number?Because JSON.parse reads every number as a double, integers above 253 can't be represented exactly. Generate those IDs as a text or pattern field so they stay strings, which is also what most APIs do with account and order numbers. Other formats The same schema exports to all six formats — switch with one dropdown: CSV, TSV, NDJSON, SQL, XML. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # Test Data Methodology, Privacy & Safety — Fun Data URL: https://fundata.dev/methodology ============================================================================ Test data methodology, privacy and safety What the generator creates, how repeatability works and why generated values are designed for testing rather than real-world identity or payment use. Generation happens locally Fun Data Playground is a static application. Schema editing, preview generation and file export run in your browser; there is no server-side generation API, and the generated rows are never uploaded. Your current schema is stored in your own browser's localStorage so it survives a refresh. Signing in is entirely optional and adds only account sync: your schemas, datasets, history and settings replicate to Firebase Authentication and Cloud Firestore so they follow you to another device. Aggregate, anonymized visit analytics (Google Analytics) are collected separately from your generated data; the Content Security Policy scopes all outbound connections to those Google services and nothing else. What is stored, and where Everything below lives in your own browser unless you deliberately sign in: fundata-schema-v1 — the schema you are currently editing, so a refresh does not lose it. fundata-saved-schemas-v1, fundata-datasets-v1, fundata-history-v1 — named schemas, reusable custom datasets and recent generation history. fundata-dark and fundata-consent — theme preference and your analytics answer. Signing in replicates those same four data keys to a single Firestore document keyed to your account, so they follow you to another device. Generated rows are never part of that sync — only the schema that describes them. Creating a share link publishes that schema, and only that schema, to a document anyone with the link can read until you revoke it. Analytics stay off until you accept them. Nothing is loaded from Google's tag servers before that, and declining is remembered, so the question is asked once rather than on every visit. Deterministic seeds A seed string is converted into the internal state of a deterministic pseudo-random number generator. Every field then consumes that sequence in schema order. The same seed, schema, options and row count produce byte-identical output. Changing field order or an option intentionally changes the sequence, so exported schemas are the safest way to preserve a long-lived fixture definition. Concretely: the seed string is hashed to a 32-bit integer, which becomes the state of a small mulberry32 generator. That is a fast, well-distributed, non-cryptographic PRNG — the same family of algorithm used for procedural generation in games, chosen for exactly the property that matters here, which is that the sequence is completely determined by its starting state. What changes the output, and what does not Reproducibility is a contract, and it is worth knowing its edges. Output changes when you change the seed, add or remove a field, reorder fields, rename a field in a way that alters the export, change any field option, or change the row count. Output does not change with your browser, operating system, locale, time zone, or the time of day — the sequence carries no machine-specific input. One consequence catches people out: leaving the seed empty is not "seed zero", it means an arbitrary seed per run. If you want the same file tomorrow, type something into the Seed field. Anything will do; the string itself is not secret and is worth committing next to the fixture. The other consequence is a limit rather than a bug. A 32-bit seed space is roughly 4.3 billion distinct streams. That is far more than any fixture library needs and far too few to be treated as unpredictable — see the note on cryptographic use below. Rows that hang together Drawing every column independently produces rows that are individually plausible and collectively absurd: a customer called Marco Schneider whose email is elena.rossi@example.org, living in Paris, United States. Two field families are therefore resolved per row rather than per cell. Person fields — First Name, Last Name, Full Name and Email Address — resolve to one underlying person for that row, so the email is built from the name beside it. Location fields — City, Country and Country Code — resolve to one place, so a city always sits in the country next to it. Everything else is drawn independently, and that is a deliberate boundary rather than an oversight. Latitude and longitude are not the coordinates of the row's city; a postcode is a plausible shape, not that city's real postcode; and a manager_id or a foreign key drawn at random points nowhere in particular. Where a relationship has to hold, derive it — a Formula field for values computed from other columns, a Custom List seeded with real parent keys for references. Fictional and documentation-safe values Names, companies and addresses are assembled from reference lists and random combinations; they are not profiles copied from a customer database. Email and domain values use reserved documentation domains such as example.com and .test. IPv4 values come from the three RFC 5737 documentation ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) and IPv6 values from RFC 3849's 2001:db8::/32. None of them routes to a real host. Domain names and URLs resolve to nothing: they are built on the reserved .test, .invalid and .example top-level domains, which cannot be registered. Credit card values are the four published payment-gateway test PANs — see test card numbers. They are not issued accounts and are rejected by live payment processing. IBAN values carry the correct country prefix and length but random check digits, so they fail the ISO 13616 mod-97 check by construction — see fake IBANs. Passwords are drawn from an unambiguous character set with the same non-cryptographic generator as everything else. They are form-filling material, not credentials. Avatar URLs point at the third-party placeholder service i.pravatar.cc. The generator only emits the URL as text — nothing is fetched here — but an application that loads those URLs will make real requests to that service. The single exception worth calling out: phone numbers. There is no globally reserved phone range equivalent to example.com, so a number generated from a generic pattern can belong to a real subscriber. If anything downstream might dial or text your fixture, pin the pattern to a reserved fictional range — the phone number generator lists the common ones. Never use this for anything that needs to be unpredictable The generator is deterministic on purpose, which makes it exactly the wrong tool for any value whose security depends on being unguessable. That rules out session tokens, password-reset links, API keys, one-time codes, and anything else where an attacker guessing the next value is the threat. The UUID field is the one most likely to be misapplied here: the format is correct, the randomness is not cryptographic, and format is not the property that matters. Use your platform's crypto.randomUUID() or an equivalent CSPRNG for those. Realism is not validation Synthetic output is intended to exercise interfaces, serializers, imports and test logic. It does not guarantee postal deliverability, legal identity, bank ownership, phone reachability or production-grade statistical representativeness. Add application-specific constraints when your test requires them. Statistical shape deserves a specific warning. Most fields draw uniformly across their range, and almost nothing in the real world is uniform — incomes, order values, session lengths and page views are all heavily skewed. A model, a capacity plan or a performance benchmark built on uniform data will be confidently wrong. The Number (Normal Dist.) field and weighted Custom Lists (repeat a value to make it more common) exist to close some of that gap, but generated data is a substitute for production data in tests, not in analysis. Synthetic data and data protection Data-protection regimes such as the GDPR and Türkiye's KVKK apply to personal data — information relating to an identifiable living person. Values produced here are not derived from any individual: names are recombined from public-domain pools, and no field is sampled, masked or perturbed from a real record. That is a meaningfully different position from anonymised or pseudonymised production data, where the source rows existed and re-identification is a question of how hard someone tries. Two practical consequences. Generating from scratch avoids the re-identification problem rather than mitigating it, which is why it is a better answer than anonymising a production export for a demo environment. And a fixture you generate is safe to commit to a repository, paste into a bug report or hand to a contractor. What this page cannot do is tell you whether your particular use is compliant — that depends on your jurisdiction, your data flows and how you combine this data with everything else. It is a description of how the generator works, not legal advice. Testing boundaries deliberately Use Blank % for nullable columns, Unique for collision-sensitive identifiers, sequential dates for time series, custom lists for domain states and patterns for controlled identifiers. A good fixture includes both ordinary rows and explicit edge cases rather than relying on randomness to discover them. Randomness finds edge cases slowly and unreliably, and the ones that matter are usually specific: a name with an apostrophe, an empty optional field, a leap day, a value at the exact column width, an address in a country with no postal code. Generate the ordinary rows, then add those deliberately through a Custom List so a failure points at something nameable. The QA guide works through the data classes worth covering. Ready to apply these rules? Browse the field type reference or choose a workflow from test-data use cases. Common questions Where is the data generated? In your browser. There is no generation server, so a schema and the rows it produces never leave the machine you are on — not because the transfer is encrypted, but because there is no transfer. Is the generated data random enough to be secure?No, and it must not be used as though it were. Generation runs on a seeded pseudo-random number generator, which is precisely what makes output reproducible; that same property makes it predictable. Use a cryptographic source for tokens, keys, password resets or anything whose safety depends on being unguessable. Are the generated emails, cards and IBANs real?No. Emails use domains reserved by RFC 2606 and RFC 6761 that cannot receive mail, card numbers are the published payment-gateway test PANs rather than arbitrary Luhn-valid numbers, and IBANs have the right country length with random check digits, so they fail mod-97 validation by design. Does generated data count as personal data under the GDPR?Not when nothing in it derives from a real person, which is the case here: values are drawn from fixed public-domain reference lists and combined by a seeded generator, with no real record as input. The judgement is still yours to make for your own use, and combining synthetic columns with real ones produces a real dataset again. Is realistic-looking data the same as valid data?No, and treating it as such is the most common way this kind of tool misleads. A value can have the right shape and still fail every check a real system applies — a well-formed IBAN that fails its checksum, an address that no postal service recognises, a phone number in no assigned range. Last updated 6 September 2026 ============================================================================ # Free Mockaroo Alternative — No Signup Test Data Generator URL: https://fundata.dev/mockaroo-alternative ============================================================================ A free Mockaroo alternative Same job — schema in, realistic rows out — with no account, no row-count meter, and no upload of the schema or the data to anybody's server. Start generating →Field type reference The one architectural difference Mockaroo is a well-established hosted service, and hosted is the right model for plenty of teams — it is how you get server-side APIs, saved projects and scheduled generation. This site is built the other way round: the generator is a static page, and every row is produced by JavaScript in your own browser. Nothing about your schema or your data is transmitted anywhere, because there is no server to transmit it to. That single decision is what removes the account, the row limit and the privacy question at the same time — there is no per-request cost to meter. It also means the site keeps working offline after the first visit, and that generating 100,000 rows is bounded by your laptop rather than a queue. Side by side What follows from generating in the browser rather than on a server Fun Data PlaygroundA hosted generator Where rows are producedIn your browserOn the service's servers Where your schema goesNowhere — there is no server to send it toTo the service, which is what lets it save and re-run the schema for you AccountNot required; optional, and only to sync between your own devicesGenerally required to save work Generation APINone — this is a page, not a serviceOften the main reason to choose one Scheduled or server-side generationNot possibleAvailable Working offlineYes, after the first visitNo What bounds a large exportYour own machineThe service's own limits Structural differences only — how each tool is used, not what it currently offers. Check the other tool's own documentation before deciding. What you get here 68 field types across Basics, Person, Location, Internet, Business, Date & Time and Text — the full list is on the field type reference. Six export formats — CSV, TSV, JSON, NDJSON, SQL and XML. 100,000 rows per export, with no account and no daily allowance. Seeded output — the same seed and schema produce byte-identical data on any machine, which is what makes a fixture safe to commit. Blank % per field for nullable columns, and a Unique toggle for constrained ones. Formula fields for values derived from other columns, so line totals actually add up. Nested JSON through dot-notation field names, and SQL dialects for Postgres and MySQL. Shareable schema links, and an optional account purely for syncing schemas between your own devices — never required to generate. Where a hosted tool is the better answer Being straightforward about this is more useful than a feature table. Choose a hosted service when you need: A generation API your CI can call to produce fresh data on every run. Multi-table relational output generated in one pass with foreign keys resolved for you. Here that is a deliberate two-pass job — generate parents, feed their IDs into a Custom List on the child — which the database guide walks through. Millions of rows per file, or generation that runs on a server while you do something else. Team accounts with shared, permissioned schema libraries. Feature sets and pricing on hosted services change, so check the current terms of whichever you are comparing rather than trusting any comparison page, including this one. Moving an existing schema over Field names and types transfer directly — the vocabulary is close enough that a users or orders schema takes a couple of minutes to rebuild. Two shortcuts: Upload a CSV sample and the generator infers a starting schema from the header row and values, which is usually faster than adding fields one at a time. Start from a template — users, orders, employees, sensors or transactions — and edit from there. Anything the built-in types do not cover is usually reachable with a Pattern field (# digit, ? uppercase, ~ lowercase), a Regex field, a Custom List, or a Formula that derives the value from other columns. On the safety of the data Generated values are built so that a leaked fixture is embarrassing rather than dangerous: emails use RFC-reserved domains that cannot receive mail, card numbers are the published gateway test values, IBANs are correctly shaped but fail their checksum, and IP addresses come from documentation ranges. The methodology page documents every one of those choices. Common questions Is it really free with no signup?Yes. Generation runs entirely in your browser, so there is no server cost to meter and nothing to charge for. An account exists only to sync your saved schemas between your own devices, and is never required. Is there a row limit?Up to 100,000 rows per export, with no daily or monthly allowance. For more, export several times with different seeds and concatenate the files. Does my schema or data get uploaded anywhere?No. The generator is a static page and every row is produced locally. Unless you deliberately sign in to sync schemas or create a share link, nothing leaves your browser. Can it generate related tables with foreign keys?Not in a single pass. Generate the parent table, then paste its key column into a Custom List field on the child schema so every reference resolves. The database guide covers the full flow. Is there an API for generating data in CI?No — that is the main thing a hosted service offers that this does not. The alternative is to generate a seeded fixture once and commit it, which is more reproducible anyway. Related Field type reference — all 68 types with examples. Getting started guide — schema, seed, export in three steps. Use cases — databases, APIs, QA, spreadsheets and pipelines. Methodology — how the data is generated and why it is safe. Last updated 6 September 2026 ============================================================================ # MongoDB Test Data Generator — Seed with mongoimport URL: https://fundata.dev/mongodb-test-data ============================================================================ MongoDB test data generator Nested documents built with dot-notation field names, exported as a JSON array or newline-delimited JSON, and loaded into a collection with a single mongoimport. Generate JSON →NDJSON details Nested documents from flat field names MongoDB documents are rarely flat, and you do not have to post-process to get structure. Name a field with dots and the JSON export nests it: Field name Document -------------------------------------------------- name { address.city "name": "Elena Rossi", address.country "address": { "city": "Lyon", account.plan "country": "France" }, account.seats "account": { "plan": "pro", "seats": 12 } } Types survive the trip: numbers stay numbers, booleans stay booleans, and a field with Blank % set produces a genuine null. That matters more in MongoDB than in a relational store, because a document database will happily accept a string where every other document has an integer, and you will not find out until an aggregation fails. Loading a collection # JSON array export mongoimport --db shop --collection customers \ --file customers.json --jsonArray --drop # NDJSON export — one document per line, streams mongoimport --db shop --collection customers \ --file customers.ndjson --drop Use NDJSON for anything large. --jsonArray asks mongoimport to parse the whole file as one array, which means holding it in memory; the newline-delimited form streams document by document and has no such ceiling. For 100,000 documents the difference is noticeable, and NDJSON is the format mongoimport prefers anyway. --drop replaces the collection, which is what you usually want when reseeding a development database and never what you want anywhere else. _id, and whether to supply one Leave _id out and MongoDB generates an ObjectId per document — fine, but the identifiers differ on every reseed, so nothing in a test can reference them. Supplying your own makes fixtures addressable. A UUID field named _id gives you a stable string key that is identical on every machine using the same seed, so a test can navigate straight to a known document. The trade-off is that a random string primary key has worse insert locality than a monotonic ObjectId — irrelevant for a development fixture, worth knowing before you copy the pattern into production. Arrays inside documents Dot notation builds nested objects, not arrays — there is no field syntax for "three tags per document". Two practical routes: Generate a delimited string with a Custom List or Words field, then split it after import with a short aggregation pipeline or a script. Generate the child collection separately and either keep the reference-style shape (which many schemas want anyway) or use $lookup and $group to embed. Pull the parent IDs into a Custom List field on the child schema so references actually resolve. The reference-style version is usually closer to a real schema than a deeply embedded one, so this constraint tends to push in a helpful direction. Dates The export writes ISO 8601 strings, not BSON date objects. mongoimport with the default JSON parsing stores them as strings, which sorts correctly but will not answer a range query the way a real date does. Convert after import when it matters: db.customers.updateMany( { created_at: { $type: "string" } }, [{ $set: { created_at: { $toDate: "$created_at" } } }] ); Schema validation If your collection has a JSON Schema validator attached, a generated import is a good test of it. Set Blank % on fields the validator marks required and confirm the load is rejected rather than silently accepted — a validator nobody has ever seen reject anything is not evidence of correctness. Common questions How do I generate nested MongoDB documents?Name fields with dot notation — address.city, address.country — and the JSON export nests them under a shared parent object. Types are preserved, so numbers and booleans arrive as numbers and booleans. Should I import JSON or NDJSON?NDJSON for anything large. --jsonArray parses the whole file as a single array in memory; newline-delimited JSON streams one document at a time and is what mongoimport prefers. Can I set my own _id?Yes — name a UUID field _id. That makes documents addressable from tests, because the same seed produces the same identifiers on every machine, unlike server-generated ObjectIds. How do I generate an array field?Dot notation builds objects, not arrays. Generate a delimited string and split it after import, or generate a separate child collection and reference the parent IDs through a Custom List field. Why are my dates stored as strings?The export writes ISO 8601 strings and mongoimport stores them as such. Convert them after import with an updateMany using $toDate if you need real BSON dates for range queries. Related Sample JSON data — typed fixtures and nesting. NDJSON export — streaming-friendly output. PostgreSQL test data — the relational equivalent. Mock API data guide — serving these documents from a fake endpoint. Last updated 6 September 2026 ============================================================================ # MySQL Test Data Generator — Seed a MySQL Database URL: https://fundata.dev/mysql-test-data ============================================================================ MySQL test data generator Backtick-quoted inserts for a seed script, or CSV for LOAD DATA when the row count gets serious — both from the same schema, both reproducible from a seed. Generate SQL →SQL format details MySQL-flavoured output Set the SQL export's dialect to mysql and identifiers are wrapped in backticks rather than double quotes — which matters, because MySQL only treats double quotes as identifier quoting under ANSI_QUOTES, and by default reads them as string literals: CREATE TABLE `customers` ( `id` INT, `full_name` VARCHAR(100), `email` VARCHAR(120), `signup_date` DATE ); INSERT INTO `customers` (`id`, `full_name`, `email`, `signup_date`) VALUES (1, 'Elena Rossi', 'elena.rossi7@example.org', '2024-03-18'), (2, 'Marco Schneider', 'marco_schneider@example.com', '2024-07-02'); Batching packs 250 rows into each statement, which is the difference between a seed script that runs in seconds and one that runs in minutes. Keep an eye on max_allowed_packet if you widen the schema considerably. Bulk loading with LOAD DATA Past a few thousand rows, export CSV instead: LOAD DATA LOCAL INFILE 'customers.csv' INTO TABLE customers CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS; Two things commonly block this. LOCAL requires local_infile enabled on both server and client (mysql --local-infile=1), and without LOCAL the file must sit inside secure_file_priv on the server. Check both before assuming the export is at fault. utf8mb4, not utf8 Generated data includes non-ASCII names and cities by default, and the export is UTF-8. If your table or connection uses MySQL's legacy utf8 — three bytes, no astral plane — anything outside the basic multilingual plane is silently mangled or rejected. Use utf8mb4 for the column, the table and the connection. The generator's Emoji field is the fastest possible test of whether you actually did. NULL in a CSV load MySQL's LOAD DATA does not treat an empty field as NULL — it stores an empty string, or zero in a numeric column. Fields set to blank via Blank % therefore need explicit handling: LOAD DATA LOCAL INFILE 'customers.csv' INTO TABLE customers FIELDS TERMINATED BY ',' ENCLOSED BY '"' IGNORE 1 ROWS (id, full_name, @email, signup_date) SET email = NULLIF(@email, ''); Use the SQL export instead if you would rather not think about it — it writes NULL literals directly. AUTO_INCREMENT after loading Inserting explicit primary keys leaves the auto-increment counter behind, so the next application insert collides. Reset it once the load finishes: ALTER TABLE customers AUTO_INCREMENT = 100001; Or truncate before loading — TRUNCATE TABLE resets the counter, DELETE FROM does not, which is a distinction that has cost many people an afternoon. Speeding up a large load SET autocommit = 0; SET unique_checks = 0; SET foreign_key_checks = 0; -- load here SET foreign_key_checks = 1; SET unique_checks = 1; COMMIT; Disabling checks is appropriate for a development seed where you trust the generated data, and inappropriate anywhere near production. Add secondary indexes after loading rather than before, and remember that InnoDB inserts fastest in primary-key order — sequential Row Number keys are the friendly case. Strict mode is your friend If a load silently truncates a long value or turns a bad date into 0000-00-00, strict mode is off. Turn it on in development so the generated data tells you about column-width and format mismatches instead of quietly absorbing them — that is precisely the feedback a test fixture exists to give. Common questions Why does my SQL script fail on double-quoted identifiers?The export was set to the Postgres dialect. Switch it to mysql so identifiers use backticks — MySQL reads double quotes as string literals unless ANSI_QUOTES is enabled. LOAD DATA LOCAL INFILE is refused. Why?local_infile has to be enabled on both the server and the client (mysql --local-infile=1). Without LOCAL, the file must live inside the directory named by secure_file_priv. Why are my empty CSV fields stored as empty strings instead of NULL?That is how LOAD DATA behaves. Map the column through a user variable and apply NULLIF(@col, ''), or use the SQL export, which writes NULL literals directly. My application hits a duplicate key error after seeding.The AUTO_INCREMENT counter is behind the explicit IDs you loaded. TRUNCATE TABLE before loading resets it, or set it explicitly with ALTER TABLE ... AUTO_INCREMENT afterwards. Do I need utf8mb4?Yes. Generated data contains non-ASCII characters, and MySQL's legacy three-byte utf8 cannot store everything UTF-8 can. Use utf8mb4 on the column, table and connection. Related PostgreSQL test data — COPY, sequences and deferred constraints. MongoDB test data — document fixtures via mongoimport. Database test data guide — constraints, nulls and foreign keys. CSV export reference — quoting and delimiters. Last updated 6 September 2026 ============================================================================ # NDJSON Test Data Generator — Fun Data Playground URL: https://fundata.dev/ndjson ============================================================================ NDJSON test data generator One JSON object per line, up to 100,000 lines — the format that streams: pipe it through jq, bulk-load it into Elasticsearch, or feed it to BigQuery. Fully client-side. Generate NDJSON data → Browse field types What the output looks like {"reading_id":"b3c9a1f2-6d4e-4a7b-9c1d-2f8e5a6b7c8d","temperature_c":21.4,"recorded_at":1742291286} {"reading_id":"7f2e0d94-1c3b-4e5a-8f6d-9a0b1c2d3e4f","temperature_c":-3.1,"recorded_at":1742291347} Why NDJSON? Newline-delimited JSON keeps JSON's typed values but drops the enclosing array, so each line is a complete, independently parseable record. Tools can process line one before line two exists — which is exactly what log pipelines, bulk loaders and streaming consumers want, and why NDJSON is the native ingestion format for Elasticsearch, BigQuery and most log tooling. Where NDJSON shines jq pipelines — filter, group and reshape records without loading the whole file. Elasticsearch — interleave with action lines for the _bulk API. BigQuery — bq load --source_format=NEWLINE_DELIMITED_JSON takes the file as-is. Big exports — 100,000 rows stream through line-based tools with constant memory. Loading NDJSON into streaming tools Everything below reads the file a line at a time, so a 100,000-row export costs the same memory as a 10-row one. # Group by country without loading the file (-s would slurp it; this doesn't) jq -s 'group_by(.country) | map({country: .[0].country, users: length})' \ fundata_10000_rows.ndjson # Filter to the rows you care about, still line by line jq -c 'select(.temperature_c < 0)' readings.ndjson > freezing.ndjson # BigQuery — the format is native, no schema conversion step bq load --source_format=NEWLINE_DELIMITED_JSON --autodetect \ mydataset.readings readings.ndjson # Elasticsearch _bulk — interleave an action line before each record jq -c '{ index: {} }, .' readings.ndjson \ | curl -s -H 'Content-Type: application/x-ndjson' \ --data-binary @- localhost:9200/readings/_bulk # DuckDB reads it directly SELECT country, count(*) FROM read_json_auto('readings.ndjson') GROUP BY 1; NDJSON compatibility and edge cases Each UTF-8 line is a complete JSON object with no outer array and no trailing comma. Newlines inside string values are escaped, so one physical line always equals one record. This is also called JSON Lines or .jsonl; the content model is the same even when tools prefer a different extension. Native numbers, booleans and nulls are preserved exactly as they are in JSON export. The whole point is partial failure. One malformed line costs you one record, not the file. A pipeline that aborts the entire load on a single bad line is throwing away NDJSON's main advantage — generate a large export and corrupt one line to find out which behaviour you actually have. The trailing newline matters. Files end with a newline after the last record. Some bulk endpoints, Elasticsearch's _bulk among them, reject a payload whose final line is unterminated. Don't pretty-print it. Indented JSON spans multiple physical lines, which breaks the one-line-one-record contract every consumer here relies on. This is why the NDJSON export is compact while the JSON export is indented. Concatenation is the merge operation. Two NDJSON files join with cat, with no header to strip and no brackets to reconcile. That makes it the practical choice when you need more than 100,000 rows: export several times with different seeds and concatenate. Line-splitting is not always byte-splitting. Tools that split on \n without decoding UTF-8 first are safe here, because a newline byte can't appear inside a multi-byte character — but the same is not true of naive fixed-size chunking. Common NDJSON questions What is the difference between JSON and NDJSON?JSON export wraps all records in one array. NDJSON writes one object per line, which lets streaming tools process records independently without parsing the entire file first. Can I use the file as JSONL?Yes. NDJSON and JSON Lines use the same one-object-per-line structure. Rename the extension to .jsonl when a particular importer expects it. How do I generate more than 100,000 rows?Export several times with different seeds and concatenate with cat. NDJSON is the one format where this needs no cleanup — there is no header to strip and no enclosing array to reconcile, so the joined file is immediately valid. Why is the NDJSON compact when the JSON export is indented?Because indentation would break it. NDJSON's contract is one record per physical line, and pretty-printed JSON spans several. The JSON export is indented for readable fixture diffs; NDJSON stays compact so streaming consumers work. Can I feed this straight into Elasticsearch's bulk API?Almost — _bulk expects an action line before each document. Pipe the export through jq -c '{ index: {} }, .' to interleave them, and make sure the payload ends with a newline, which the export already does. For ingestion fixtures and repeatable pipeline tests, see QA test-data patterns. Other formats The same schema exports to all six formats — switch with one dropdown: CSV, TSV, JSON, SQL, XML. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # Playwright Test Data — Deterministic Fixtures for E2E Tests URL: https://fundata.dev/playwright-test-data ============================================================================ Playwright test data Fixtures that are identical on your laptop and in CI, so a red test means the application changed — not that the data did. Generate JSON fixture →QA test data guide Why generated beats faked-at-runtime Calling a fake-data library inside a test gives different values on every run. That sounds like better coverage and behaves like a flaky suite: a test fails once in forty runs, nobody can reproduce it, and eventually somebody adds a retry. Generating the data once with a seed and committing the file moves the randomness to a place you control — you still get realistic, varied data, but the same realistic, varied data every time. When you do want a different dataset, change the seed deliberately and commit that. The variation becomes a decision with a diff attached rather than a background process. A committed fixture Export JSON with a seed and save it under tests/fixtures/: // tests/fixtures/users.json — seed "users-v3", 25 rows import users from './fixtures/users.json' with { type: 'json' }; import { test, expect } from '@playwright/test'; test('user list renders every row', async ({ page }) => { await page.goto('/users'); await expect(page.getByTestId('user-row')).toHaveCount(users.length); await expect(page.getByText(users[0].full_name)).toBeVisible(); }); Because the seed fixes the output, users[0].full_name is a known value. The assertion reads clearly and the failure message names an actual person instead of "expected 25, received 24". Data-driven tests Generate the edge cases as their own small fixture and loop — Playwright creates a separate test per entry, so one bad row fails one test rather than the whole block: import cases from './fixtures/signup-cases.json' with { type: 'json' }; for (const c of cases) { test(`signup validation: ${c.label}`, async ({ page }) => { await page.goto('/signup'); await page.getByTestId('email').fill(c.email); await page.getByTestId('submit').click(); await expect(page.getByTestId('form-error')).toHaveText(c.expected); }); } Build that fixture from a Custom List field holding the malformed inputs you care about, alongside a generated column of valid ones. Mocking the API instead of the database For a frontend-only run, serve the fixture straight from page.route — no backend, no database, and full control over the response states that are awkward to trigger for real: await page.route('**/api/users*', route => route.fulfill({ status: 200, json: users }) ); // the states a real backend rarely produces on demand await page.route('**/api/users*', route => route.fulfill({ status: 500 })); await page.route('**/api/users*', route => route.fulfill({ status: 200, json: [] })); await page.route('**/api/users*', route => route.abort('failed')); The empty array is the one worth writing first. Empty states are the most commonly broken screen in any application, precisely because the development database is never empty. Seeding a real database For full end-to-end runs, export SQL and load it in globalSetup so every worker starts from the same known state: // playwright.config.ts → globalSetup: './tests/seed.ts' import { execSync } from 'node:child_process'; export default async function seed() { execSync('psql $TEST_DB -f tests/fixtures/seed.sql'); } Reseeding between runs rather than accumulating rows is what keeps a suite honest — a test that only passes on the third run is a test that depends on leftovers. Practising against a real site This generator is itself built for automation practice: every interactive control carries a stable data-testid, and those names do not change between deploys. Point a Playwright script at the builder, fill the schema form, trigger an export and assert on the preview — it exercises forms, selects, dialogs, drag-and-drop reordering, downloads and dynamically rendered rows without needing a test environment of your own. Common questions Should I generate data at runtime or commit a fixture?Commit a fixture generated with a seed. Runtime fake data varies per run, which turns a real failure into an unreproducible flake. A seeded file gives you realistic variety that is identical everywhere. How do I write data-driven Playwright tests?Export a small JSON fixture of cases and loop over it, calling test() inside the loop. Playwright registers one test per entry, so a single bad case fails on its own rather than taking the block with it. Can I use fixtures without a backend?Yes. page.route with route.fulfill serves the JSON directly, which also makes error, empty and timeout states trivial to test compared with provoking them from a real server. How do I seed a database before an end-to-end run?Export SQL and load it from globalSetup in playwright.config.ts so every worker starts from the same state. Reseed between runs instead of accumulating rows. Does this site work as a Playwright practice target?Yes. Every control has a stable data-testid that is kept unchanged across deploys, covering forms, dialogs, drag-and-drop, downloads and dynamic rows. Related Cypress test data — the same patterns with cy.fixture. QA test data guide — choosing what to cover. Sample JSON data — typed fixture output. Mock API data — response states worth generating. Last updated 6 September 2026 ============================================================================ # PostgreSQL Test Data Generator — Seed a Postgres Database URL: https://fundata.dev/postgresql-test-data ============================================================================ PostgreSQL test data generator Build a schema, export Postgres-flavoured SQL or COPY-ready CSV, and load a hundred thousand realistic rows into a development database in a couple of commands. Generate SQL →SQL format details Two ways in, and they are not equivalent Set the SQL export's dialect to postgres and identifiers are double-quoted, which is what Postgres expects. You get an optional CREATE TABLE followed by inserts, batched 250 rows to a statement when batching is on: CREATE TABLE "customers" ( "id" INT, "full_name" VARCHAR(100), "email" VARCHAR(120), "city" VARCHAR(60), "signup_date" DATE ); INSERT INTO "customers" ("id", "full_name", "email", "city", "signup_date") VALUES (1, 'Elena Rossi', 'elena.rossi7@example.org', 'Lyon', '2024-03-18'), (2, 'Marco Schneider', 'marco_schneider@example.com', 'Hamburg', '2024-07-02'); That is convenient up to a few thousand rows. Past that, use CSV and \copy instead — bulk loading bypasses per-statement overhead and is commonly an order of magnitude faster: psql mydb -c "TRUNCATE customers RESTART IDENTITY CASCADE" psql mydb -c "\copy customers FROM 'customers.csv' CSV HEADER" Note \copy (client-side, reads a file on your machine) rather than COPY (server-side, needs the file on the database host and superuser rights). The backslash form is almost always the one you want locally and in CI. Types the generator infers Each field carries a SQL type used by CREATE TABLE — INT, VARCHAR(n), DATE, TIME, DECIMAL(10,6), CHAR(36) for UUIDs. These are conservative and portable rather than idiomatic Postgres. If you are creating the table yourself, prefer the native types: uuid instead of CHAR(36), timestamptz instead of a string, numeric for money, and text instead of VARCHAR(n) — Postgres gains nothing from the length limit. Generate against your real DDL; treat the generated one as a starting point. NULL versus empty string Set Blank % on a field and the SQL export emits NULL, while the CSV export emits an empty field. Those two are the same thing to \copy by default — an unquoted empty field becomes NULL — but an explicitly quoted empty string does not. If the distinction matters in your schema, control it at load time: \copy customers FROM 'customers.csv' WITH (FORMAT csv, HEADER, NULL '') This is worth being deliberate about. A column that is NOT NULL in production but full of empty strings in development hides bugs until the day it does not. Sequences after a load The mistake that catches everyone: loading explicit primary keys does not advance the underlying sequence, so the first insert your application makes collides with row 1. Either truncate with RESTART IDENTITY before loading, or fix the sequence afterwards: SELECT setval( pg_get_serial_sequence('customers', 'id'), (SELECT COALESCE(MAX(id), 1) FROM customers) ); Foreign keys and load order Generate parent tables first and feed their key column into a Custom List field on the child schema, so every child row references a parent that exists — repeating some IDs gives you the realistic skew a uniform draw never will. Then load parents before children, or defer the checks and let one transaction sort it out: BEGIN; SET CONSTRAINTS ALL DEFERRED; \copy customers FROM 'customers.csv' CSV HEADER \copy orders FROM 'orders.csv' CSV HEADER COMMIT; Loading 100,000 rows quickly Drop non-essential indexes first, recreate after. Every index is maintained per inserted row; this is usually the single biggest win. One transaction. Autocommit forces a flush per statement. Consider UNLOGGED for throwaway development tables — no WAL, much faster, and you genuinely do not care if it survives a crash. Raise maintenance_work_mem before recreating indexes, then ANALYZE so the planner has statistics. Keep it separate from production Use an obviously named development database, never production credentials, and remember that generated values are for testing rather than identity or payment verification — IBANs fail their checksum and card numbers are published test values by design. The methodology page has the full picture. Common questions Should I use SQL inserts or CSV with COPY?Inserts are fine up to a few thousand rows and convenient because they are self-contained. Past that, export CSV and use \copy — bulk loading skips per-statement overhead and is commonly an order of magnitude faster. Why does my application hit a duplicate key error after seeding?Loading explicit IDs does not advance the sequence. Truncate with RESTART IDENTITY before loading, or call setval() with the current maximum afterwards. Does the export create the table for me?Optionally. Enable CREATE TABLE in the SQL export options. The inferred types are portable rather than idiomatic Postgres — for real work prefer uuid, timestamptz, numeric and text, and generate against your own DDL. How do I get NULL instead of an empty string?Set Blank % on the field. The SQL export writes NULL directly; for CSV, load with WITH (FORMAT csv, HEADER, NULL '') so empty fields become NULL. How do I make foreign keys line up?Generate the parent table first, then paste its key column into a Custom List field on the child schema. Independent random columns will never match. Related MySQL test data — the same job with LOAD DATA. MongoDB test data — JSON and NDJSON via mongoimport. Database test data guide — schema design and constraints. SQL export reference — dialects, batching and options. Last updated 6 September 2026 ============================================================================ # QA Test Data Generator for Automated Testing — Fun Data URL: https://fundata.dev/qa-test-data ============================================================================ QA test data for automated testing Build deterministic fixtures for Playwright, Cypress, unit tests and CI while keeping enough variation to expose real product defects. Build a QA fixture →Generator guide Stable data, actionable failures Random data can broaden coverage, but an unrepeatable failure is expensive. Give every committed fixture a descriptive seed and export its schema. When a test fails, teammates can regenerate the same rows locally; when the contract changes, update the schema and seed deliberately. Cover data classes explicitly Ordinary values: realistic names, addresses, prices and timestamps for the happy path. Missing values: Blank % produces nullable fields at a controlled rate. Distinct identifiers: Unique reduces accidental collisions within the generated dataset. Domain states: Custom List emits accepted statuses randomly, sequentially or with weights. Boundary shapes: Pattern and Regex Pattern create exact identifier formats. Time behavior: sequential dates create ordered time-series fixtures. Match the test layer to a format Use JSON for browser and unit-test fixtures, SQL or CSV for database setup, NDJSON for ingestion tests and XML for integration contracts. All formats share the same schema and seed. Playwright fixture example import users from './fixtures/users.json'; for (const user of users) { test(`profile renders for ${user.id}`, async ({ page }) => { await page.route('**/api/profile', route => route.fulfill({ json: user })); await page.goto('/profile'); }); } Seeding strategy for a suite One seed for an entire test suite is the usual first attempt, and it couples every test to every other one: change the schema for a new case and unrelated assertions start failing on different rows. Two rules avoid that. Give each scenario its own seed, named after the scenario. checkout-empty-cart, profile-long-names and orders-2024-boundary are independent fixtures that happen to share a generator. Regenerating one cannot disturb another, and a failure name tells you which fixture to reproduce. Regenerate deliberately, not automatically. A fixture rebuilt on every CI run is not a fixture — it is a source of flake. Commit the exported file, and treat regeneration as a reviewable change like any other, with the seed and schema recorded next to it. tests/fixtures/ checkout-empty-cart.json # seed: checkout-empty-cart profile-long-names.json # seed: profile-long-names orders-2024-boundary.ndjson # seed: orders-2024-boundary README.md # the schema share-links, one per fixture What generated data cannot test Being explicit about the boundary is what keeps the fixture honest. Generated rows give you breadth, volume and repeatability. They do not give you: Referential integrity. Each column is generated independently, so an order.customer_id will not correspond to a real customer row. Fixtures that need relationships have to be assembled — generate the parents, then generate children whose foreign keys you draw from a custom list of the parent IDs. Business-rule consistency. Nothing enforces that shipped_at comes after ordered_at, or that a cancelled order has no delivery date. If a rule matters, assert it rather than assuming the fixture honours it. Realistic distributions. Values are drawn evenly, and production data almost never is. Performance work that depends on skew — hot keys, a handful of enormous accounts — needs a shaped fixture, not a uniform one. Your exact edge cases. The empty string, the 256th character, the emoji in a surname, the leap-second timestamp. Hand-author those few rows and keep them alongside the generated bulk. Avoid false confidence A large random file is not a test plan. Add hand-authored rows for exact length limits, invalid states, duplicate business keys and timezone transitions. Use generated data for breadth and repeatability, then assert the behavior that matters. QA fixture checklist The schema reflects the production contract without copying production records. The seed is named and versioned. Null, empty, long and international strings are covered. IDs are stable and unique where required. Dates include boundaries relevant to the feature. Generated payment, email and network values remain test-only. See the methodology for safety details or the mock API guide for response fixtures. Common questions How do I make test data reproducible across a CI run? Give the seed a name and treat it as part of the test, not as a setting. The same seed and schema produce byte-identical output on any machine, so a failure on CI reproduces locally with the same rows rather than with new ones that happen not to fail. Should test fixtures be committed or generated at run time?Commit them when the test asserts against specific values, and generate when the test only needs volume. A committed file shows up in a diff and cannot change under a passing test; a generated one keeps the repository small but has to be regenerated identically, which is what the seed is for. What edge cases should generated test data cover?Empty and null values, the longest string a column allows, names and addresses with non-ASCII characters, dates at the boundaries of the range under test, and duplicate values in columns that are supposed to reject them. Blank %, the Unique toggle and the eight name locales cover most of these directly. What can generated data not test?Anything that depends on the shape of real data rather than its type. Realistic distribution, genuine referential integrity across many tables, values that encode business history, and the specific malformed records your production system has accumulated — those you have to construct deliberately, and generated data will quietly convince you they are covered when they are not. Can I use generated data in Playwright or Cypress?Yes, and the usual shape is a committed JSON fixture plus route interception, so the browser never depends on a backend. Both frameworks have a page here working through fixture placement and seeding. Last updated 6 September 2026 ============================================================================ # Random Date Generator — Bulk Test Dates & Timestamps URL: https://fundata.dev/random-date-generator ============================================================================ Random date generator Dates in a range you set, in the format your system expects — random for realistic spread, or sequential when you need a clean time series. Generate dates →All field types Range, format and order The Date field takes four options, and the last one is the interesting one: from and to — the inclusive bounds of the range. format — yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy or dd.MM.yyyy. order — random scatters dates across the range; sequential walks evenly from from to to across however many rows you asked for. Sequential order is what you want for anything that gets plotted or aggregated. A random draw over two years produces a lumpy series with gaps and clusters, which makes a chart look broken; sequential gives you one row per interval and a clean line. Random is right for things like signup dates, where clustering is realistic. The other time fields date 2024-11-02 (Date, yyyy-MM-dd) time 14:07:52 (Time, seconds optional) datetime_iso 2024-11-02T14:07:52Z (Datetime ISO 8601) unix_timestamp 1730556472 (Unix Timestamp) day_of_week Saturday month November timezone Europe/Istanbul Datetime (ISO 8601) is the one to reach for by default in API fixtures — it is unambiguous, sorts lexicographically and every JSON client parses it. Unix Timestamp gives you an integer column for systems that store epoch seconds. Birthdate lives with the person fields and is configured by age range rather than date range, which is usually what you actually mean. Dates that break things Date handling is where quiet bugs live, and a generated range will not find them on its own. Pick your from and to deliberately so the range crosses the boundaries that matter: A leap day. Any range spanning 29 February will eventually produce one. If your range is a single non-leap year, you have not tested it. A daylight-saving transition. The hour that does not exist in spring and the hour that happens twice in autumn. Combine a Date field spanning late March or late October with a Time field. Year boundaries. Week-numbering and fiscal-year logic go wrong around 31 December far more often than anywhere else. Month ends. A range covering 28, 29, 30 and 31-day months finds "add one month" bugs. Future dates. Set to past today deliberately if you want to test a validator that should reject them — otherwise the default range ends today and you never exercise that path. Two dates that have to agree A common need is created_at and updated_at, where the second must never precede the first. Independent date fields will violate that in roughly half the rows. Generate the first date normally, then use a Formula field for the second so it is derived from the first rather than drawn separately — the same approach applies to start and end dates, order and shipping dates, and subscription periods. The Formula reference shows the syntax. Format follows destination Generate the format your target actually stores. For a Postgres date column or a MySQL DATE, use yyyy-MM-dd — the ISO form is the only one those parse unambiguously. dd/MM/yyyy and MM/dd/yyyy are display formats, and loading them into a database means relying on locale settings that differ between your machine and CI. They are worth generating for exactly one purpose: testing that your import layer handles ambiguous dates, where 03/04/2024 is either March or April depending on who is reading. The same trap exists in spreadsheets, where Excel reinterprets date strings on import according to regional settings — covered in the Excel guide. Common questions How do I generate an evenly spaced time series?Set the Date field's order option to sequential. The generator walks evenly from the start of the range to the end across your requested row count, which is what charts and aggregations need. Random order produces realistic but lumpy data. Which date formats are available?yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy and dd.MM.yyyy for the Date field. For timestamps, use the separate Datetime (ISO 8601) or Unix Timestamp fields. How do I make sure updated_at comes after created_at?Derive it. Two independent date fields will produce impossible pairs in about half the rows; a Formula field that computes the second date from the first will not. Can I generate dates in the future?Yes. Set the "to" bound past today. The default range ends at the current date, so future-date validation paths go untested unless you extend it deliberately. Do the dates include a time zone?The Datetime (ISO 8601) field emits UTC with a Z suffix. The Date and Time fields are plain local values with no zone, and the Timezone field gives you IANA zone names as a separate column. Related generators UUID generator — identifiers for the same rows. Sales sample data — a time series with revenue attached. Sample JSON data — ISO timestamps in API fixtures. Date & time field reference — every time field and its options. Last updated 6 September 2026 ============================================================================ # Random Name Generator — Bulk Fake Names for Testing URL: https://fundata.dev/random-name-generator ============================================================================ Random name generator Realistic first, last and full names in eight locales — up to 100,000 at a time, generated in your browser and reproducible from a seed. Generate names →All field types What it generates Three separate fields cover the usual shapes: First Name, Last Name and Full Name. Put all three in one schema and they stay consistent — the full name is the same person as the first and last name on that row, not a fourth independent draw. first_name,last_name,full_name,email Elena,Rossi,Elena Rossi,elena.rossi7@example.org Marco,Schneider,Marco Schneider,marco_schneider@example.com Aisha,Yılmaz,Aisha Yılmaz,aisha.yilmaz@example.net That row coherence extends to Email Address: the local part is built from the same row's name, transliterated to ASCII. A fixture where elena.rossi7@example.org belongs to a row named "Marco Schneider" looks wrong the moment a human reads it, and that is exactly what tends to end up in a screenshot. Locales Every name field takes a locale option: any, en, es, de, it, tr, jp, in and ar. The default, any, draws from all of them, which is usually what you want — a name list that is 100% Anglo-American is a poor test of a system real people will use. Pin a single locale when you are testing something locale-specific: sort order under a particular collation, a column width that has to survive longer German surnames, or a UI that has to render Turkish dotted and dotless i (İ, ı) without mangling them. Mixed-script names are one of the cheapest ways to find encoding bugs before a user does. Names that break things If the point of the fixture is to stress-test, generate the ordinary list first and then add the awkward cases by hand as a Custom List field. The categories worth having in every serious test set: Apostrophes and hyphens — O'Brien, Sainte-Marie. These find SQL escaping bugs and over-eager input validation. Non-ASCII characters — Yılmaz, Müller, Þórsdóttir. These find encoding and collation bugs. Single-word names. Plenty of people have one. A required "last name" field is a design bug, not a data problem. Very long names. Set your Full Name column against the real database limit and see what truncates. The generator's own names are realistic rather than adversarial, which is the right default — you want most of your rows to look like ordinary data, with the edge cases added deliberately so a failure points at something specific. Reproducing the same list Type anything into the Seed field and the same seed plus the same schema produces byte-identical names every run, on every machine. That turns a name list into something you can reference in a test assertion instead of something you have to snapshot. Change the seed and you get a completely different list of the same shape — useful for checking that a test passes because the logic is right, not because it memorised one dataset. Exporting Names come out of every format the generator supports. CSV for spreadsheets and bulk loaders, JSON for frontend fixtures, SQL for a seed script that goes straight into Postgres or MySQL. Add phone numbers and addresses to the same schema and you have a full customer table rather than a name column. Are these real people? No. First and last names are drawn independently from public-domain name pools and recombined, so a generated full name is a combination, not a record. Any given combination could coincidentally match a living person — that is unavoidable with any name generator, including a hand-typed one — which is why the generated rows are for testing and demos rather than for anything that implies a real identity. The methodology page sets out what each field draws from. Common questions How many names can I generate at once?Up to 100,000 rows per export. Generation runs in your browser, so the practical ceiling is your own machine rather than a server quota. For a larger list, export several times with different seeds and concatenate the files. Can I get the same names again later?Yes. Enter a seed before generating. The same seed and schema always produce the same names, in the same order, on any machine — which is what makes generated names safe to assert against in a test. Do the email addresses match the names?Yes. First Name, Last Name, Full Name and Email Address are coherent within a row: the email local part is built from that row's name, transliterated to ASCII. Can I generate names for one country only?Set the locale option on each name field to en, es, de, it, tr, jp, in or ar. Leave it at "any" to draw from all nine, which produces a more realistic mix for most applications. Are the names safe to commit to a repository?Yes. They are synthetic combinations from public-domain pools, not records about real people, and no value is derived from a customer database. Related generators Fake email generator — addresses on reserved documentation domains that match the row's name. Fake address generator — streets, cities, postcodes and countries that stay coherent per row. Random phone number generator — pattern-driven numbers in any national format. Employee sample data — a ready-made schema built on these fields. Last updated 6 September 2026 ============================================================================ # Random Phone Number Generator — Fake Numbers for Testing URL: https://fundata.dev/random-phone-number-generator ============================================================================ Random phone number generator Fake phone numbers in whatever shape your form expects — you describe the format with a pattern, and the generator fills in the digits. Generate phone numbers →All field types You choose the format The Phone Number field takes a format string where # becomes a random digit and every other character is kept literally. The default is North American, but any national shape is one edit away: +1 (###) ###-#### → +1 (415) 892-3071 +44 7### ###### → +44 7912 480365 +90 5## ### ## ## → +90 532 118 47 26 0### ### ## ## → 0212 604 39 85 ###-###-#### → 415-892-3071 That is the entire mechanism, and it is enough for essentially every phone format in use, including extensions (+1 (###) ###-#### x###) and fixed operator prefixes you want to keep constant. Which numbers are safe to generate A phone column is the one place a "realistic" fixture can genuinely reach a stranger. Unlike email, there is no globally reserved phone range that fails safely everywhere — so the format you choose matters. Several countries reserve blocks specifically for drama and documentation, and pinning your pattern to one of them is the safest option if there is any chance the numbers get dialled or texted: North America: +1 (###) 555-01## — the 555-0100 to 555-0199 range is reserved for fictional use. United Kingdom: Ofcom's drama ranges, e.g. +44 7700 900### for mobile. Anywhere else: check whether the regulator publishes a reserved block, and if not, assume the number belongs to somebody. If the numbers are only ever going into a database and a UI, any pattern is fine. The moment an SMS gateway is anywhere in the pipeline, use a reserved range — the same reasoning behind the reserved domains in the email generator. Testing validation, not just display Generated numbers all match your pattern perfectly, which tests the happy path only. Real phone input is far messier, so add a Custom List field with the cases that actually break parsers: numbers with spaces in unexpected places, a leading 00 instead of +, brackets around the country code, letters (1-800-FLOWERS), a number that is one digit short, and an international number entered without any country code at all. Set Blank % to something like 15 if phone is an optional field — a surprising number of forms treat an empty phone as valid on submit and then crash when something downstream tries to format it. Storing numbers Generate the format you intend to store, not the one you intend to display. If your application normalises to E.164 (+14158923071, no spaces or punctuation) then generate that shape with +1##########, and let the UI do the formatting. A fixture full of pretty display strings hides exactly the normalisation bugs you want to find. The SQL export maps the field to a string column, which is correct — phone numbers are not integers, and treating them as such loses leading zeros. Exporting Phone columns work in all six formats. Watch one detail in CSV: a number like 0212 604 39 85 opened by double-clicking in Excel loses its leading zero, because Excel guesses the column is numeric. Import via Data → From Text/CSV and set the column to Text — the spreadsheet guide covers this. Common questions Can these numbers actually be called?It depends entirely on the pattern you choose. A generic pattern can produce a number that belongs to a real subscriber. If anything in your pipeline might dial or text these, use a reserved fictional range such as +1 (###) 555-01## in North America or +44 7700 900### in the UK. How do I generate numbers for a specific country?Set the format option to that country's shape, using # for each digit and keeping prefixes literal — for example +90 5## ### ## ## for a Turkish mobile or +44 7### ###### for a UK mobile. Can I generate E.164 numbers?Yes. Use a pattern with no punctuation, such as +1##########, which is the format most systems normalise to for storage. Why does my CSV lose the leading zero?That is Excel, not the export. Import with Data → From Text/CSV and mark the phone column as Text rather than double-clicking the file. Are the numbers unique?Not unless you enable the Unique toggle on the field. With a restrictive pattern the pool of possible values shrinks quickly, so keep the row count well inside what the pattern can produce. Related generators Random name generator — the contacts these numbers belong to. Fake address generator — the rest of a contact record. Pattern field reference — the full pattern syntax, including letters and regex. QA test data guide — building fixtures that cover the unhappy paths. Last updated 6 September 2026 ============================================================================ # Random User API Alternative — Generate Locally, No Fetch URL: https://fundata.dev/randomuser-api-alternative ============================================================================ Home / Random user API alternative A random user API alternative that needs no API A random user API gives you profiles over HTTP. This gives you the same kind of records as a file, generated on your machine — which changes what can go wrong. What a runtime fetch costs you Fetching random users at runtime is the fastest way to fill a demo, and it quietly adds a third-party service to the list of things that can break your build. Four failure modes that a generated file does not have: The network. A test that fetches its own fixtures fails on a train, in a locked-down CI runner, and on the day the service has an outage. Rate limits. Fine for a demo, less fine when a suite runs a thousand times a day from one egress IP. Non-determinism. Random data fetched fresh each run means a failing test may not reproduce — the worst kind of flake, because it looks like a real bug. Drift. The response shape is the service's to change, and it does not change on your schedule. A generated file has none of those. It is bytes in your repository: the same on the train, in CI, and in two years. And because generation is seeded, you can regenerate exactly the same set instead of storing it, if you would rather keep the seed than the file. You choose the fields A user API returns the profile it was designed to return. Here you decide what a "user" has: name, email, phone, address, avatar URL, job title, signup timestamp, an active boolean, an account balance, a UUID — plus anything else your schema needs, named the way your database already names it. The output is a users table for your schema rather than a generic profile you then have to map. Side by side Generating records locally, or fetching them Fun Data Playgrounda random user API How you get recordsGenerated on your machine, then downloadedFetched over HTTP when your code runs Network neededOnly to load the page onceOn every call Rate limitsNoneWhatever the service sets Repeating a run exactlySame seed, same rowsFresh random data each request Which fields you getThe ones your schema declaresThe profile the service returns PhotographsAvatar URLs pointing at a placeholder service, not photographsSome services serve a photo set Who can change the response shapeNobody — the file is yoursThe service, on its own schedule Structural differences only — how each tool is used, not what it currently offers. Check the other tool's own documentation before deciding. Where an API is the better answer A live demo that should look different on every load, with no fixture to bundle. Portrait photographs. Some user APIs serve a photo set; the avatar field here is a URL to a placeholder-style service, not a photograph. You are already offline-tolerant and the convenience of a one-line fetch outweighs the dependency. Server-side generation on demand, which a static page cannot do. Tools change. This page sticks to structural differences — how each one is used rather than what it currently offers — but check the current documentation of whichever you are comparing rather than trusting any comparison page, including this one. Replacing a fetch with a fixture The usual migration is three steps. Build a schema whose field names match the response shape you are already consuming — dots make it nested, so name.first and name.last reproduce a nested name object. Export as JSON and save it in your test fixtures. Then replace the fetch with an import, or keep the fetch and intercept it: Playwright and Cypress both cover serving a fixture in place of a real request, which is usually the smaller change. The data is safe to publish Every value is synthetic. Email addresses use domains reserved by RFC 2606 and RFC 6761 that cannot receive mail, so a fixture that leaks into a mailing list sends nothing to anybody; IP addresses come from documentation ranges. That is worth more than it sounds for demo data, which has a habit of ending up in screenshots and public repositories. The methodology page documents each choice. Common questions Why generate locally instead of calling an API?Because a runtime fetch adds a third-party service to the things that can break your build: the network, rate limits, non-reproducible random data and a response shape that is not yours to control. A generated file has none of those. Can I get the same users again?Yes. Enter a seed and the same schema produces byte-identical output every time, so you can either commit the file or keep the seed and regenerate it. Does it include profile photos?Not photographs. There is an avatar URL field pointing at a placeholder-style image service. If you need portraits specifically, that is a reason to use a photo-serving API. Can I match the response shape I already consume?Yes. Name the fields to match, and use dots for nesting — name.first and name.last produce a nested name object — so the fixture drops into code written against the API shape. Are the email addresses safe to use?Yes. They use reserved documentation domains that cannot receive mail, so a fixture that escapes into a real mailing list sends nothing to anyone. Related /api-mock-data — using the generated records as mock API responses. /random-name-generator — the name fields on their own, with a live example. /playwright-test-data — intercepting a request and serving a fixture instead. /methodology — what the values are made of and what they are not safe for. /faker-js-alternative — the same trade against a library rather than an endpoint. /mockaroo-alternative — and against a hosted generator. /json-generator-alternative — and against a template-based JSON generator. Last updated 6 September 2026 ============================================================================ # Sales Sample Data — Free Orders & Revenue Test Dataset URL: https://fundata.dev/sales-sample-data ============================================================================ Sales sample data An orders dataset built to survive a pivot table: enough rows, a clean time axis, categories that repeat, and revenue that varies the way real revenue does. Generate sales data →Excel & Sheets guide Ready-made files to download Generated from the schemas below with a fixed seed and committed to the site, so a link to one of these files keeps returning the same bytes. They are public domain (CC0) — use them in a tutorial, a test suite or a course without asking. Need different columns, more rows or another format? Build it above. FileRowsColumnsFormatSize orders-100.csv1009CSV13 KB orders-100.json1009JSON30 KB orders-100.sql1009SQL16 KB orders-1000.csv1,0009CSV127 KB orders-1000.json1,0009JSON304 KB orders-1000.sql1,0009SQL154 KB The schema Column Field type Options ------------------------------------------------------------ order_id Row Number order_date Date from 2024-01-01, sequential customer_name Full Name region Custom List North America, Europe, APAC, LATAM country Country product Custom List your real product names category Custom List Hardware, Software, Services quantity Integer min 1, max 12 unit_price Price min 9.99, max 899.00 currency Currency Code status Custom List paid, pending, refunded, cancelled sales_rep Custom List 8–12 repeating names order_id,order_date,region,product,quantity,unit_price,currency,status 1,2024-01-01,Europe,Standing Desk,2,349.00,EUR,paid 2,2024-01-01,North America,License Pack,1,129.99,USD,paid 3,2024-01-02,APAC,Onboarding,1,899.00,USD,pending Getting a chart that looks like a business Most generated sales data produces a flat, noisy line, because every column is an independent uniform draw. Four adjustments fix that: Sequential dates. Set the Date field's order to sequential so rows spread evenly across the period. Random dates leave gaps and pile-ups that make a daily chart unreadable. Weighted categories. Repeat values inside a Custom List. Real revenue is concentrated — a handful of products and one or two regions dominate. An even split across four regions is the least realistic thing a sales dataset can do. Skewed order values. Number (Normal Dist.) for quantity or price gives you a believable middle with genuine outliers, rather than as many twelve-unit orders as single-unit ones. A status mix that reflects reality. Repeat paid a dozen times against one refunded and one cancelled. A dataset that is 25% refunds will make every dashboard you build look wrong. Line totals Quantity and unit price as independent columns are fine until something has to sum them. If your dashboard needs a line_total, do not generate it as a third random column — it will not equal quantity × unit price, and the first person to check will lose trust in the whole dataset. Use a Formula field so the total is derived from the other two columns in the same row. The same applies to tax, discount and net totals: derive them, never draw them. Currencies A Currency Code column alongside a Price column is realistic and slightly dangerous: summing revenue across mixed currencies without conversion produces a meaningless number, and dashboards do it constantly. That makes it a genuinely useful thing to have in a test dataset — if your BI tool happily adds euros to yen, you have found a real bug. If you want simple totals instead, pin the currency to a single value with a one-item Custom List. Sizing For a pivot table or a dashboard demo, 5,000 to 20,000 rows is the sweet spot: enough for grouping to be meaningful, small enough to recalculate instantly. Go to 100,000 when you are testing load performance, refresh time or the point at which a chart stops rendering. Spreadsheet row limits matter here — the Excel guide covers them. Export targets CSV for Excel, Sheets, Power BI and Tableau. TSV if product names contain commas and you would rather not deal with quoting. SQL to seed an orders table directly — see PostgreSQL or MySQL. JSON if the dashboard reads from a mock API instead of a file. Common questions How do I make the revenue chart look realistic?Set the date field to sequential order, weight categories by repeating values in a Custom List, and use the Number (Normal Dist.) field for order values. Independent uniform draws produce a flat, noisy line that looks nothing like real revenue. How do I get line totals that actually add up?Use a Formula field so line_total is computed from quantity and unit_price in the same row. A separately generated column will not match, and anyone who checks will stop trusting the dataset. How many rows should I generate?Around 5,000 to 20,000 for dashboards and pivots — enough for grouping to be meaningful and small enough to recalculate instantly. Use the full 100,000 for load and refresh testing. Can I use my own product names?Yes. Put them in a Custom List field, repeating the ones that should appear more often. The built-in Product Name field is there for when you do not care about the specific names. Should I mix currencies?Only deliberately. Mixed currencies without conversion make totals meaningless — which is useful if you are testing whether your BI tool notices, and unhelpful if you just want a clean demo. Pin to one currency with a single-item Custom List for the latter. Related Employee sample data — the HR equivalent. Random date generator — building a clean time axis. Excel sample data — pivots, imports and regional settings. Sample CSV files — other ready-made schemas. Last updated 6 September 2026 ============================================================================ # Sample CSV Files — Download Free Test CSV Data URL: https://fundata.dev/sample-csv-files ============================================================================ Sample CSV files Most sample CSVs you find online are the wrong size with the wrong columns. Here you pick both, and download the file a few seconds later. Build a sample CSV →CSV format details Ready-made files to download Generated from the schemas below with a fixed seed and committed to the site, so a link to one of these files keeps returning the same bytes. They are public domain (CC0) — use them in a tutorial, a test suite or a course without asking. Need different columns, more rows or another format? Build it above. FileRowsColumnsFormatSize users-100.csv1009CSV9 KB users-1000.csv1,0009CSV95 KB employees-100.csv1008CSV9 KB employees-1000.csv1,0008CSV93 KB orders-100.csv1009CSV13 KB orders-1000.csv1,0009CSV127 KB sensor-readings-100.csv1008CSV10 KB sensor-readings-1000.csv1,0008CSV95 KB Four starting points The generator ships with schema templates that cover the shapes people usually want a sample file for. Open the generator, pick one, set the row count, download: Users — id, name, email, city, signup date. The default import test. Orders — order id, customer, product, quantity, price, status, date. Numeric and categorical columns together. Employees — name, job title, department, hire date, salary. See employee sample data for the full breakdown. Sensors — device id, timestamp, reading. A time series, useful with sequential dates. Every template is a starting point rather than a fixed file — add, remove and rename columns before exporting. What a small sample looks like id,first_name,last_name,email,city,country,signup_date,is_active 1,Elena,Rossi,elena.rossi7@example.org,Lyon,France,2024-03-18,true 2,Marco,Schneider,marco_schneider@example.com,Hamburg,Germany,2024-07-02,true 3,Aisha,Yılmaz,aisha.yilmaz@example.net,Izmir,Türkiye,2025-01-27,false 4,Priya,Nair,priya.nair42@example.org,Osaka,Japan,2025-04-11,true Note the third row: a non-ASCII city and surname in a UTF-8 file. That single row finds more import bugs than the other three combined, which is why it is worth keeping a sample file honest rather than tidy. Choosing a row count Sample size should follow what you are testing, and the common mistake is picking a round number that tests nothing: 10–100 rows — reading the file by eye, checking column mapping, writing a parser test. 1,000–5,000 rows — pivot tables, charts, anything where distribution matters. 50,000–100,000 rows — import performance, pagination, memory behaviour, and finding the point where your UI stops being usable. 100,000 rows is the per-export ceiling. For more, export several times with different seeds and concatenate, stripping the header from every file after the first. Making the sample awkward on purpose A sample file that imports cleanly proves very little. The controls that make a file useful as a test: Blank % on optional columns produces empty cells, so you find out whether your importer distinguishes empty string from null. Text fields — a Sentence or Words column will eventually contain a comma, which forces quoting and exercises your parser properly. Leading zeros — a Pattern field like 0##### is the fastest way to prove whether your pipeline treats identifiers as numbers. Header row off — for testing an importer that expects positional columns. Quoting follows RFC 4180: fields containing a comma, quote or newline are quoted, and embedded quotes are doubled. The CSV format page shows the anatomy. Same file every time Enter a seed and the download is byte-identical on every machine and every run. That is what makes a generated sample suitable for committing next to a test: the assertion can name a value in row 3 without the file drifting underneath it. Change the seed and you get a different file of the same shape, which is a good way to check a parser test passes for the right reason. Other formats from the same schema Nothing about the schema is CSV-specific. The same columns export as TSV when your data contains commas and tabs are cleaner, JSON for a frontend fixture, NDJSON for streaming tools, SQL for a seed script, or XML for an integration test. Common questions Are the sample CSV files really free?Yes. No account, no signup and no row-count paywall — generation runs entirely in your browser, so there is no server cost to meter. How large can a sample CSV be?Up to 100,000 rows per export. For larger files, export several times with different seeds and concatenate them, removing the header from all but the first file. Can I choose the columns?Yes — that is the main difference from a static sample file. Start from a template or an empty schema and add any of the 68 field types, renaming columns to match your target. Is the file UTF-8?Yes. The export is UTF-8 and includes non-ASCII names and cities by default, which is deliberate — those rows are what expose encoding bugs. Can I get the same file again later?Enter a seed before exporting. The same seed and schema always produce a byte-identical file. Related Sample JSON data — the same schemas as typed JSON. Excel sample data — importing without Excel mangling your columns. Employee sample data and sales sample data. CSV format reference — quoting, delimiters and compatibility. Last updated 6 September 2026 ============================================================================ # Sample JSON Data — Free Test JSON Files & Fixtures URL: https://fundata.dev/sample-json-data ============================================================================ Sample JSON data Realistic JSON fixtures with actual types — numbers as numbers, booleans as booleans, nulls where you asked for them — and nested objects when a flat record is not enough. Build sample JSON →JSON format details Ready-made files to download Generated from the schemas below with a fixed seed and committed to the site, so a link to one of these files keeps returning the same bytes. They are public domain (CC0) — use them in a tutorial, a test suite or a course without asking. Need different columns, more rows or another format? Build it above. FileRowsColumnsFormatSize users-100.json1009JSON26 KB users-1000.json1,0009JSON259 KB employees-100.json1008JSON26 KB employees-1000.json1,0008JSON259 KB orders-100.json1009JSON30 KB orders-1000.json1,0009JSON304 KB sensor-readings-100.json1008JSON26 KB sensor-readings-1000.json1,0008JSON256 KB Typed, not stringly typed The common failing of hand-written JSON fixtures is that everything ends up a string, so the fixture silently disagrees with the API it is standing in for. The JSON export emits each field as its natural type: [ { "id": 1, "name": "Elena Rossi", "email": "elena.rossi7@example.org", "age": 34, "balance": 1284.50, "is_active": true, "referred_by": null, "created_at": "2024-03-18T09:14:22Z" } ] age is a number, is_active is a boolean, and referred_by is null because that field has a Blank % set. A frontend that does user.age > 18 behaves the same against this fixture as against the real endpoint — which is the entire point of a fixture. Nested objects Real API responses are rarely flat. Name a field with dots and the export nests it: Field name JSON output ------------------------------------------------ id "id": 1 address.city "address": { "city": "Lyon", address.country "country": "France" } account.plan "account": { "plan": "pro", account.seats "seats": 12 } That covers most response shapes without any post-processing. For anything more elaborate — a wrapping envelope, pagination metadata, an array inside each record — generate the array and reshape it in a couple of lines with jq or a script; the mock API guide shows the common envelope patterns. One array or one object per line? The JSON export gives you a single array, which is what a browser fetch, a fixture file and a mock server all expect. NDJSON gives you one object per line, which is what streaming tools want — jq without slurping, BigQuery loads, Elasticsearch bulk ingest, and anything that should not hold the whole file in memory. Same schema, different serializer. Fixtures that survive a code review Three things make generated JSON worth committing rather than regenerating: A seed. Byte-identical output every run means the fixture file has a stable diff, and a test can assert on data[0].email without a snapshot dance. Deliberate nulls. Set Blank % on every field the API declares optional. Most frontend crashes in this area come from a field the fixture always populated and the API sometimes does not. Enough rows to page. Twenty records will not reveal a broken pagination control. Generate past your page size. Loading it into a mock server # json-server: an instant REST API over the file npx json-server --watch users.json --port 3001 # Node: import it in a test const users = require('./fixtures/users.json'); # jq: check the shape before trusting it jq '.[0] | keys' users.json jq 'map(select(.is_active)) | length' users.json Escaping and encoding Output is UTF-8 and valid JSON, with quotes, backslashes and control characters escaped as the specification requires. Text fields deliberately include non-ASCII names, which is worth keeping — a fixture consisting only of ASCII will not tell you whether your pipeline mangles Yılmaz somewhere between the database and the browser. Common questions Is the JSON typed or all strings?Typed. Numbers export as JSON numbers, booleans as true/false, and fields with a Blank % set produce null rather than an empty string — so a fixture behaves like the API it replaces. Can I generate nested JSON objects?Yes. Name a field with dot notation, such as address.city, and the export nests it under an address object. Multiple fields sharing a prefix group together. What is the difference between the JSON and NDJSON exports?JSON gives one array containing every record — right for fetch, fixtures and mock servers. NDJSON gives one object per line — right for streaming tools, jq, BigQuery and Elasticsearch bulk loads. How many records can I generate?Up to 100,000 per export, generated in your browser with no account required. Can I get the same JSON file every time?Yes. Enter a seed and the output is byte-identical on every run and every machine, which is what makes the file safe to commit alongside tests. Related Mock API data guide — envelopes, response states and contract tests. Sample CSV files — the same schemas as flat files. NDJSON export — streaming-friendly output. Playwright test data — using JSON fixtures in a test suite. Last updated 6 September 2026 ============================================================================ # SQL Test Data Generator — Fun Data Playground URL: https://fundata.dev/sql ============================================================================ SQL test data generator Turn a schema into ready-to-run INSERT statements — with an optional CREATE TABLE whose column types are inferred from your fields. Seed a dev database in one command. Generate SQL inserts → Browse field types What the output looks like CREATE TABLE customers ( id INTEGER, full_name TEXT, email TEXT, is_active BOOLEAN ); INSERT INTO customers (id, full_name, email, is_active) VALUES (1, 'Elena Rossi', 'elena.rossi7@example.org', TRUE); Why SQL export? Sometimes the fastest way to a populated dev database is a plain .sql file: no ETL tool, no ORM seeder, just psql -f or mysql <. The generator writes standard INSERT statements, escapes quotes correctly, renders blanks as NULL, and can prepend a CREATE TABLE whose column types (integer, decimal, boolean, text) are inferred from your field types. SQL-specific options Table name — set it once; it is used in both the CREATE TABLE and every INSERT. Create table toggle — turn it off when the table already exists and you only want rows. Seed — the same seed regenerates the exact same script, so your whole team seeds identical databases. Applying the script # PostgreSQL — stop at the first error instead of ploughing on psql -d mydb -v ON_ERROR_STOP=1 -f fundata_1000_rows.sql # MySQL / MariaDB mysql -u dev -p mydb < fundata_1000_rows.sql # SQLite sqlite3 dev.db < fundata_1000_rows.sql # Dry run inside a transaction you never commit psql -d mydb -1 -c '\i fundata_1000_rows.sql' -c 'ROLLBACK;' # Docker-based CI: seed a throwaway database on startup docker run -d -e POSTGRES_PASSWORD=x \ -v "$PWD/fundata_1000_rows.sql:/docker-entrypoint-initdb.d/seed.sql" postgres:17 Database compatibility and safety The generated script uses portable SQL literals: text is single-quoted with embedded quotes escaped, numbers remain unquoted, booleans use TRUE/FALSE and missing values become NULL. It is designed for disposable development and test databases. Review inferred column sizes, constraints and dialect-specific identifiers before applying a script to an existing schema; the tool never connects to your database. Portable SQL is a smaller language than any one dialect, so a few things are worth checking before a script meets a schema you care about: BOOLEAN is not universal. PostgreSQL has a real boolean type; MySQL treats it as TINYINT(1), and SQLite stores 0 and 1. The TRUE/FALSE literals work in all three, but the column type in CREATE TABLE may need adjusting. No primary keys, indexes or constraints are emitted. The inferred CREATE TABLE is a plain column list. That is deliberate — guessing a key would be wrong more often than right — but it means the script populates a table rather than defining a schema you should keep. Identifiers are unquoted. A table or column name that collides with a reserved word, or that needs case preserved in PostgreSQL, has to be quoted by hand. Sticking to lowercase names with underscores avoids the whole question. Apostrophes are the escaping case that matters. Names like O'Brien are emitted as 'O''Brien' — a doubled quote, which is the SQL standard and works everywhere. Generating a batch of Irish surnames is a quick way to prove a downstream tool handles them. Rows are batched into multi-row INSERTs. That is far faster than one statement per row, but it also means a single bad row rejects its whole batch. Run with ON_ERROR_STOP so a partial load doesn't look like a success. Common SQL questions Which databases can use the generated SQL?The simple inserts work with PostgreSQL, MySQL and SQLite in common schemas. Dialect-specific types, quoted identifiers, auto-increment clauses and constraints may need a small edit. Can I generate rows without CREATE TABLE?Yes. Disable the Create table option and the export contains only INSERT statements for the table name you provide. Are primary keys, indexes or foreign keys included?No. The inferred CREATE TABLE is a plain column list — guessing a key or a relationship would be wrong more often than right. Define the schema in your migrations and use this script to populate it, ideally with Create table switched off. How are apostrophes in names handled?By doubling, which is the SQL standard: O'Brien becomes 'O''Brien'. This works in PostgreSQL, MySQL and SQLite alike, and no backslash escaping is used, since that is a MySQL-specific extension. Is it safe to run against a real database?Treat it as a development and test tool. The script only ever inserts, and never connects to anything itself, but it emits no transaction wrapper of its own — run it inside one, or against a disposable database, so a partial load can be rolled back. Why is my import slow for 100,000 rows?Usually indexes and per-statement commits rather than the script. Loading into a table with its indexes created afterwards, inside a single transaction, is dramatically faster. For bulk loading specifically, CSV with COPY or LOAD DATA beats INSERT statements by a wide margin. Plan realistic schemas and repeatable seeds with the database test-data guide. Other formats The same schema exports to all six formats — switch with one dropdown: CSV, TSV, JSON, NDJSON, XML. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # TSV Test Data Generator — Fun Data Playground URL: https://fundata.dev/tsv ============================================================================ TSV test data generator The same 68 realistic field types, exported as tab-separated values — ideal for clipboard pastes into spreadsheets and for tools that choke on comma quoting. Generate TSV data → Browse field types What the output looks like order_id product quantity unit_price b3c9a1f2-6d4e-4a7b-9c1d-2f8e5a6b7c8d Ergonomic Bamboo Keyboard 2 129.99 7f2e0d94-1c3b-4e5a-8f6d-9a0b1c2d3e4f Rustic Steel Lamp 1 54.50 Why TSV? Tab-separated values sidestep CSV's biggest annoyance: commas inside the data. Because real-world values almost never contain tabs, TSV rows rarely need quoting at all — which makes the files trivially cut-, awk- and paste-friendly, and means a copied block drops straight into Excel or Google Sheets with columns intact. Where TSV shines Spreadsheet pastes — copy the raw output and paste it directly into a sheet; every tab becomes a column. Unix pipelines — cut -f2, sort -k3 and friends work without a CSV parser. MySQL imports — LOAD DATA INFILE defaults to tab separators, so TSV loads with zero extra flags. Header row & Blank % — same controls as CSV: toggle the header, inject empty cells to simulate missing data. Working with TSV on the command line This is where TSV earns its keep: because the delimiter never appears inside a value, the standard Unix text tools are enough and no CSV parser is involved. # Average the fourth column, skipping the header awk -F'\t' 'NR>1 { sum += $4 } END { print sum/(NR-1) }' fundata_1000_rows.tsv # Pull two columns out, keeping the tab separator cut -f2,4 fundata_1000_rows.tsv # Sort by the numeric third column, descending sort -t$'\t' -k3,3nr fundata_1000_rows.tsv # Count rows per value of column 2 tail -n +2 fundata_1000_rows.tsv | cut -f2 | sort | uniq -c | sort -rn # MySQL: tab is already the default, so no FIELDS clause is needed LOAD DATA LOCAL INFILE 'fundata_1000_rows.tsv' INTO TABLE orders IGNORE 1 ROWS; TSV compatibility and edge cases Every record occupies one line and every field is separated by a literal tab. Text is encoded as UTF-8, so non-English names survive spreadsheet and command-line workflows. Because tabs are rare in ordinary values, TSV avoids much of CSV's quoting overhead; when a consumer requires RFC-style comma-separated input, switch to CSV. Blank percentages generate empty fields between delimiters, making missing-value tests easy to spot. TSV has no agreed escaping rule. This is the real trade-off against CSV. RFC 4180 tells every CSV parser what a quote means; TSV has no such document, so tools disagree about what a tab inside a value would even look like. The format works because that case is avoided, not because it is handled. Empty field or missing column? Two consecutive tabs mean an empty value. A row with fewer tabs than the header has fewer columns — a different defect, and one some parsers pad silently instead of rejecting. Blank % produces the first case so you can confirm your importer tells them apart. Trailing whitespace is invisible and significant. A value ending in a space looks identical in a terminal but compares unequal. Piping through cat -A makes tabs (^I) and line ends ($) visible when a diff refuses to make sense. Locale changes how sort behaves. Sorting names with accents gives different results under LC_ALL=C than under a UTF-8 locale. Generating international names is the quickest way to catch a pipeline that assumes ASCII ordering. TSV has no escape hatch — and that is the point The structural difference between TSV and CSV is not the delimiter, it is what happens when a value contains one. CSV answers with quoting rules: wrap the field, double any internal quote, and a parser has to implement a small state machine to read it back. The classic tab-separated format has no such mechanism at all — a tab inside a value simply cannot be represented. That sounds like a weakness and is usually a strength. Splitting a TSV line is line.split('\t'), correct and complete, with no state machine, no quoting edge cases and no ambiguity about what a lone quote character means. Every field is exactly what lies between two tabs. That is why the format survives in bioinformatics, log processing and command-line pipelines, where the parser is often three lines of awk. The trade is that you have to know your data contains no tabs and no newlines. Generated fields here never contain a tab, so the export is well-formed by construction; if you later merge in text from elsewhere — a description field, a pasted comment, anything a user typed — check it first. A single stray tab shifts every subsequent column on that row, and the failure is silent: the file still parses, it is just wrong from that column onwards. If your data genuinely might contain tabs, use CSV and accept the quoting, or NDJSON where the escaping is the serializer's problem rather than yours. Common TSV questions Is TSV the same as tab-delimited text?Yes. TSV, tab-separated values and tab-delimited text describe the same simple table format: rows are lines and columns are separated by tab characters. When should I choose TSV instead of CSV?Choose TSV for spreadsheet pastes, Unix tools and data containing many commas. Choose CSV when an importer explicitly expects comma delimiters or RFC-style quoting. What happens if a generated value contains a tab?None of the field types emit tab characters, so the case doesn't arise in practice. That is precisely why TSV can skip quoting: the guarantee comes from the data, not from an escaping rule. If you paste your own values into a custom list, keep them tab-free. Can I paste TSV straight into Excel or Google Sheets?Yes, and this is TSV's strongest use. Copy the output and paste into a sheet — every tab becomes a column boundary with no import dialog at all. CSV pasted the same way lands in a single column. Does TSV support explicit nulls?No. Like CSV, TSV has only empty strings, and each importer decides whether an empty field becomes NULL. When the distinction matters, use JSON or NDJSON, which emit a real null. For a practical spreadsheet workflow, read the Excel and Sheets sample-data guide. Other formats The same schema exports to all six formats — switch with one dropdown: CSV, JSON, NDJSON, SQL, XML. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # Synthetic Data Field Types & Examples — Fun Data Playground URL: https://fundata.dev/types ============================================================================ Field type reference Every generator available in the schema builder, grouped by category. Options marked in mono can be tuned per field, and every field additionally accepts a Blank % to inject nulls and a Unique toggle that retries until every value in the column differs — if a type cannot produce enough distinct values, the builder warns you and the remaining duplicates are kept. Not sure which fields belong together? Start from a test-data workflow for databases, mock APIs, QA automation or spreadsheets. Safety characteristics and documentation-only ranges are explained in the data methodology. BasicsPersonLocation InternetBusinessDate & TimeText Swipe categories for more → Basics TypeDescription & optionsExample Row NumberSequential counter. Options: start.1, 2, 3… IntegerUniform random whole number. Options: min, max.42 DecimalRandom decimal. Options: min, max, decimals.483.07 Booleantrue or false. Options: true % — the probability of true (default 50).true UUID v4RFC 4122 version-4 identifier.b3c9a1f2-6d4e-4a7b-9c1d-2f8e5a6b7c8d FormulaMockaroo-compatible formula evaluated per row. Formula can be its own field type, or the f(x) button can transform any generated field using this. Fields are evaluated top-to-bottom.if quantity > 5 then (unit_price * quantity).round(2) else unit_price end Number (Normal Dist.)Gaussian (bell-curve) number via Box-Muller — more realistic than uniform for ages, prices, latencies. Options: mean, std dev, optional min/max clamp, decimals.97.4 Dedicated guides: UUID generator · sample CSV files · SQL export Formula quick reference Reference earlier fields directly, or use field("Field Name") for names containing spaces. Prefix helper field names with __ to keep them available to formulas while omitting them from every export. Logic: if / elsif / else / end, postfix if and unless, case / when, and / or, comparisons and ternary expressions. Functions: random, generate, concat, upper, lower, format, pad, base64, hex, uuid_v5, normal_dist, date, date_diff, day, month, year, time, epoch, now, date offsets from years through seconds, code (returns its argument as a string), and naughty(value, percent) — swaps in a hostile test string (script tag, SQL fragment, control characters) at the given percentage, for input-hardening tests. Values and methods: arrays, hashes, indexing, nil?, empty?, string case/search/replace methods, numeric rounding and predicates, plus common array methods such as compact, uniq, sort, sum and join. Person TypeDescription & optionsExample First NameGiven names. Options: locale (any, en, es, de, it, tr, jp, in, ar). First Name, Last Name, Full Name and Email Address stay coherent within a row — the email matches the row's name.Elena Last NameFamily names, coherent with the row's other name fields. Options: locale.Yılmaz Full NameFirst + last name from one person, matching the row's First/Last Name columns. Options: locale.Marco Rossi GenderInclusive set of gender labels.Non-binary Usernameadjective_noun + number.swift_falcon42 PasswordRandom unambiguous characters. Options: length.kR7!mQ2vX9pZ Email AddressBuilt from the row's First/Last Name (transliterated to ASCII) on safe example domains.elena.rossi7@example.org Phone NumberPattern-driven. Options: format (# = digit).+1 (555) 014-2831 Job TitleCommon roles across tech and business.QA Engineer DepartmentTypical org departments.Quality Assurance Avatar URLPlaceholder avatar image URL.https://i.pravatar.cc/150?u=48293017 AgeWhole-number age. Options: min, max.34 BirthdateDate of birth derived from an age range. Options: min age, max age, format.1991-04-17 Dedicated guides: random names · fake emails · phone numbers · employee data Location TypeDescription & optionsExample Country50 country names. Country, Country Code, City and Full Address stay coherent within a row — a row never pairs a city with the wrong country. A second field of the same kind draws independently.Netherlands Country Code (ISO)ISO 3166-1 alpha-2 code, matching the row's Country.NL City~110 world cities, drawn from the row's country.Izmir State (US)US state name.Oregon State Abbrev (US)Two-letter US state code.OR Street AddressNumber + street + suffix.2847 Maple Avenue Zip / Postal CodePattern-driven. Options: format.94103 Full AddressStreet, city, zip and country in one line; the city and country match the row's other location fields.193 Cedar Lane, Vienna 20481, Austria Latitude−90 to 90, six decimals.48.208176 Longitude−180 to 180, six decimals.16.373819 Dedicated guides: fake addresses · sales data with shipping Internet TypeDescription & optionsExample Domain NameCompany-style domain on a reserved documentation TLD.novadynamics.test URLhttps URL with two path segments on a reserved documentation TLD.https://emberlabs.test/lorem/ipsum IPv4 AddressRFC 5737 documentation-only address from 192.0.2.0/24, 198.51.100.0/24 or 203.0.113.0/24.192.0.2.42 IPv6 AddressRFC 3849 documentation-only address from 2001:db8::/32.2001:db8:85a3:0000:0000:8a2e:0370:7334 MAC AddressColon-separated uppercase hex.0A:1B:2C:3D:4E:5F Hex Color#RRGGBB.#e8590c Color NameHuman-readable color word.turquoise User AgentRealistic modern browser UA strings.Mozilla/5.0 (Windows NT 10.0…) Chrome/126.0 Semvermajor.minor.patch version string.2.14.7 MIME TypeCommon IANA media type.application/json Hash (hex)Deterministic-looking hex digest — not a real cryptographic hash. Options: length (8–64), optional source field to derive it from another field above it (same source + seed ⇒ same hash).9f2c7a1e4b8d0f36 Dedicated guides: sample JSON data · mock API fixtures · MongoDB documents Business TypeDescription & optionsExample Company NamePrefix + suffix, sometimes a legal form.Zenith Analytics GmbH Product NameAdjective + material + noun.Ergonomic Bamboo Keyboard PriceTwo-decimal amount. Options: min, max.129.99 Currency CodeISO 4217 code.EUR IBAN (fake)Country-length-shaped but not checksum-valid or bank-valid. Options: country.DE89 3704 0044 0532 0130 00 Credit Card # (test)Official payment-gateway test PANs only; never use for real payments.4111111111111111 Credit Card TypeMajor card network name.Mastercard Crypto CoinPopular ticker symbols.ETH EAN-13 Barcode12 random digits plus a correct EAN-13/ISBN-style check digit.4006381333931 ISBN-13978-prefixed 13-digit code with a correct check digit.9781234567897 Dedicated guides: test card numbers · fake IBANs · sales sample data Date & Time TypeDescription & optionsExample DateDate in a range. Options: from, to, format, order (random, or sequential — evenly spaced across the range in row order, for time-series fixtures).2024-11-03 Time24h clock. Options: seconds.14:07:52 Datetime (ISO 8601)UTC timestamp in a range. Options: from, to, order.2025-03-18T09:41:26Z Unix TimestampSeconds since epoch. Options: from, to, order.1742291286 Day of WeekMonday–Sunday.Thursday MonthJanuary–December.March TimezoneIANA timezone name.Europe/Berlin Dedicated guides: random dates & timestamps · NDJSON for time series Text TypeDescription & optionsExample WordSingle lorem word.voluptate WordsSpace-separated words. Options: count.magna aliqua enim Sentence6–14 word capitalized sentence.Dolore magna aliqua enim minim veniam quis. ParagraphMultiple sentences. Options: sentences.Lorem ipsum dolor amet. Sed tempor… Custom ListYour own values. Options: values (plain a, b, c or weighted a:5, b:1), mode (random, sequential, or weighted — weighted honors the value:weight ratios).pending My DatasetDraws from a reusable value list saved under My datasets on the builder (one value per line, up to 10,000 values). Datasets are reusable across schemas, and signing in syncs them across devices. Options: dataset (which list), mode (random or sequential).acme-widget Pattern# digit, ? A–Z, ~ a–z; other chars kept.SKU-4821-QF Regex PatternGenerates strings matching a constrained regex subset: literals, \d \w \s (+ negations), ., [classes], (groups), alternation |, quantifiers * + ? {n} {n,} {n,m}. No backreferences or lookaround. Options: pattern.ABC-4821 Slugword-word-number, kebab-case.amber-falcon-482 EmojiSingle common emoji.🚀 Dedicated guides: QA edge cases · Playwright fixtures · Cypress fixtures Missing a type you need? Grab a Pattern / Custom List field, which together cover most bespoke formats. Last updated 6 September 2026 ============================================================================ # Synthetic Test Data Use Cases — Fun Data Playground URL: https://fundata.dev/use-cases ============================================================================ Synthetic test data use cases Start from the system you need to test, then choose a schema, seed and export format that make the fixture useful—not merely random. Build a dataset →Read the guide Every workflow below is the same three steps — describe the columns, fix a seed, pick a serializer — and the differences are entirely in the details: which fields, how many rows, where the nulls go, and which format the destination actually wants. Those details are what separate a fixture that finds bugs from one that just fills a table. Database seeding Populate local, CI and staging databases with repeatable customers, orders, employees or transactions. Use a fixed seed so every developer starts from the same rows, then export portable SQL inserts or a CSV for bulk loading. The decision that matters most is size. Up to a few thousand rows, SQL inserts are convenient and self-contained — one file, checked into the repository, applied with a single command. Past that, CSV plus a bulk loader is commonly an order of magnitude faster, because COPY and LOAD DATA bypass per-statement overhead entirely. The second decision is relationships. Columns are generated independently, so a foreign key will not match a parent row by accident. Generating a related set is a deliberate two-pass job: export the parent table, then paste its key column into a Custom List field on the child schema so every reference resolves. Repeating some parent IDs in that list also gives you the hot-key skew that a uniform draw never produces. Plan database test data → · PostgreSQL · MySQL · MongoDB Mock APIs and frontend development Create typed JSON objects with realistic names, emails, prices, dates, booleans and nulls. Serve the fixture from a local route or test interceptor while a backend is unfinished, or use it to exercise empty, partial and long-value UI states. Types are the part hand-written fixtures usually get wrong. A file where every value is a string will pass your tests and disagree with the real endpoint, and the disagreement surfaces in production. The JSON export emits numbers as numbers and booleans as booleans, and any field with a Blank % set produces a real null — which is the value most frontend crashes in this area are actually about. Generate several sizes from one schema: a three-row file for layout, one past your page size for pagination, and an empty array for the empty state. Because they share a schema they stay consistent with each other, which a set of hand-written fixtures never manages for long. Create mock API data → · Sample JSON data QA automation and regression fixtures Combine deterministic seeds with Blank %, unique values, patterns and custom lists. The resulting fixture stays stable across Playwright, Cypress and unit-test runs while still covering missing values and format boundaries. The reason to generate once and commit, rather than call a fake-data library at runtime, is that runtime randomness turns a real failure into an unreproducible flake. A test that fails once in forty runs gets a retry rather than an investigation, and the bug ships. A seeded file gives you the same realistic variety on every machine, and a known value you can name in an assertion instead of a snapshot you have to trust. Randomness also finds the interesting cases slowly. Generate the ordinary rows in bulk, then add the specific awkward ones — an apostrophe in a surname, a value at the exact column width, a missing optional field, a leap day — through a Custom List, so a red test points at something nameable. Design QA test data → · Playwright · Cypress Excel, Sheets and BI samples Generate clean tabular samples for formulas, dashboards, import mapping and training. CSV provides broad importer compatibility; TSV is convenient for direct clipboard pastes and comma-heavy text. Spreadsheets are where uniform random data looks most obviously fake. A pivot table over four equally sized regions and evenly spread dates produces a flat, featureless chart. Three adjustments fix it: set the date field to sequential order so the time axis is clean, weight categories by repeating values inside a Custom List, and use the Number (Normal Dist.) field for amounts so there is a believable middle with real outliers. The other recurring problem is Excel rewriting your data on import — leading zeros stripped from IDs, dates reinterpreted according to regional settings, long numbers turned into scientific notation. Generating a column of zero-padded identifiers is the fastest way to find out whether your import path has that problem. Generate spreadsheet sample data → · Sales sample data · Employee sample data Data pipelines and demos Use NDJSON for line-oriented ingestion, CSV for ETL tools, or XML for integration contracts. Templates create a believable demo quickly; a seed makes screenshots, tutorials and benchmark runs reproducible. For pipelines specifically, NDJSON is usually the right answer at volume because it streams — a consumer handles one record at a time rather than parsing a whole array into memory, which is what BigQuery loads, Elasticsearch bulk ingest and jq without --slurp all expect. Demos have a different requirement: the same data every time. A fixed seed means the screenshot in your documentation matches what the reviewer sees, the number in the tutorial is still the number next month, and a benchmark comparison is measuring your code rather than a different dataset. Training, tutorials and interview exercises A fixed seed makes a dataset shareable as a recipe rather than a file: publish the schema and the seed, and every student generates byte-identical rows locally. Exercise answers are then the same for everyone, and nobody is working from a stale download. Because the data is fully synthetic it is also safe to publish, unlike the anonymised production extracts these exercises are often built from. The same applies to automation practice. This site's own controls all carry stable data-testid attributes that survive deploys, so it doubles as a target for Playwright and Cypress exercises — forms, selects, dialogs, drag-and-drop reordering, file downloads and dynamically rendered rows, without needing a test environment of your own. Load and performance testing Generating 100,000 rows is the point at which schema decisions start to matter for reasons other than realism. Wide schemas serialize more slowly than narrow ones; unique constraints on a small value space get expensive; and the format you export changes the load time downstream far more than the generation time upstream. Two cautions. Uniform data is a poor performance proxy — real workloads are skewed, and a cache that looks effective against evenly distributed keys may not be against a realistic hot-key distribution, which is what weighted Custom Lists are for. And a single export is capped at 100,000 rows; for larger fixtures, export repeatedly with different seeds and concatenate, stripping the header from every file after the first. Which format for which job The schema is the same in every case — only the serializer changes, so switching costs one dropdown. This is the shortest version of the decision: If you need…UseBecause A spreadsheet or a bulk database loadCSVEvery importer on earth reads it, and COPY/LOAD DATA are far faster than inserts. A clipboard paste or a shell pipelineTSVPastes into a sheet as real columns, and cut/awk need no parser. A test fixture or a mock API responseJSONTypes survive: numbers, booleans and real nulls rather than strings. A streaming or bulk-ingestion pipelineNDJSONOne record per line, so 100,000 rows cost constant memory and one bad line loses one record. A populated dev database, no ETL stepSQLpsql -f and it exists. Optional inferred CREATE TABLE. A legacy or integration contractXMLComplete document with a declaration, escaped entities and XPath-friendly structure. Three things that make a fixture actually useful Realistic values are the easy part. What separates a fixture you keep from one you regenerate every time is usually one of these: Set a seed and write it down. A seed turns the export into a function: same seed plus same schema gives byte-identical output, forever. That is what makes a committed fixture diff cleanly and a failing CI run reproducible on a laptop. Name seeds after what they represent — sprint-42, empty-cart — rather than leaving them blank. Generate the unhappy path on purpose. Blank % exists because most defects live in missing values, not present ones. A fixture where every field is populated tests the one case you were already confident about. The same applies to long values, international characters and boundary numbers. Keep the schema next to the code it feeds. Save the schema, or share the URL — the whole definition is encoded in the link — so the next person can regenerate the fixture instead of guessing what produced it. Choose safe synthetic fields Generated emails use reserved domains, network addresses use documentation ranges and sensitive-looking values are explicitly test-only. Read the data methodology and safety notes before using fixtures in shared environments. This matters beyond tidiness. Synthetic data is the only kind you can safely commit to a repository, paste into a bug report, share with a contractor or put on a screen during a demo — none of which are safe with a production extract, however well intentioned the anonymisation. Starting from generated data removes the question entirely. How this compares with the other options None of these workflows require this particular tool, and the honest comparison is a structural one: what changes is where generation happens and what you end up holding. Each of these pages says plainly when the other option is the better answer. Against a hosted generator — a server that generates for you, versus a page that generates on your machine. Against a library like Faker.js — values produced inside your program at run time, versus a file you download once and commit. Against a self-hosted application — infrastructure you control, versus infrastructure that does not exist. Against a template-based JSON generator — a template language you write, versus typed fields you pick. Against a random user API — records fetched over HTTP when your code runs, versus records already on disk. Common questions What is synthetic test data used for? Filling a system with realistic data when real data is unavailable, unsafe or too slow to get: seeding a development database, mocking an API a frontend is not waiting for, building QA fixtures that fail the same way twice, and making spreadsheets and demos that look like production without containing any of it. Is synthetic test data safe to commit to a repository?Yes, when every value is generated rather than derived from real records. Here emails use reserved documentation domains, card numbers are published payment-gateway test PANs, IP addresses come from documentation ranges, and no row describes a real person — so a generated fixture carries no personal data to leak. How much test data do I actually need?Enough to make the failure modes visible, which is usually far less than people assume. A hundred rows exercises layout, sorting and null handling; a thousand shows pagination and index behaviour; a hundred thousand is for measuring query plans and import times. Generating more than you will look at mostly slows your own feedback loop. Can the same dataset be reused across a team?Yes. Enter a seed and the same schema produces byte-identical output on every machine, so a fixture can be regenerated from a seed instead of stored, and two developers debugging the same row are looking at the same row. Which export format suits which job?CSV and TSV for spreadsheets and bulk database loads, JSON for API fixtures, NDJSON for streams and log pipelines, SQL for direct seeding with a CREATE TABLE included, and XML for systems that ask for it. The same schema exports to all six, so the choice is not made when you design it. Last updated 6 September 2026 ============================================================================ # UUID Generator — Bulk UUID v4 for Test Data URL: https://fundata.dev/uuid-generator ============================================================================ UUID generator (v4) Correctly formatted version-4 UUIDs by the thousand, as a column alongside the rest of your schema — and reproducible from a seed, which a normal UUID generator can never be. Generate UUIDs →All field types What it generates Standard 36-character version-4 UUIDs in canonical 8-4-4-4-12 form, with the version and variant bits set correctly — the 4 in the third group and one of 8, 9, a or b starting the fourth: id,user_id,created_at 3f2b8c14-7d9e-4a1f-b3c2-9e5d81a06f47,... c81d4e2a-06b7-4f3d-9a8e-2b5c7f10d934,... 7a9e5c30-4182-4bd6-8f71-3c0e6a2b95d8,... Anything that parses UUIDs will accept these: uuid.UUID() in Python, UUID.fromString() in Java, Postgres uuid columns, and validators that check the version nibble. The one thing to know before you use these These UUIDs come from the generator's seeded pseudo-random number generator, not from a cryptographic source. That is a deliberate trade: it is what makes the same seed produce the same UUIDs every run, which is the entire reason to generate identifiers here rather than in application code. So: excellent for fixtures, seed scripts, load-test payloads and anything you want to be able to reproduce. Not for session tokens, password-reset links, API keys or anything else where unpredictability is the security property. For those, use your platform's crypto.randomUUID() or equivalent. This distinction matters, and no amount of formatting correctness changes it. Why reproducible IDs are worth having A test that creates a record and then asserts against it usually has to capture the generated ID at runtime, which makes the assertion awkward and the failure message vague. With a seeded fixture the ID is known before the test runs, so you can reference it directly: // seed "orders-v2" always produces this first row const FIRST_ORDER = '3f2b8c14-7d9e-4a1f-b3c2-9e5d81a06f47'; await page.goto('/orders/' + FIRST_ORDER); The same applies to fixtures shared across a team: everyone's local database has the same identifiers, so a bug report can quote one. Keeping them unique Collisions in a 122-bit random space are not a practical concern at these row counts, but the underlying generator is seeded rather than cryptographic, so if you are loading into a column with a UNIQUE or primary-key constraint, enable the Unique toggle and let the generator guarantee it rather than relying on probability. Also worth remembering: two exports with the same seed produce the same UUIDs by design. That is a feature when you are re-seeding a database, and a problem when you are appending to one. Change the seed for the second batch. Foreign keys across tables Each column is generated independently, so a user_id in an orders table will not match a id in a users table by accident. Generate the parent table first, then paste its ID column into a Custom List field on the child schema — every child row then points at a parent that exists. Repeating some IDs in that list gives you realistic skew instead of a uniform spread. The database seeding guide works through the whole two-pass flow. UUID v4 or something else? The generator produces v4 specifically. If your schema uses a sortable identifier — UUID v7, ULID, or a snowflake — those encode a timestamp prefix and are not interchangeable with v4. For a sortable-looking key in a fixture, a Pattern field or a Row Number column is usually closer to what you want than a v4 UUID pretending to be ordered. Common questions Are these valid UUID v4 values?Yes. They use canonical 8-4-4-4-12 formatting with the version nibble set to 4 and the variant bits set to 8, 9, a or b, so standard UUID parsers and validators accept them. Are they cryptographically secure?No. They come from a seeded pseudo-random generator so that the same seed reproduces the same values. Use them for test data and fixtures, never for tokens, keys or anything where unpredictability is a security requirement. Can I generate the same UUIDs again?Yes — that is the point of the seed. The same seed and schema produce identical UUIDs on every machine, so fixtures can be referenced by ID in test assertions. How do I make a UUID foreign key match another table?Generate the parent table first, then paste its ID column into a Custom List field on the child schema. Independent random columns will never line up on their own. Does it support UUID v1 or v7?No, the field generates v4 only. Time-ordered identifiers such as v7 or ULID encode a timestamp and are better modelled with a Pattern field or a Row Number column if you need sortability in a fixture. Related generators Random date generator — created-at and updated-at columns for the same rows. Fake email generator — the other column that usually carries a UNIQUE constraint. PostgreSQL test data — loading a uuid-keyed table. Basics field reference — Row Number, Integer, Decimal, Boolean and Formula. Last updated 6 September 2026 ============================================================================ # XML to JSON Converter — Free, Private, In Your Browser URL: https://fundata.dev/xml-to-json-converter ============================================================================ Home / XML to JSON XML to JSON converter Paste XML and get JSON back. XML carries things JSON has no slot for — attributes, mixed content, repeated tags — so the mapping is spelled out below rather than left to guesswork. The converter needs JavaScript — it runs entirely in your browser, which is also why nothing is uploaded. The reference below works without it. Attributes, text nodes and repeated elements XML is a richer format than JSON, so every XML-to-JSON converter has to make three decisions. Converters differ, which is why the same file gives different JSON in different tools. Here they are, explicitly: 1. Attributes get an @ prefix becomes {"@id": "1001"}. The prefix keeps an attribute from colliding with a child element of the same name — which is legal XML and would otherwise silently overwrite one with the other. 2. An element with only text becomes that text Ada becomes "Ada", not {"#text": "Ada"}. The wrapper object would be noise in the overwhelmingly common case. When an element has both text and attributes, the text moves into a #text key alongside them, because there is nowhere else for it to go. 3. Repeated sibling elements become an array Two elements under the same parent become a two-element array. One becomes a single object, not a one-element array — and that asymmetry is worth knowing about, because it is the classic bug in code that consumes converted XML. Feed it a file with one record and the consumer that expected an array breaks. If the root element's children are all records, they are treated as the record list, which is why the example above converts to a clean array of two orders. Well-formedness is enforced Parsing uses the browser's own XML parser, so a file that is not well-formed is rejected with the reason rather than half-converted. Unclosed tags, mismatched nesting, stray & and undeclared entities all fail here — which is the correct outcome, and earlier than the failure you would otherwise get downstream. Namespaces are kept as part of the element name: becomes the key "ns:price". Nothing is resolved or stripped, because a prefix carries meaning that a converter cannot safely discard. What it is not This is not an XSLT engine and not a schema validator. It does not read a DTD or an XSD, does not apply default attribute values from one, and does not know that a particular element should be a number. Comments, processing instructions and CDATA markers are dropped — CDATA content comes through as text. Mixed content — text and elements interleaved inside the same tag, as in

see this now

— has no faithful JSON representation at all. The elements are kept and the loose text around them is not. If your XML is document-shaped rather than record-shaped, JSON is the wrong target. Common questions How are XML attributes represented?Each attribute becomes a key prefixed with @, so id="1001" becomes "@id": "1001". The prefix prevents an attribute from colliding with a child element that has the same name. Why is a single repeated element not an array?Because nothing in the XML itself says the element repeats — one is indistinguishable from a list of one. This is the classic bug in code that consumes converted XML, so it is worth handling explicitly on your side. What happens to malformed XML?It is rejected with the parser’s reason. Parsing uses the browser’s own XML parser, so anything a browser would refuse is refused here rather than half-converted. Are namespaces resolved?No. A prefixed element keeps its prefix in the key, so becomes "ns:price". Stripping the prefix would discard meaning the converter cannot safely reconstruct. Does it handle mixed content?Not faithfully — nothing can. Text interleaved with elements has no JSON equivalent; the elements are kept and the loose text between them is not. Document-shaped XML is a poor fit for JSON in general. Related /xml — generating well-formed XML test records instead of converting a file. /csv-to-json-converter — the same destination format, from a flat source. /json-to-csv-converter — flattening the JSON this page produces into columns. /api-mock-data — using the converted JSON as a fixture for a mock endpoint. Last updated 6 September 2026 ============================================================================ # XML Test Data Generator — Fun Data Playground URL: https://fundata.dev/xml ============================================================================ XML test data generator Well-formed documents with correct escaping, built from the same 68 realistic field types — for the enterprise integrations, SOAP endpoints and XSLT pipelines that still speak XML. Generate XML data → Browse field types What the output looks like 1 Elena Rossi elena.rossi7@example.org Why XML? Plenty of production systems — ERP integrations, banking interfaces, SOAP services, print pipelines — still exchange XML, and they deserve realistic test payloads too. The export wraps each row in a element under a single root, with field names as element names and all special characters (&, <, quotes) properly escaped. Where XML export helps Legacy imports — feed batch interfaces that only accept XML drops. XSLT & XPath tests — realistic documents make transformation bugs visible early. Schema mapping — field names become element names, so a customer_email field yields exactly. Blank % — blanks render as empty elements, the classic edge case in XML consumers. Validating and transforming the output # Well-formedness only — no schema needed xmllint --noout fundata_1000_rows.xml && echo "well-formed" # Validate against your own contract in CI xmllint --noout --schema contract.xsd fundata_1000_rows.xml # Count records and pull a single field with XPath xmllint --xpath 'count(/records/record)' fundata_1000_rows.xml xmllint --xpath '/records/record[1]/email/text()' fundata_1000_rows.xml # Reshape into the envelope your integration actually expects xsltproc to-soap.xsl fundata_1000_rows.xml > payload.xml # Pretty-print a file that arrived on one line xmllint --format fundata_1000_rows.xml Going the other way — XML you already have, and something downstream that wants JSON — is a different job from generating, and there is a page for it: the XML to JSON converter runs in the browser and documents the decisions the conversion has to make, including what happens to attributes and to repeated sibling elements. XML structure and compatibility Files include an XML declaration and a single root, so the result is a complete document rather than a fragment. Field names are sanitized for element use, text is UTF-8 and reserved characters are entity-escaped. The generator does not attach an XSD or namespaces because integration contracts vary; use the stable, seeded output as a fixture and validate it against your own schema in CI. Element names have rules that column names don't. An XML name cannot begin with a digit or contain a space, so field names are sanitized before they become tags. Name your fields customer_email rather than Customer Email and the mapping stays one-to-one and predictable. Empty element or absent element? Blank % produces — present but empty, which is not the same as omitting the element, and not the same as xsi:nil="true". Consumers routinely conflate all three; this is the cheapest way to find out whether yours does. Only five entities are predefined. &, <, >, " and ' are the whole set. Anything else —  , for instance — requires a DTD and will fail a plain parser, which is why accented characters are emitted as literal UTF-8 rather than as entities. Attributes are not used. Every value is element text. That keeps the output uniform and easy to XPath, but if your contract expects you will need an XSLT step. Large documents are not streamable by default. Unlike NDJSON, a 100,000-record XML file is one document; a DOM parser will hold all of it in memory. Use a SAX or pull parser at that size. Common XML questions Is the generated XML well-formed?Yes. Each record is nested under one root element, tags are balanced and special characters in values are escaped. You can verify any export with xmllint --noout. Can I generate SOAP envelopes or a custom XML hierarchy?The built-in export intentionally uses a simple records/record structure. Generate the row data first, then transform it with XSLT or application code when your contract needs namespaces or nested envelopes. Can I get values as attributes instead of elements?Not from the export itself — every value is element text, which keeps the document uniform and easy to query with XPath. An XSLT step converts elements to attributes in a few lines if your contract requires it. How are blank values represented?As an empty element: . That is deliberately different from omitting the element and from xsi:nil="true" — three states consumers often treat as one. Blank % lets you generate the case and check. Is there an XSD or namespace?No. Integration contracts differ too much for a generated schema to be useful, so the output is plain, namespace-free XML. Validate it against your own XSD with xmllint --schema — a seeded export makes that a stable CI check. Will a 100,000-record file load in my parser?It is one document, so a DOM parser holds the whole thing in memory. At that size use a streaming SAX or pull parser, or switch to NDJSON, where each record is independently parseable. For integration and regression fixtures, see the QA test-data guide. Other formats The same schema exports to all six formats — switch with one dropdown: CSV, TSV, JSON, NDJSON, SQL. New here? Start with the getting-started guide or the full field type reference. Last updated 6 September 2026 ============================================================================ # CSV’den JSON’a Dönüştürücü — Ücretsiz ve Tarayıcıda URL: https://fundata.dev/tr/csv-to-json-converter ============================================================================ Ana sayfa / CSV’den JSON’a CSV’den JSON’a dönüştürücü Bir CSV dosyası yapıştırın, karşılığında bir JSON dizisi alın. Dönüştürme bu sekmede olur — hiçbir şey yüklenmediği için, barındırılan bir araca yapıştırmayacağınız veriler için de güvenlidir. Diğer diller:EnglishEspañol Dönüştürücü JavaScript gerektirir — her şey tarayıcınızda çalışır, hiçbir veri yüklenmez. Aşağıdaki başvuru bölümü JavaScript olmadan da okunur. Tip algılama ve baştaki sıfır kuralı Bir CSV dosyasındaki her hücre metindir. JSON’un ise gerçek tipleri vardır; bu yüzden her dönüştürücü hangi metnin sayı, hangisinin doğru/yanlış, hangisinin boş değer olduğunu tahmin etmek zorundadır — ve veriyi sessizce bozan şey tam olarak bu tahmindir. Kutucuğu kapatırsanız her değer metin olarak gelir; emin değilseniz güvenli seçim budur. Algılama açıkken en önemli kural şudur: baştaki sıfır asla sayıya çevrilmez. 007 metin olarak "007" kalır; 01234, +90 555 000 0000 ve 0000-0002-1825-0097 de öyle. Bu bir incelik değil: posta kodları, telefon numaraları, stok kodları, ISBN’ler ve TC kimlik numaraları baştaki sıfırı anlam taşıyan rakam dizileridir, ve klasik hesap tablosu hatası tam olarak bu dönüşümün özensiz yapılmasıdır. HücreAlgılama açıkkenNeden 4242Düz tam sayı. -3.5-3.5Düz ondalık. 00Tek başına sıfırın kaybedecek baştaki sıfırı yok. 007"007"Baştaki sıfır anlamlı. 1.50"1.50"Sondaki sıfır kaybolurdu. 1e5"1e5"Geri yazıldığında 100000 olurdu. true / TRUEtrueBüyük/küçük harf duyarsız. (boş)nullBoş hücre, boş metin değil, olmayan değerdir. Genel ölçüt gidiş-dönüştür: bir değer yalnızca, ortaya çıkan sayı geri yazıldığında özgün metni birebir verdiğinde çevrilir. 1.50, 1e5 ve 007 değerlerini koruyan tek kural budur. Neleri doğru işler Tırnaklı alanlar — "…" içindeki virgüller, tırnaklar ve satır sonları; kaçış tırnağı "". Sekme veya virgül — ayraç başlık satırından anlaşılır, yani TSV de ayar değiştirmeden çalışır. CRLF veya LF — Windows ve Unix satır sonlarının ikisi de okunur. Eksik sütunlu satırlar — kısa bir satırın eksik sütunları boş kalır, kaymaz. Adsız sütunlar — boş bir başlık hücresi boş anahtar yerine col_3 olur. Bilerek yapmadıkları İç içe yapı kurmaz. user.name adlı bir CSV sütunu, iç içe bir nesne değil, adı birebir "user.name" olan bir JSON anahtarı olur — ters yön olan JSON’dan CSV’ye sayfası (İngilizce) noktayla düzleştirir, ama noktaları yapı olarak geri okumak, sütun adında nokta bulunan her dosyayı sessizce yeniden şekillendirirdi. Akış (streaming) da yapmaz. Dosyanın tamamı bellekte ayrıştırılır; girdiyi 2 MB civarında tutun, ötesinde dosyayı bölün veya komut satırı aracı kullanın. Bozuk CSV’yi de onarmaz: kapanmamış tırnağı olan bir dosya bir şeye ayrışır, ama kastettiğiniz şeye değil. Sık sorulan sorular Dosyam bir yere yükleniyor mu?Hayır. Dönüştürme kendi sekmenizde çalışan JavaScript’tir — dosya makinenizden hiç çıkmaz ve sayfa bir kez yüklendikten sonra çevrimdışı da çalışır. Sunucu kaynaklı bir boyut sınırı da bu yüzden yoktur; tek sınır tarayıcınızın belleğidir. Posta kodum neden metin olarak geldi?Sıfırla başladığı için. Bir değer yalnızca, ortaya çıkan sayı geri yazıldığında özgün metni birebir verdiğinde sayıya çevrilir; 007, 01234 ve 1.50 böyle korunur. Her sütunu metin isterseniz tip algılamayı kapatın. CSV yerine TSV dönüştürebilir miyim?Evet. Ayraç başlık satırından anlaşıldığı için sekmeyle ayrılmış bir dosya ayar değiştirmeden dönüşür. Alan içindeki virgülleri işler mi?Alan tırnak içindeyse evet — RFC 4180’in gerektirdiği budur. "Yılmaz, Ada" tek değer olarak gelir; ayrıştırıcı kaçış tırnaklarını ("") ve tırnak içindeki satır sonlarını da işler. Boş hücreye ne olur?Tip algılama açıkken null olur, çünkü bir CSV dosyasındaki boş hücre neredeyse her zaman "değer yok" demektir, "boş metin" değil. Algılama kapalıyken boş metin olarak kalır. İlgili sayfalar /json-to-csv-converter — aynı dönüşümün ters yönü ve iç içe yapının nasıl düzleştiği (İngilizce). /tr/json-to-typescript — aynı JSON’dan TypeScript arayüzleri üretmek. /csv-to-sql-converter — JSON’u atlayıp doğrudan CREATE TABLE ve INSERT üretmek (İngilizce). /sample-csv-files — dönüştürücüyü denemek için hazır CSV dosyaları (İngilizce). Son güncelleme 6 Eylül 2026 ============================================================================ # Convertidor de CSV a JSON — Gratis y en tu Navegador URL: https://fundata.dev/es/csv-to-json-converter ============================================================================ Inicio / CSV a JSON Convertidor de CSV a JSON Pega un archivo CSV y obtén un array JSON. La conversión ocurre en esta pestaña — no se sube nada, así que es seguro para datos que no pegarías en una herramienta alojada. Otros idiomas:EnglishTürkçe El conversor necesita JavaScript — funciona por completo en tu navegador, que es también por lo que no se sube nada. La referencia de abajo se lee sin él. Detección de tipos y la regla del cero inicial Cada celda de un CSV es texto. JSON tiene tipos reales, así que cualquier conversor debe adivinar qué texto era un número, un booleano o un nulo — y esa suposición es donde las conversiones de CSV a JSON corrompen datos en silencio. Desactiva la casilla y cada valor llega como cadena, que es lo seguro cuando no estás seguro. Con la detección activada, la regla que más importa es que un cero inicial nunca es un número. 007 se queda como la cadena "007", igual que 01234, +34 600 000 000 y 0000-0002-1825-0097. No es un detalle menor: códigos postales, teléfonos, SKUs, ISBNs y NIFs son cadenas de dígitos cuyos ceros iniciales significan algo, y el clásico error de hoja de cálculo es exactamente esta conversión hecha sin cuidado. CeldaCon detecciónPor qué 4242Entero simple. -3.5-3.5Decimal simple. 00Un cero solo no tiene cero inicial que perder. 007"007"El cero inicial es significativo. 1.50"1.50"Se perdería el cero final. 1e5"1e5"Al reescribirlo saldría 100000. true / TRUEtrueBooleano, sin distinguir mayúsculas. (vacío)nullUna celda vacía es un valor ausente, no una cadena vacía. La prueba general es la ida y vuelta: un valor solo se convierte si al reescribir el número resultante se obtiene exactamente el texto original. Esa única regla es la que mantiene intactos 1.50, 1e5 y 007. Qué maneja bien Campos entrecomillados — comas, comillas y saltos de línea dentro de "…", con "" como comilla escapada. Tabuladores o comas — el delimitador sale de la fila de cabecera, así que un TSV funciona sin cambiar nada. CRLF o LF — se leen los finales de línea de Windows y de Unix. Filas incompletas — a una fila corta le faltan valores, no se le desplazan las columnas. Columnas sin nombre — una cabecera vacía se convierte en col_3, no en una clave vacía. Lo que deliberadamente no hace No anida. Una columna llamada user.name se convierte en una clave JSON literalmente llamada "user.name", no en un objeto anidado — la dirección contraria en la página de JSON a CSV (en inglés) aplana con puntos, pero volver a leer esos puntos como estructura reformaría en silencio cualquier archivo cuyas columnas contengan un punto. Tampoco procesa en flujo. El archivo entero se analiza en memoria, así que mantén la entrada por debajo de unos 2 MB; más allá, divide el archivo o usa una herramienta de línea de comandos. Y no repara CSV roto: un archivo con una comilla sin cerrar se analiza, pero no como querías. Preguntas frecuentes ¿Se sube mi archivo a algún sitio?No. La conversión es JavaScript ejecutándose en tu propia pestaña — el archivo nunca sale de tu máquina y la página funciona sin conexión una vez cargada. Por eso tampoco hay un límite de tamaño impuesto por un servidor, solo el de la memoria de tu navegador. ¿Por qué mi código postal es una cadena?Porque empieza por cero. Un valor solo se convierte en número cuando al reescribirlo se reproduce exactamente el texto original, lo que mantiene intactos 007, 01234 y 1.50. Desactiva la detección de tipos si quieres todas las columnas como cadenas. ¿Puedo convertir TSV en vez de CSV?Sí. El delimitador se detecta desde la fila de cabecera, así que un archivo separado por tabuladores se convierte sin cambiar ningún ajuste. ¿Maneja comas dentro de un campo?Sí, si el campo está entrecomillado, que es lo que exige RFC 4180. "García, Ada" llega como un solo valor; el analizador también maneja comillas escapadas ("") y saltos de línea dentro de campos entrecomillados. ¿Qué pasa con una celda vacía?Con la detección de tipos activada se convierte en null, porque una celda vacía en un CSV casi siempre significa "sin valor" y no "la cadena vacía". Con la detección desactivada se queda como cadena vacía. Relacionado /json-to-csv-converter — la misma conversión en sentido contrario, y cómo se aplana el anidamiento (en inglés). /es/json-to-typescript — generar interfaces de TypeScript a partir del mismo JSON. /csv-to-sql-converter — saltarse JSON e ir directo a CREATE TABLE e INSERT (en inglés). /sample-csv-files — archivos CSV de ejemplo para probar el conversor (en inglés). Última actualización 6 de septiembre de 2026