English · 한국어

API Documentation

Everything you need to generate realistic mock data with the Apiryner API.

Quick Start

Generate random data in 3 steps:

  1. Create a Layout — define your data schema with field names and types
  2. Get an API Key — create one from the API Keys page
  3. Make a Request — call the API with your layout slug

Example request:

curl -H "X-API-Key: ak_your_key_here" \
  "https://api.apiryner.com/v1/layouts/user-profile?count=2&locale=en"

Example response:

[
  {
    "id": 1,
    "name": "Sarah Johnson",
    "email": "[email protected]",
    "age": 28,
    "is_active": true
  },
  {
    "id": 2,
    "name": "James Wilson",
    "email": "[email protected]",
    "age": 45,
    "is_active": true
  }
]

Authentication

Apiryner supports two authentication methods:

API Key (Read-Only)

Pass your API key in the X-API-Key header. API keys can only read/generate data.

curl -H "X-API-Key: ak_your_key_here" \
  https://api.apiryner.com/v1/layouts/my-layout

Bearer Token (Full Access)

Use an OAuth 2.0 Bearer token for write access (create, update, delete). The easiest way is the CLI login, which runs the OAuth PKCE flow and stores the token locally:

# Login via OAuth PKCE (opens the browser)
npx apiryner login

# Use token
curl -H "Authorization: Bearer oat_your_token" \
  -X POST https://api.apiryner.com/v1/layouts \
  -H "Content-Type: application/json" \
  -d '{"name": "Users", "slug": "users", "schema": {"id": {"type": "uuid"}}}'

Tokens carry the scopes granted on the consent screen: read allows GET requests, write allows everything. A request outside the token's scopes returns 403 insufficient_scope; write operations with an API key return 403 forbidden.

Endpoints

Method Endpoint Auth Description
Generation
GET /v1/layouts/:slug API Key / Bearer Generate data from a layout
GET /v1/bundles/:slug API Key / Bearer Generate every layout in a bundle in one response
GET /v1/snapshots/:slug API Key / Bearer Serve a snapshot’s pinned data
Layouts
GET /v1/layouts API Key / Bearer List your layouts
POST /v1/layouts Bearer Create a layout
PUT /v1/layouts/:slug Bearer Update a layout
DELETE /v1/layouts/:slug Bearer Delete a layout
POST /v1/layouts/import Bearer Infer a schema from a JSON sample and save it as a layout
Bundles
GET /v1/bundles API Key / Bearer List your bundles
POST /v1/bundles Bearer Create a bundle from layout IDs
PUT /v1/bundles/:slug Bearer Update a bundle
DELETE /v1/bundles/:slug Bearer Delete a bundle
Snapshots
GET /v1/snapshots API Key / Bearer List your snapshots
POST /v1/snapshots Bearer Generate a dataset and pin it as a snapshot
PUT /v1/snapshots/:slug Bearer Replace a snapshot’s name or data
DELETE /v1/snapshots/:slug Bearer Delete a snapshot
Types & templates
GET /v1/types API Key / Bearer The full data type catalog
GET /v1/types/:name API Key / Bearer One type’s details
GET /v1/templates API Key / Bearer List preset templates
GET /v1/templates/:id API Key / Bearer One template’s layout definitions
POST /v1/templates/:id/apply Bearer Create layouts from a template
Webhooks
GET /v1/webhooks API Key / Bearer List your webhooks
POST /v1/webhooks Bearer Subscribe a URL to events
DELETE /v1/webhooks/:id Bearer Delete a webhook
GET /v1/webhooks/:id/deliveries API Key / Bearer Recent delivery attempts
Account
GET /v1/account/usage API Key / Bearer This period's request and record usage
GET /v1/account/api-keys API Key / Bearer List your API keys
POST /v1/account/api-keys Bearer Create an API key
PUT /v1/account/api-keys/:id/deactivate Bearer Deactivate an API key
DELETE /v1/account/api-keys/:id Bearer Delete an API key

Every /v1 route needs credentials — there is no anonymous access. API keys are read-only, so rows marked Bearer need an OAuth token with the write scope. List endpoints accept limit (max 100) and offset.

Query Parameters for Data Generation

When calling GET /v1/layouts/:slug:

Parameter Type Default Description
count integer the layout’s own count Number of records to generate. A value above your plan’s per-request maximum is capped, not rejected.
locale string the layout’s own locale Data locale (en, ko, ja, zh). Use random to give each record a random locale — still reproducible with seed.
seed integer random Seed for reproducible data. The same seed gives the same output; 0 counts as no seed.
format string json Response format (json, csv)

A layout saved as a single object — or any request with count=1 — answers with one JSON object instead of an array. GET /v1/bundles/:slug takes seed and locale only; GET /v1/snapshots/:slug takes format only, since its data is already fixed.

Data Types

Apiryner ships 30 built-in data types, organized by category. Use the type field in your schema to pick each field’s generator; GET /v1/types returns the same catalog at runtime.

Basic

Type Description Options
integer Random integer within a range min, max
decimal Random decimal number min, max, precision
boolean Random true/false value true_rate
string Random string of characters length, charset
enum Pick from a list of values values

Nested

Type Description Options
object Nested JSON object properties (nested schema)
array Array of items items, min_items, max_items

Personal

Type Description Options
name Full person name (family name first for ko/ja/zh)
email Email address, built from the record’s own name when it has one
phone Phone number format
address Full address
city City name
zipcode Postal/ZIP code
street Street name with number

Text

Type Description Options
product_name Product name
word Random word
sentence Random sentence
paragraph Random paragraph sentences
description Descriptive text length (short/medium/long)

Date & Time

Type Description Options
date Random date from, to, format
datetime Random date and time, always RFC 3339 in UTC from, to
time Random time format

Finance

Type Description Options
currency Random monetary amount currency, min, max
price Product price (same generator as currency) currency, min, max

Identifiers

Type Description Options
uuid UUID v4
slug URL-friendly slug words
sequence Auto-incrementing number start, prefix

Media

Type Description Options
image_url Random image URL (placeholder) width, height
color Random color code format (hex/rgb/rgba)

Relation

Type Description Options
reference Pick a value from another layout's generated records layout, field, fallback

Relations & Conditions

Reference fields

A reference field pulls its value from another layout's generated records, so related layouts share consistent keys. Point it at a target layout (by slug) and one of that layout's fields:

{
  "id": { "type": "uuid" },
  "user_id": {
    "type": "reference",
    "options": { "layout": "users", "field": "id" }
  }
}
  • In a bundle — layouts are generated in dependency order, and reference fields pick from the records in the same bundle response. Include every referenced layout in the bundle.
  • Individual layout calls — HTTP requests are stateless, so a reference cannot reuse the records some earlier call returned. Each referenced layout is regenerated with your request's seed, its own array count, and its own locale. Two calls with the same seed therefore see identical parent records; a call with no seed sees fresh ones. To pin one parent dataset across several layouts, generate them together with a bundle call.
  • Validation — the target layout must exist on your account and must actually have the field you name. Saving a layout that breaks either rule is rejected, in the API and in the web editor alike.
  • Cycles — layouts that reference each other (directly or transitively) are rejected when saving and fail generation.
  • fallback is returned when the referenced layout has no generated records.

Conditional fields

Any field can carry a condition that decides per record whether the field is included, based on a previously generated field in the same record:

{
  "status": { "type": "enum", "options": { "values": ["active", "banned"] } },
  "banned_reason": {
    "type": "sentence",
    "condition": { "field": "status", "equals": "banned" }
  }
}
Operator Meaning Value
equals / not_equals Exact match against the referenced field any value
in / not_in Membership in a list array of values
gt / gte / lt / lte Numeric comparison number

When the condition doesn't match, the field is omitted from that record. The condition's field must name a field in the same object.

Bundles & Snapshots

Bundles

A bundle names a set of your layouts and generates them together in one response, so reference fields resolve against the very records you receive.

curl -H "X-API-Key: ak_your_key_here" \
  "https://api.apiryner.com/v1/bundles/shop?seed=42"

The response is an object keyed by layout slug:

{
  "users": [
    { "id": "8f0c1d2e-4b77-4c2a-9c31-2a7d0f6b1e55", "name": "Sarah Johnson" }
  ],
  "orders": [
    { "id": "b1a70c93-2f18-49d6-8f0a-6c5e2b9d4471",
      "user_id": "8f0c1d2e-4b77-4c2a-9c31-2a7d0f6b1e55", "total": 41.5 }
  ]
}
  • Layouts are generated in dependency order, so a reference always sees its target’s records.
  • A layout that references one outside the bundle gets an error under its own key; the rest of the bundle still generates.
  • Record counts come from the bundle’s config, falling back to each layout’s own count, and are capped by your plan.
  • seed and locale apply to the whole bundle. There is no count parameter and no CSV output.

Create one from the layout IDs it should hold:

curl -X POST https://api.apiryner.com/v1/bundles \
  -H "Authorization: Bearer oat_your_token" \
  -H "Content-Type: application/json" \
  -d '{"name": "Shop", "slug": "shop", "layout_ids": [12, 13]}'

Snapshots

A snapshot freezes one generated dataset so every call returns the same bytes — for demos, screenshots and fixtures that must not move under you.

curl -X POST https://api.apiryner.com/v1/snapshots \
  -H "Authorization: Bearer oat_your_token" \
  -H "Content-Type: application/json" \
  -d '{"layout_slug": "users", "name": "Demo users",
       "slug": "demo-users", "count": 20, "seed": 42}'
  • GET /v1/snapshots/:slug serves the stored data, and ?format=csv works as it does on a layout.
  • shapeauto (default), object or array — decides whether the snapshot answers with a single object or an array. Auto follows the layout.
  • PUT /v1/snapshots/:slug replaces name or data, so pinned records can be hand-edited.
  • Serving a snapshot still counts against your monthly record quota — pinned data is data the API delivers.

Webhooks

Subscribe an HTTP(S) URL to account events. The creation response carries the signing secret once; it is never returned again.

curl -X POST https://api.apiryner.com/v1/webhooks \
  -H "Authorization: Bearer oat_your_token" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/apiryner",
       "events": ["layout.updated", "usage.warning"]}'

Pass layout_ids to limit layout events to specific layouts; omit it to receive them for every layout.

Events

Event Fires when
layout.created A layout is created — through the API, the web editor or an MCP tool call
layout.updated A layout’s name, slug, schema or generation defaults change
layout.deleted A layout is deleted
snapshot.created A snapshot is pinned
snapshot.deleted A snapshot is deleted
bundle.created A bundle is created
usage.warning Monthly requests or records pass 80% of the limit
usage.limit_reached Monthly requests or records reach the limit

Delivery & verification

Each delivery is a JSON POST carrying two headers: X-Apiryner-Event with the event name, and X-Apiryner-Signature with an HMAC-SHA256 of the raw request body, keyed with your secret and hex-encoded. Verify it before trusting the payload:

const expected = crypto
  .createHmac("sha256", secret)
  .update(rawBody)          // the raw body, before JSON.parse
  .digest("hex");

if (expected !== req.headers["x-apiryner-signature"]) {
  return res.status(401).end();
}

Delivery times out after 10 seconds. Network errors and 408/429/5xx responses are retried twice — after 5 and 30 seconds — while any other 4xx is final. Every attempt is recorded; read them back with GET /v1/webhooks/:id/deliveries.

Options Reference

Each field in a schema can have a type and optional options object.

Schema field structure:

{
  "field_name": {
    "type": "integer",
    "options": {
      "min": 1,
      "max": 100
    }
  }
}

Every pool-backed type — name, email, phone, the address types, the text types and slug — also accepts a locale option that overrides the request’s locale for that one field. A field that overrides the locale generates its own identity rather than sharing the record’s person:

{
  "name": {"type": "name"},
  "korean_name": {"type": "name", "options": {"locale": "ko"}}
}

Nested object example:

{
  "address": {
    "type": "object",
    "properties": {
      "street": {"type": "street"},
      "city": {"type": "city"},
      "zipcode": {"type": "zipcode"}
    }
  }
}

Array example:

{
  "tags": {
    "type": "array",
    "items": {"type": "word"},
    "options": {"min_items": 2, "max_items": 5}
  }
}

Enum example:

{
  "status": {
    "type": "enum",
    "options": {
      "values": ["active", "inactive", "pending"]
    }
  }
}

Errors & Limits

Errors come back as JSON with an error code and, in most cases, a human-readable message:

{
  "error": "forbidden",
  "message": "API keys are read-only. Use an OAuth Bearer token for write operations."
}
Status error When
400 invalid request Malformed body, an invalid slug, or a schema the generator cannot read
401 unauthorized Missing, invalid, inactive or revoked credentials
403 forbidden A write attempted with an API key — API keys are read-only
403 insufficient_scope The OAuth token lacks the read or write scope (also sent as WWW-Authenticate)
403 layout_limit_reached Your plan’s layout limit is already reached
404 <resource> not found No layout, bundle, snapshot or template with that slug on your account
429 rate_limit_exceeded Per-minute request rate exceeded — Retry-After says when to retry
429 quota_exceeded The monthly request or record limit is used up

Rate limits & quota

Every response carries the per-minute budget:

  • X-RateLimit-Limit — requests allowed per minute on your plan
  • X-RateLimit-Remaining — how many are left in the current window
  • X-RateLimit-Reset — when the window resets, as a Unix timestamp
  • Retry-After — seconds to wait, sent only with a 429

Monthly quota is counted in two dimensions, requests and records; a bundle call counts as one request plus every record it returns. Read the current period with GET /v1/account/usage:

{
  "requests": 128,
  "records": 4210,
  "limit_requests": 1000,
  "limit_records": 10000,
  "period": "2026-08"
}

A count above your plan’s per-request maximum is capped rather than rejected, so a request never fails for asking too much. Layout, API key and nesting-depth limits are enforced the same way, at the point of use.

MCP Server

AI coding agents — Claude Code, Cursor, Claude Desktop — can generate mock data and manage your layouts through Model Context Protocol tool calls. Connect to the hosted endpoint, or run the server locally from the CLI.

Remote server — nothing to install

Point your agent at the hosted endpoint and authorize it in the browser. No npm package, no API key to copy.

claude mcp add --transport http apiryner https://api.apiryner.com/mcp

Your agent will report that the server needs authentication; approving it in the browser is the whole setup. To use an API key instead of the browser flow:

claude mcp add --transport http apiryner https://api.apiryner.com/mcp \
  --header "X-API-Key: ak_your_key_here"

Local server

Runs the server from the apiryner CLI on your own machine. Its schema tools work offline, before you have an account.

Add it to Claude Code:

claude mcp add apiryner -- npx apiryner mcp

Or configure it by JSON (mcpServers):

{
  "mcpServers": {
    "apiryner": {
      "command": "npx",
      "args": ["apiryner", "mcp"],
      "env": { "APIRYNER_API_KEY": "ak_your_key_here" }
    }
  }
}

Tools

Tool Auth Arguments
list_types optional
validate_schema schema
infer_schema input, type
quick_generate schema, count, seed
list_layouts required limit, offset
create_layout required name, slug, schema, locale, is_array, array_count
update_layout required slug plus the fields to change
delete_layout required slug
generate required slug, count, seed, locale, format
generate_bundle required slug
get_snapshot required slug

infer_schema and generate_bundle are on the local server only for now.

Authentication

validate_schema, infer_schema, and quick_generate run entirely on your machine — no account, no network. An agent can build fixtures or turn an OpenAPI file into a layout schema before you have signed up.

list_types is what keeps an agent from inventing field types. Authenticated, it returns this page’s full type catalog with descriptions and supported locales; unauthenticated, it falls back to the type names built into the package rather than failing.

The remote server authorizes through your browser: your agent registers itself, you approve it once, and the token it receives is bound to this endpoint alone. An X-API-Key header works too, and keeps the read-only limits an API key always has.

For the local server, credentials are resolved on the first tool call that needs the API, in this order:

  1. --key <key> passed after apiryner mcp, or $APIRYNER_API_KEY
  2. the OAuth session from apiryner login, refreshed automatically when expired
  3. the API key stored by apiryner config set api-key <key>

Starting the server unauthenticated is fine: the account-backed tools explain what is missing and begin working the moment you log in, with no restart. Note that create_layout, update_layout and delete_layout write to your account, and that a delete cannot be undone.