Mock data API · 29 types · 4 locales
Ship the screen before the API exists
Describe your data as a JSON layout and get realistic records back — nested, conditional, relational, reproducible by seed, in English, Korean, Japanese, or Chinese.
[ { "id": "b3f1c8a2-4d19-4c30-9f2a-5b8e7d441ce0", "name": "Marion Whitfield", "email": "[email protected]", "age": 34, "plan": "pro", "signed_up": "2026-03-14", "address": { "city": "Rotterdam", "zipcode": "30012" } }, { "id": "7c04ae51-8b62-4f19-9d03-2ea6c1f80b95", "name": "Priya Raman", "email": "[email protected]", "age": 41, "plan": "free", "signed_up": "2026-05-02", "address": { "city": "Leeds", "zipcode": "51884" } } ]
[ { "id": "b3f1c8a2-4d19-4c30-9f2a-5b8e7d441ce0", "name": "Marion Whitfield", "email": "[email protected]", "age": 34, "plan": "pro", "signed_up": "2026-03-14", "address": { "city": "Rotterdam", "zipcode": "30012" } }, { "id": "7c04ae51-8b62-4f19-9d03-2ea6c1f80b95", "name": "Priya Raman", "email": "[email protected]", "age": 41, "plan": "free", "signed_up": "2026-05-02", "address": { "city": "Leeds", "zipcode": "51884" } } ]
422 · invalid layout
Fix the JSON on the left, then run it again.
something broke on our side
The generator hiccuped. Your layout is fine — try the same request again.
429 · rate limit reached
The anonymous demo allows 20 requests per minute per IP.
A free account has no per-minute cap.
Sign up freeGET /v1/layouts/user-profile?count=2&seed=42&locale=en
anonymous demo · 20 requests per minute
How it works
Three steps to a dataset
Write a layout in JSON — or import sample JSON, a JSON Schema, an OpenAPI component, or SQL DDL.
{ "id": { "type": "uuid" }, "name": { "type": "name" }, "email": { "type": "email" } }
One GET, any count. Pass a seed and the response is deterministic.
curl -H "X-API-Key: $KEY" \ "$API/v1/layouts/user-profile\ ?count=100&seed=42"
Point fixtures, Storybook mocks, or seed scripts at the endpoint — or freeze a snapshot.
[ { "id": "b3f1c8a2-4d19-…", "name": "Marion Whitfield", "plan": "pro" } ]
Type catalog
29 field types, one line each
Hover a type to see a sample value. Every one of them works in the demo above.
Basic
Date & time
Identifiers
Media
Personal data
Text
Finance
Nested
Beyond random
Mock data that behaves like production data
Random strings are easy. Shipping screens needs structure, conditionals, and data that stays put between test runs.
Nested structure & conditional fields
Objects in objects, arrays of objects, as deep as your real payloads go. A field can depend on a sibling — a trial_ends_at that only exists when plan is "trial".
{ "plan": { "type": "enum", "options": { "values": ["free", "trial", "pro"] } }, "trial_ends_at": { "type": "date", "condition": { "field": "plan", "equals": "trial" } }, "billing": { "type": "object", "properties": { "card_last4": { "type": "string", "options": { "length": 4, "charset": "numeric" } } } } }
Same seed → same data.
Two identical requests, run a week apart, byte-for-byte equal. A failing test stays failing until you fix it — not until the data shifts. Growing a locale's data pool is the one thing that moves the result; a snapshot pins a set for good.
[ { "id": "b3f1c8a2-4d19-…", "name": "Marion Whitfield", "age": 34, "plan": "pro" } ]
[ { "id": "b3f1c8a2-4d19-…", "name": "Marion Whitfield", "age": 34, "plan": "pro" } ]
GET /v1/bundles/store-fixtures?seed=42
{
"users": [ /* 50 records */ ],
"orders": [ /* 200 records */ ],
"line_items": [ /* 600 records */ ]
}Relational data that resolves
Group the layouts that belong together into a bundle — users, orders, line items — and generate the whole set in one request, each with its own record count. Every layout in the bundle shares a single seed, so the set reproduces as a unit, not layout by layout.
Snapshots: freeze a set, serve it forever
Pin any generated set to its own URL. Hand-edit records if you need a specific case, then let CI, demos, and teammates fetch exactly the same data every time.
POST /v1/snapshots
{ "layout_slug": "user-profile", "name": "Checkout QA",
"slug": "checkout-qa", "count": 500, "seed": 42 }
GET /v1/snapshots/checkout-qa
# the same 500 records, every timeCREATE TABLE users ( id UUID PRIMARY KEY, email VARCHAR(255) NOT NULL, plan VARCHAR(20) DEFAULT 'free' ); → imported: layout "users" · 3 fields inferred
Start from the schema you already have
Paste sample JSON, a JSON Schema, an OpenAPI component, or CREATE TABLE DDL — Apiryner infers the layout and you edit from there. Nobody retypes 40 fields.
Four locales, native-looking values
Names, addresses, phone formats, and product copy that read native in English, Korean, Japanese, and Chinese. One query param — try locale=ko in the demo above.
{ "name": "김서연", "city": "서울", "phone": "010-4821-7739", "product": "보온 스테인리스 텀블러" }
Use cases
Where the data ends up
Frontend prototyping
Real-shaped data in Storybook and prototypes before the backend exists.
const users = await fetch( "…/v1/layouts/user-profile?count=12&seed=7" ).then((r) => r.json());
Test fixtures
Deterministic fixtures — a failing test reproduces on every machine.
beforeAll(async () => {
users = await api.generate("user-profile",
{ count: 50, seed: 1337 }); // always the same 50
});Demo environment seeding
Fill staging with data that looks real in screenshots and sales demos.
users = apiryner.generate("user-profile",
count: 100, seed: 42, locale: "ko")
User.insert_all(users)Load testing
Export once as CSV, then replay the same rows through k6 or JMeter.
GET /v1/layouts/user-profile?count=100&format=csv # → fixtures/users.csv, versioned with the repo
API & SDKs
Call it from anywhere
The HTTP API is live today — anything that can make a request works, no client library needed. The npm SDK ships with a CLI, React/Vue adapters, and an MCP server for AI agents. Swift Package Manager and Gradle SDKs are written and tested but not published yet.
npm install apiryner
import { Apiryner } from "apiryner";
const api = new Apiryner({ apiKey: process.env.APIRYNER_KEY });
const users = await api.generate("user-profile", {
count: 100,
seed: 42,
locale: "ko",
});Not published yet — this is the shape it will have. Use the cURL tab today; every SDK calls the same endpoint.
.package( url: "https://github.com/devryner/apiryner-sdk-swift", from: "0.1.0" )
let api = try Apiryner(config: .init(apiKey: apiKey)) let users: [User] = try await api.generate( slug: "user-profile", options: .init(count: 100, seed: 42, locale: "ko") )
Not published yet — this is the shape it will have. Use the cURL tab today; every SDK calls the same endpoint.
implementation("com.apiryner:sdk:0.1.0")val api = Apiryner(ApirynerConfig(apiKey = System.getenv("APIRYNER_KEY")))
val users: List<User> = api.generate(
slug = "user-profile",
options = GenerateOptions(count = 100, seed = 42, locale = "ko"),
)npx apiryner --help # ships with the apiryner npm package
export APIRYNER_API_KEY=ak_... npx apiryner generate user-profile \ --count 100 --seed 42 --locale ko \ --format csv --out fixtures/users.csv # wrote fixtures/users.csv
# no SDK needed — it's just HTTP
curl -s -H "X-API-Key: $APIRYNER_KEY" \ "https://api.apiryner.com/v1/layouts/user-profile\ ?count=100&seed=42&locale=ko" | jq '.[0].name' "김서연"
Templates
Skip the blank layout
Fork a preset in the schema builder and edit from there.
Contact list with personal details
8 fields
Blog articles with titles, content, authors, and tags
11 fields
Company profiles with contact info and details
11 fields
Product catalog with prices, categories, and images
10 fields
Event data with dates, venues, and descriptions
11 fields
Social media content with engagement metrics
9 fields
Questions, answered
Is it free? ↓
The free tier covers 1,000 API requests and 10,000 generated records a month, up to 100 records per request, with 10 layouts, 3 API keys, and 3-level nesting. Every field type, every locale, and snapshots are included. No credit card to sign up, and the demo on this page needs no account at all.
Do I need an account to try it? ↓
No. The demo above hits the real generation engine, limited to 20 requests per minute per IP and 5 records per run. An account lifts both and gives you saved layouts, API keys, bundles, and snapshots.
What exactly does the seed guarantee? ↓
The same layout, seed, count, and locale return byte-for-byte identical output — across days, SDKs, and regions. Change any one of them and you get a new, equally deterministic set. The one thing outside your control is the data pool itself: when we add names or cities to a locale, previously generated values shift. Pin a snapshot when you need a set that can never move.
Can I import a schema I already have? ↓
Yes. Paste sample JSON, a JSON Schema, an OpenAPI component, or CREATE TABLE DDL, and Apiryner infers a layout you can edit before saving.
Which locales are supported? ↓
English, Korean, Japanese, and Chinese. Names, addresses, phone numbers, and text values all localize; structural values such as uuid, slug, and dates stay ASCII.
Can I keep a generated dataset fixed? ↓
Snapshots pin any generated set to its own URL that returns the same records every time. You can hand-edit records first if you need a specific case in there.
Your first dataset is two minutes away
Free covers 1,000 requests a month — no card, no sales call. The demo above is the product.