A fake data generator — also called a test, mock, dummy, sample or synthetic data generator — creates realistic but entirely fictional records you can drop straight into a database, an API mock, a spreadsheet or a design mock-up. You build a schema of the columns you need, choose how many rows to generate, and export the result. Everything on this page runs 100% in your browser: no upload, no account, no row-count paywall, and nothing that could leak, which is what makes it a safe answer to “can we just copy production into staging?”.
Identity, contact, location, business, finance, commerce, network, date & time, technical, text and special — from IBAN and EAN-13 to Kubernetes pod names.
Credit cards pass Luhn, IBANs carry a real mod-97 check digit, EAN-13 and ISBN-13 carry a correct GS1 check digit. Your validators will accept them.
The email matches the name in the same row, the work email matches the company, the state code matches the state. Not a bag of unrelated values.
US, UK, Belgium, Netherlands, Germany, France, Spain, the Nordics or a global mix — names, cities, phone formats, postcodes and IBAN country all follow.
Set a seed and the same schema produces byte-identical data every run, on any machine. Safe to commit as a fixture and to use in snapshot tests.
CSV, Excel-friendly CSV, TSV, JSON, NDJSON, SQL with optional CREATE TABLE, YAML, XML, Markdown and HTML — up to 50,000 rows.
Most generators hand you a grid of independent random values: an email that has nothing to do with the name beside it, a state code that contradicts the state, a credit card number your own validation rejects. That data is fine for filling space and useless for finding bugs. Here, each row is built with a small amount of context — the person's name drives the email, the username, the initials and the social handle; the company drives the work email, the domain and the website; the locale drives the city, the street format, the postcode, the phone number and the IBAN country. What comes out looks like a real export, so it breaks your code in the same places a real export would.
Seeding a fresh environment so it looks real. Mocking API responses before the backend exists. Filling a dashboard for a screenshot without exposing a single customer. Load-testing an import with fifty thousand rows. Proving that your CSV parser survives a comma inside a company name, that your table layout survives a Belgian address, and that your report survives a null. All of it without the legal and operational risk of copying production data into a system with weaker controls — the single most common finding in a data-protection review.
active:70,inactive:20,banned:10, because real status columns are never evenly splitGenerate up to 50,000 rows and export in ten formats. CSV is RFC 4180-escaped; CSV Excel adds a BOM and semicolons so European Excel opens it in columns with accents intact; SQL can emit a matching CREATE TABLE with inferred column types, UNIQUE and NOT NULL constraints, batched 500 rows per statement; NDJSON feeds log pipelines and bulk loaders; Markdown and HTML are for documentation and mock-ups. Switching format re-renders the same rows rather than drawing new ones, so you can export one dataset five ways.
This covers the core of what hosted generators charge for — custom schemas, 123 field types, per-field null rates and uniqueness, ten export formats, a live table preview and reproducible seeded output — with no row-count paywall, no account and no sign-up. It runs entirely client-side, so it also works offline once loaded and never sends your schema or your data anywhere. What hosted tools still do better: multi-table relational exports, regex-driven custom types, and an API you can call from CI.
Seventy-seven questions on building a schema, which field types carry real checksums, how locales and seeds work, when to use nulls and unique columns, which export format your tool actually wants, and why generated data is the safe answer to copying production.
A fake data generator creates realistic but entirely fictional records — names, emails, addresses, IBANs, IP addresses, timestamps, UUIDs — so you can build and test software without touching real customer information. Here you build a schema (a list of columns, each with a type), choose how many rows you want and an export format, then press Generate. Everything runs in your browser; nothing is uploaded.
Click a template — Person, Address, Company, Tech / API, E-commerce, Full Record, Healthcare, Event Log, Social Media, Network / FW, Transactions or IoT Sensors. Each loads a complete, sensibly configured schema and generates immediately. Change a type, adjust the row count, pick a format and you are done.
Up to 50,000 rows per run. The slider covers the common range up to 5,000 and the number box next to it accepts any value up to the maximum. Above roughly 20,000 rows the page shows a note, because the browser will pause for a moment while it builds the file.
No account, no email, no paywall and no row limit behind a subscription. That is the main practical difference from hosted generators: because everything happens in your browser, there is no server cost to recover and nothing to sign up for.
Yes, once the page has loaded. All the data pools and generators are part of the page itself, so you can disconnect and keep generating. It is not installed as an offline app, so the first load needs a connection.
Ctrl / ⌘ + Enter generates, and Ctrl / ⌘ + S downloads the current output as a file instead of opening the browser's save-page dialog.
Press Add Field, type a column name, and choose a type from the grouped dropdown. Use ▲▼ to reorder columns, ∅% to make a share of the values null, U to force unique values, and ✕ to delete a column. Numeric and date types get inline min/max boxes.
Three ways. 💾 Save stores it in this browser; 📂 Load brings it back. ⎘ Copy JSON puts the schema on your clipboard so you can paste it into a repo or a ticket, and ⇤ Import JSON reads it back. 🔗 Copy share link packs the schema, seed, row count, locale and format into a single URL.
⊞ Table renders the first 200 rows as a real table with sticky headers, so you can scan the data and spot nulls, wrong ranges or a column that is not varied enough. </> Raw shows the exact text you will copy or download.
123 types in 11 groups: Identity, Contact, Location, Business, Finance, Commerce, Network, Date & Time, Technical, Text and Special. The exact number is shown next to the Add Field button, since it changes as types are added.
They pass the Luhn check and use the correct issuer prefixes and lengths — Visa 16 digits starting with 4, Mastercard 16 starting 51–55, American Express 15 starting 34 or 37, Discover 16 starting 6011. That means your validation code will accept them, which is exactly what you need for testing. They are not linked to any real account and cannot be charged.
They are structurally valid: the correct country length, and a mod-97 check digit calculated per ISO 13616, so an IBAN validator will accept them. The bank and account portions are random, so they do not correspond to any real account.
Both carry a correct GS1 modulo-10 check digit, and ISBN-13 uses a real 978 or 979 prefix. Barcode software and ISBN validators will accept them; they will not match a real product or book.
Yes — this is the part most generators get wrong. Within a row, Email, Work Email, Username, Initials and Social Handle are derived from the First Name and Last Name in the same row, Work Email and Domain and URL come from the Company in the same row, and State and State Code always match. Pick the Person template and look at the table: emma.peeters@… sits next to Emma Peeters, not next to a stranger.
They are invented and use ordinary public mail domains, so an address could in theory belong to someone. Never point a test mail server at generated addresses — use a catch-all mailbox, a tool like MailHog, or replace the domain with example.com, which RFC 2606 reserves for exactly this purpose.
Email uses consumer domains such as gmail.com or proton.me. Work Email builds first.last@company-domain from the Company field in the same row, so a table of employees looks like a real staff directory.
Log Level, Severity, ISO 8601, Duration ms, HTTP Status, HTTP Method, URL Path, Referrer, IP Address, Private IP, CIDR Block, ASN, User Agent, Browser, OS, Device Type, K8s Pod, Docker Image, Cloud Region and File Path. The Event Log and Network / FW templates wire several of these together.
UUID (v4), Nano ID, Mongo ObjectId, Git SHA and Short SHA, MD5, SHA-256, API Key, JWT, Base64, Transaction ID, Employee ID, SKU and Number Sequence. Turn on U for any of them if the column must be a primary key.
It draws from a list where you control the odds. Write active:70,inactive:20,banned:10 and roughly 70% of rows will be active. Real data is almost never uniformly distributed, and a status column that is one-third banned users will not exercise your UI the way production does.
Custom List picks one value at random from your comma-separated list. Constant writes the same value into every row — useful for a tenant id, an environment tag or a version marker that has to be identical across the whole import.
It is an integer within a range you set, meant to reference another table. Generate a parents table with a Number Sequence id from 1 to 500, then give the child table a Foreign Key column with min 1 and max 500 — the references will resolve.
Only in a limited way: the JSON Object type puts a small object inside a single column. Full nested documents with arrays and sub-objects are not supported — every row here is a flat record, which is what CSV, SQL and spreadsheets need anyway.
For healthcare and insurance test data, where you need a plausible clinical column to build screens and reports around. The values are common condition names attached to entirely fictional patients — nothing in the output relates to a real person.
Not from the interface. The page is a single self-contained HTML file, so if you save it you can add an entry to the GEN object and to TYPE_GROUPS — a type is just a name and a function that returns a value. For one-off needs, Custom List or Weighted List usually covers it without touching code.
It switches the whole row to one country's conventions: first and last names, cities, street naming and address order, postcode format, phone and mobile number format, country and country code, and the IBAN country. Nine options are available — Global mix, United States, United Kingdom, Belgium, Netherlands, Germany, France, Spain and the Nordics.
It picks a locale per row rather than per dataset, so one row is Belgian, the next German, the next American — but each row stays internally consistent. That is the closest thing to an international customer base, and it is the setting that finds encoding and formatting bugs fastest.
Because most real bugs live in the assumptions. A five-digit postcode field breaks on 1000 in Belgium and on SW1A 1AA in London. A name column that assumes ASCII breaks on Müller, Léa and Álvarez. A phone validator built for (555) 123-4567 rejects +32 470 12 34 56. Generating with a non-US locale surfaces all of that before a customer does.
The generator is driven by a seeded pseudo-random number generator, so the seed decides every value. Type order-fixtures as your seed and the same schema will produce byte-identical data every time, on any machine. Leave the field empty and a new random seed is used per run.
Press 📌 next to the seed field. The seed that produced what you are looking at is copied into the box, so the next Generate reproduces it exactly. 🎲 does the opposite: a new random seed and a fresh dataset.
Because a fixture that changes on every run makes tests lie. A snapshot test, a visual-regression screenshot or a report with a fixed expected total all break when the data underneath them shifts. A fixed seed turns generated data into something you can commit and diff — the only changes you see are the ones you made.
No, and that is intentional. The values are drawn in schema order, so adding, removing or reordering a column changes what every later column receives. Keep the schema and the seed together — the share link stores both, which is the safest way to hand a dataset to someone else.
The full schema (names, types and every per-field option), the seed, the row count, the locale, the export format and the table name — encoded into the part of the URL after the #. Browsers never send that fragment to a server, so the link is self-contained and private. Open it and the page rebuilds the dataset immediately.
That is a good use for it. Drop it in a comment above your fixture file or in the README of a test suite, and anyone can regenerate or extend exactly the same data instead of guessing how the file was made.
A small deterministic generator (mulberry32) seeded with an FNV-1a hash of your seed string. It is fast and identical across browsers, which is the point. It is not cryptographically secure: the Password and API Key types are fine as placeholders but must never be used as real secrets.
It sets the probability, per field, that a value comes out null. Set 20 and roughly one row in five will be empty in that column. In CSV and TSV that is an empty cell, in JSON and YAML a real null, in SQL NULL, and in XML a self-closing element.
Because missing data is where software falls over. A dashboard that averages a column, a template that prints user.middle_name.toUpperCase(), a CSV import that assumes every row is complete — all of them work perfectly until the first empty cell. Ten or twenty per cent nulls in optional columns is a realistic and revealing setting.
It forces unique values in that column. The generator retries a value that has already appeared, up to fifty times per row. Use it for primary keys, emails, usernames and any column with a unique index — otherwise a 10,000-row import will hit a duplicate-key error somewhere in the middle.
If a type has fewer distinct possible values than the number of rows you asked for — 10,000 rows of a five-item Custom List, say — the generator appends a numeric suffix so the column stays unique, and tells you how many values needed one. That is a signal to switch to a type with a bigger space, such as Number Sequence, UUID or Nano ID.
For Number Sequence, UUID, Nano ID and Mongo ObjectId, effectively yes. For short or list-based types it is best-effort with the suffix fallback described above. If the column is a real primary key, use Number Sequence — it is unique by construction and it sorts.
Combine the tools: a Custom List containing an apostrophe, a comma, a quote and an emoji; Paragraph in a column your UI expects to be short; ∅% at 50 to flood the null path; the Nordics or Belgium locale for non-ASCII names; and Amount with a negative minimum to test refunds and credits. That mix finds escaping, layout and validation bugs in one pass.
Float has a decimals option in its defaults (two by default), and Price and Amount always produce two. For currency, two is what you want; storing money as a float at all is a separate argument your database will eventually win.
Yes — set a negative minimum on Integer, Float or Amount. Amount defaults to a range that includes negatives precisely so a transactions table contains refunds as well as payments.
Yes. Dates are drawn as real timestamps inside the year range you set, so you get 29, 30 and 31-day months and a genuine 29 February in leap years. That matters: the classic date bug is code that only ever saw days 1–28 in its test data.
UTC. Date, DateTime, ISO 8601 and both Timestamp types are all derived from the same UTC instant, so a row's date and timestamp columns agree with each other. The ISO 8601 type is the one to use when your importer expects a timezone marker.
Ten: CSV, CSV Excel, TSV, JSON, NDJSON, SQL, YAML, XML, Markdown and HTML. Switching format re-renders the data you already generated — it does not draw new values.
Plain CSV is comma-separated UTF-8, which is what most importers and pandas expect. CSV Excel is semicolon-separated and starts with a UTF-8 byte-order mark, which is what Excel needs in most European locales to split the columns properly and show é, ü and ø correctly instead of mojibake. If your spreadsheet dumps everything into column A, this is the option you want.
Yes. Any value containing the separator, a double quote, a line break or leading or trailing spaces is wrapped in quotes, and internal quotes are doubled — RFC 4180 behaviour. That matters as soon as your data contains a Paragraph field or a company name with a comma in it.
JSON gives one array you can drop into a fixture or a mock API. NDJSON gives one object per line, which is what log pipelines, BigQuery, Elasticsearch bulk loaders and most streaming tools ingest — and it lets you read a huge file line by line without holding all of it in memory.
Yes. Choose SQL and tick Include CREATE TABLE. Column types are inferred from the field types — BIGINT for counters and timestamps, DECIMAL(12,4) for money and coordinates, DATE and TIMESTAMP for dates, BOOLEAN, TEXT for long prose and VARCHAR(255) otherwise. Columns marked unique get a UNIQUE constraint, and columns with no nulls get NOT NULL.
The inserts use backtick-quoted identifiers, which is MySQL and MariaDB syntax. For PostgreSQL or SQLite, replace the backticks with double quotes or delete them — a single find-and-replace. Values are escaped with doubled single quotes, which is standard everywhere.
Rows are batched 500 at a time. One statement with 50,000 value tuples exceeds the maximum packet size on a default MySQL install and is painful to debug when one row fails. Batches of 500 import fast and fail readably.
For Excel, use CSV Excel and open the file directly. For Google Sheets, plain CSV works better — File → Import → Upload. If you only need a few rows, copy the Markdown or plain CSV output and paste it in.
Download plain CSV and read it with pandas.read_csv('fake-data-abc123.csv') or readr::read_csv(). For a JSON workflow use NDJSON with pandas.read_json(path, lines=True). The filename includes the seed, so you can always trace a file back to the exact configuration that produced it.
A ready-made table you can paste into a README, a pull request, a ticket or documentation — useful when you want to show the shape of a dataset rather than ship it. Pipes inside values are escaped so the table does not break.
A clean <table> with a thead and tbody and everything HTML-escaped, ready to paste into a page, a component or a design mock-up that needs a realistic-looking data table.
Yes — generate JSON and serve it from a static file, or paste it into a mocking tool such as Mockoon, WireMock, MSW or json-server. Set a seed so the mock is stable, and use Work Email, UUID and ISO 8601 to make the payload look like something a real backend would return.
Only the on-screen preview is truncated, at around 300,000 characters, because rendering a multi-megabyte string in the browser is slow and pointless. Copy and Download always give you the complete dataset, and the row counter in the toolbar shows the real total.
No. There is no back end. The schema, the seed and every generated row exist only in your browser tab, and the share link keeps its payload in the URL fragment, which browsers never transmit. The site uses privacy-first analytics that count page views without cookies or personal data.
The data is invented, so it is not personal data and the GDPR does not apply to it — which is exactly why generated data is the right answer to “can we copy production into staging?”. Two caveats worth stating plainly: a random name can coincide with a real person's, and an email address on a real domain could exist. Treat the output as fictional, never as anonymised production data.
That is the best reason to use it. Copying production into a test environment spreads personal data into systems with weaker access control, longer retention and more people looking at it — one of the most common findings in a data-protection audit. A generated dataset with the same shape carries none of that risk.
No, and the difference is legally important. Pseudonymised data is still personal data — it is derived from real records and can be re-identified. Synthetic data is invented from scratch and has no data subject behind it. Only the second one takes you out of scope.
Yes. There is no licence on the output, no attribution requirement and no watermark. Use it in products, demos, courses, screenshots and test suites.
They are a mix of invented names and well-known fictional companies — Acme, Initech, Contoso, Fabrikam, Umbrella. They are placeholders. If you are producing screenshots for publication, prefer your own invented names to avoid any trademark question.
Yes, and it is the safest option. Set a seed so the demo shows the same records every time you present it, avoid anything that looks like a real person's contact details, and use the example.com domain if the screenshots will be published.
Only as placeholders. They come from a seeded, non-cryptographic generator, so anyone who knows the seed can reproduce them exactly. Never use them as real credentials — for that, use your operating system's or language's secure random source.
It removes the protected health information problem, which is most of it: no real patient record ever enters your test environment. It does not make a system compliant by itself — access control, audit logging and retention are still yours to get right.
It covers the same core ground — custom schemas, well over a hundred field types, per-field null rates and uniqueness, ten export formats and a live table preview — with no account, no row-count paywall and no upload. What hosted tools still do better is very large datasets, related multi-table exports, regex-driven custom types and API access.
For millions of rows, a library in your own language — Faker, Bogus, factory_boy — belongs in the pipeline instead.
Generation happens on the browser's main thread, so a 50,000-row export with twenty columns will block the tab for a second or two while it builds the string. That is expected. If it happens often, generate in a few smaller runs or reduce the number of long text columns, which dominate the output size.
The clipboard API needs a secure context and a user gesture; if it is blocked, the tool falls back to an older copy method automatically. For very large outputs, Download is more reliable than Copy — some browsers refuse to put multi-megabyte strings on the clipboard.
The file is built in memory and handed to the browser as a blob, which some strict privacy extensions and managed corporate profiles block. Copying the output and pasting it into a new file gives the identical result. On iOS the file may open in a viewer instead of saving — use the share sheet from there.
Turn on U for that column. Without it, values are drawn independently and a collision in a 5,000-row set is not just possible but likely — the birthday problem makes duplicates far more common than intuition suggests.
The schema panel flags them in red, and on export they are automatically numbered (email, email_2) so the file stays valid. Empty column names are replaced with field_1, field_2 and so on. It is worth fixing them properly before you export.
Values are drawn in schema order from a single seeded stream, so inserting a column shifts everything after it. If you need a dataset to stay stable, freeze the schema first and only then pin the seed — or keep the share link, which stores both together.
Yes. Below roughly 1000 pixels the schema panel moves above the output instead of beside it. Building a wide schema is more comfortable on a desktop, but generating, previewing and downloading all work on mobile.
Any current version of Chrome, Edge, Firefox, Safari, Brave, Opera or Vivaldi, on desktop and mobile. There are no external libraries and no framework — the page is self-contained, which is also why it keeps working offline.
It is free with no account and no paid tier. It is one of a set of client-side browser tools at jasperbernaers.com/apps — no server means no running cost, no sign-up and nothing to leak.