Back to the builder

Mock API Builder guide

Build a fake REST API in your browser and call it from anywhere. This guide walks through the builder from your first endpoint to fake data, lists, paging, and conditional responses.

Open the builder
On this page

Quick start

  1. Add an endpoint

    Click Add Endpoint, pick a method, and type a path such as /users or /users/:id.
  2. Write the response

    Choose a status code and type, then write the body. Plain JSON works as is. Add placeholders like {{faker.person.fullName}} when you want a fresh value on every call.
  3. Call it

    The endpoint editor shows a ready-made curl command for the selected endpoint. Copy it, or copy the Base URL from the toolbar and add your path. On a phone the toolbar hides the base URL, so use the curl command.

A GET /users/:id endpoint with this body:

Response body
{
  "id": "{{params.id}}",
  "name": "{{faker.person.fullName}}",
  "email": "{{faker.internet.email}}"
}

answers a request for user 42 like this:

Terminal
$ curl https://webtoolz.dev/api/mock-api/YOUR_CONFIG/users/42

{
  "id": "42",
  "name": "Maria Lopez",
  "email": "maria.lopez@example.net"
}
Tip: Pick a template to start from a working API you can call right away and then change.

How the URL works

There is no account and no server to set up. Your whole API (every endpoint, response, and rule) is packed into one piece of the URL. The mock server reads that piece on each request and answers from it.

The same piece shows up in three addresses, so if you have one you can get to the other two:

  • /api/mock-api/CONFIG/your/path
    The live API your app calls
  • /json/mock-api/CONFIG
    The builder, opened with this API loaded
  • /json/mock-api/docs/CONFIG
    Interactive docs for this API

Because the URL is the API, the URL changes whenever you edit something. The builder also keeps your current work in this browser, so it is still there when you come back.

Watch out: After you change an endpoint, the old URL still serves the old version. If your app or a teammate uses the URL, copy the new one (or click Share again) after each change.

Endpoints and paths

An endpoint is a method plus a path. The builder supports GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. You can reuse a path with different methods, such as GET /users and POST /users. If two endpoints end up with the same method and path, the path field turns red, and only the higher one is ever used.

Path parameters

Start a segment with : to accept any value there. The value is available in the response as {{params.name}}.

Paths and the placeholders they give you
/users
/users/:id                          ->  {{params.id}}
/projects/:projectId/tasks/:taskId  ->  {{params.projectId}}, {{params.taskId}}

How a request finds its endpoint

  • The method must match exactly. A HEAD request needs its own HEAD endpoint; it does not fall back to GET.
  • The path must have the same number of segments, and fixed segments are case-sensitive. /users/42/posts does not match /users/:id.
  • A trailing slash makes no difference: /users/ matches /users.
  • Endpoints are checked from the top of the list down, and the first match wins.
Watch out: Order matters when a fixed path overlaps a parameter. If GET /users/:id sits above GET /users/me, a request for /users/me goes to the first one. New endpoints are added at the bottom, so add the fixed path first.

Sample request body

POST, PUT, and PATCH endpoints have a Sample Request Body field. It fills in the curl command and the request form on the docs page, so whoever tests the endpoint starts with a valid body. Generate from templates builds one from the {{body.*}} fields your responses read.

Adding, copying, and deleting

Add Endpoint always creates GET /, so change its method or path before adding another. To copy an endpoint with all its responses, use the duplicate button on its card; the copy gets a -2 suffix on its path. The trash button on a card deletes the endpoint, and once an endpoint has two or more responses, each response has its own delete button.

Watch out: Deleting does not ask for confirmation and cannot be undone. Copy the URL first if you might want the endpoint back.

Responses

Each response has these settings:

  • Status
    The HTTP status code. Pick from the common codes: 200, 201, 202, 204, 301, 302, 304, 307, 400, 401, 403, 404, 405, 409, 422, 429, 500, 502, 503, 504.
  • Type
    JSON, Text, HTML, or XML. Sets the Content-Type header.
  • Delay (ms)
    Waits before answering. See below.
  • Collection
    JSON only. Lets the caller page, filter, and sort an array. See Paging, filtering, sorting.
  • Response Body
    What the endpoint returns. Can include placeholders.
  • Response Headers
    Extra headers to send back, such as X-Api-Version. Sent exactly as written; placeholders are not filled in here.

Leave the body empty for responses like 204 No Content. HEAD requests get the status and headers with no body.

Delay

Set a delay to make the endpoint feel like a slow network. It is the easiest way to check loading spinners, timeouts, and retry logic. The maximum is 10,000 ms.

Headers, CORS, and caching

Every response allows calls from any origin, so you can use the mock from localhost or any site without a proxy. Preflight OPTIONS requests are answered for you.

A GET or HEAD response that is the same on every call (no random values, nothing read from headers or the body, no delay, no sequence) and has a status like 200, 204, or 404 is cached. That never shows you stale data, because any edit gives the API a new URL.

Watch out: Because OPTIONS is always answered automatically with an empty 204, an OPTIONS endpoint you add is never served. Some headers are also reserved: Set-Cookie, Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, Content-Disposition, the CORS allow headers, and the CDN cache headers are dropped, and Link, X-Powered-By, and the X-Mock-* headers are replaced with the mock's own.

HTML responses are served with a strict security policy, so a page you return displays as static markup. Scripts, images, and outside stylesheets in it do not load; inline styles do.

Dynamic values

A placeholder is text in double curly braces. When a request comes in, each placeholder is replaced with a value. Use them anywhere in the body.

Values from the request

  • {{params.id}}
    Gives you: A path parameter
    Example: /users/42 -> 42
  • {{query.page}}
    Gives you: A query string value
    Example: ?page=2 -> 2
  • {{headers.authorization}}
    Gives you: A request header (name in any case)
    Example: Bearer abc123
  • {{body.user.email}}
    Gives you: A field from a JSON request body. Use dots for nested fields.
    Example: jane@example.com

If the query value, header, or body field is missing, the placeholder becomes an empty string. The same happens to every {{body.*}} placeholder when the request body is not valid JSON. Objects and arrays from the body are inserted as JSON. When a query parameter appears more than once, as in ?tag=a&tag=b, only the last value is used, here and in rules and collection filters.

Generated values

  • {{uuid}}
    Gives you: A new random UUID each time it appears
    Example: 3b1f8c2e-...
  • {{now}}
    Gives you: The current time as an ISO 8601 string
    Example: 2026-11-02T09:30:00.000Z
  • {{timestamp}}
    Gives you: The current time in Unix milliseconds
    Example: 1793611800000
  • {{int 1 100}}
    Gives you: A whole number in the range, inclusive
    Example: 57
  • {{oneOf 'on' 'off'}}
    Gives you: One of the options, picked at random. Quote each option; quotes let an option contain spaces.
    Example: off
  • {{faker.person.fullName}}
    Gives you: Realistic fake data. See Fake data below.
    Example: Maria Lopez

{{now}} and {{timestamp}} have the same value everywhere in one response, so a createdAt and updatedAt set from them will match.

Quotes and types

A placeholder is replaced as plain text, so you decide the JSON type. Put it inside quotes for a string, or leave the quotes off for a number or boolean:

Response body
{
  "id": "{{uuid}}",
  "age": {{int 18 75}},
  "active": {{faker.datatype.boolean}},
  "page": {{query.page}}
}
Watch out: An unquoted placeholder that comes out empty breaks the JSON. In the example above, a request without ?page= returns "page": }. Quote request values unless you are sure the caller always sends them.

For HTML and XML responses, inserted values are escaped, so a < in a query value shows up as text instead of markup. A placeholder the builder does not recognize, such as a typo, is left in the response exactly as you wrote it, which makes mistakes easy to spot.

Fake data

Placeholders that start with faker. call Faker.js and return a new value on every request. These are the ones people use most:

  • {{faker.person.fullName}}
    John Smith
  • {{faker.internet.email}}
    john@example.com
  • {{faker.number.int(1,1000)}}
    427
  • {{faker.lorem.sentence}}
    Lorem ipsum dolor sit amet.
  • {{faker.date.past}}
    2024-03-15T...
  • {{faker.commerce.price}}
    29.99
  • {{faker.location.city}}
    San Francisco
  • {{faker.image.avatar}}
    https://avatars.githubusercontent.com/...

Arguments

  • A range for numbers: {{faker.number.int(1,100)}} or {{faker.number.float(0,10)}}.
  • A single count or length: {{faker.lorem.words(3)}} or {{faker.string.alphanumeric(8)}}.

Two arguments only work on number.int and number.float. On any other method both are ignored and it uses its default.

Which methods work

Most methods from these Faker modules work, not only the ones listed here: person, internet, number, lorem, image, location, commerce, company, date, phone, finance, datatype, string, animal, color, food, music, science. A method that returns an object is inserted as JSON, so leave it unquoted. Methods that need an options object, such as faker.date.between, are not supported and stay as text.

Show every method in the builder's reference panel

Person

faker.person.fullName
John Smith
faker.person.firstName
John
faker.person.lastName
Smith
faker.person.jobTitle
Software Engineer
faker.person.bio
Short bio text

Internet

faker.internet.email
john@example.com
faker.internet.username
john_doe42
faker.internet.url
https://example.com
faker.internet.ip
192.168.1.1
faker.internet.password
aB3dEf9Gh

Number

faker.number.int(1,1000)
427
faker.number.float(0,100)
73.42

Lorem

faker.lorem.sentence
Lorem ipsum dolor sit amet.
faker.lorem.paragraph
A full paragraph...
faker.lorem.word
consequatur
faker.lorem.words(3)
velit aut magni

Image

faker.image.avatar
https://avatars.githubusercontent.com/...
faker.image.url
https://picsum.photos/...

Location

faker.location.city
San Francisco
faker.location.country
United States
faker.location.zipCode
94102
faker.location.streetAddress
123 Main St
faker.location.latitude
37.7749
faker.location.longitude
-122.4194

Commerce

faker.commerce.price
29.99
faker.commerce.productName
Ergonomic Chair
faker.commerce.productDescription
A comfortable chair...

Company

faker.company.name
Acme Corp
faker.company.catchPhrase
Innovative solutions

Date

faker.date.past
2024-03-15T...
faker.date.future
2027-08-22T...
faker.date.recent
2026-02-28T...

Phone

faker.phone.number
+1-555-123-4567

Finance

faker.finance.amount
542.32
faker.finance.currencyCode
USD
faker.finance.bitcoinAddress
3J98t1Wp...

Datatype

faker.datatype.boolean
true

String

faker.string.uuid
a1b2c3d4-...
faker.string.alphanumeric(8)
a8Kd02Qz

Other

faker.animal.type
dog
faker.color.human
blue
faker.food.dish
Spaghetti
faker.music.genre
Rock

The same list sits under Faker Reference at the bottom of the endpoint editor. Its Helpers group has the list helpers from the next section. Click any entry to copy it.

Lists

To return an array, wrap one item in {{#repeat N}} and {{/repeat}}, and put the block between the square brackets. Each copy gets its own fake data, and the commas between items are added for you.

Response body
[
  {{#repeat 5}}
  {
    "id": {{index1}},
    "name": "{{faker.person.fullName}}",
    "role": "{{oneOf 'admin' 'editor' 'viewer'}}"
  }
  {{/repeat}}
]

That returns five users with ids 1 to 5.

  • {{#repeat 5}}
    Exactly 5 items
  • {{#repeat 5 20}}
    A random number of items between 5 and 20
  • {{#repeat query.count}}
    As many items as the caller asks for with ?count=
  • {{index}}
    The position inside the block, starting at 0
  • {{index1}}
    The position, starting at 1. Handy for ids.

Blocks can be nested, so each item can have its own list. Inside a nested block, {{index}} refers to the innermost one. One response holds at most 1,000 repeated items in total, counting nested blocks.

Watch out: Do not type commas between items yourself, and do not leave a comma after the closing brace inside the block. Either one produces invalid JSON. If a block has no matching {{/repeat}}, it is left in the response as plain text.

More than one response

Click Add Response to give an endpoint a second response. Once it has two or more, you choose how the endpoint picks between them: Sequence (the default) or Conditional.

Sequence: a different response on each call

The endpoint goes through the responses in order (Call #1, Call #2, and so on) and starts again from the first after the last one. Use it to test retries, polling, or a token that expires.

Sequence with two responses
Call #1  ->  500  {"error": "Server busy"}
Call #2  ->  200  {"status": "ok"}
Call #3  ->  500  (back to the first)
Watch out: The call count lives on the server, not with each caller. Anyone else calling the same URL moves it forward, different server instances may each keep their own count, and a restart sets it back to the first response. Use a sequence to watch retry behavior by hand, not to assert an exact call number in an automated test.

Conditional: pick a response by what the request contains

When you switch to Conditional, every response except the last gets rule fields to fill in, and the last one becomes the fallback. Each response with rules can check:

  • Query param matches
    Checks: A query string value, such as status
    Example pattern: ^active$
  • Header matches
    Checks: A request header. The name can be in any case.
    Example pattern: ^Bearer .+
  • Path param matches
    Checks: A :param from the path. Only offered when the path has one.
    Example pattern: ^999$
  • Body
    Checks: The raw request body text. Only offered for POST, PUT, and PATCH.
    Example pattern: "role":\s*"admin"
  • Responses with rules are checked from the top. The first one whose rules all match is returned.
  • If none match, the first response without rules is returned. A response whose rule fields are all left empty counts as having no rules.
  • Every rule is a regular expression and matches anywhere in the value. admin also matches superadmin, so use ^admin$ for an exact match.
  • A rule on a query param, header, or path param that the request does not have never matches.
  • An invalid pattern, a pattern with a repeated group such as (cat|dog)+, or a value over 2,000 characters is compared as plain text instead: the rule only matches if the value contains the pattern exactly as typed. Write ^(cat|dog)$ rather than repeating a group.
POST /auth/login
Response 1   200  {"token": "{{uuid}}"}
             when body matches  "password":\s*"s3cret"

Response 2   401  {"error": "Invalid credentials"}
             fallback
Tip: The Auth Endpoints template has this login endpoint and a sequence that expires a token, ready to try.

Paging, filtering, sorting

Tick Collection: paginate, filter, sort on a JSON response whose body is an array. The endpoint then understands the same query parameters as json-server. Pair it with a {{#repeat}} block to get a large list to work with.

  • ?_page=2&_limit=10
    Page 2, 10 items per page. Without _limit a page has 10 items. page and limit work too.
  • ?_start=20&_end=30
    A slice by position, counting from 0. _limit on its own returns the first N items.
  • ?_sort=name&_order=desc
    Sort by a field. The order is asc unless you pass desc.
  • ?role=admin
    Only items whose field equals the value exactly
  • ?age_gte=18&age_lte=65
    Greater than or equal, less than or equal
  • ?role_ne=viewer
    Not equal
  • ?name_like=ann
    Contains the text, ignoring case
  • ?q=jane
    Search every field of every item
  • ?author.name=Jane
    Filter or sort on a nested field with dots

Filters are applied first, then search, then sorting, then paging. Every collection response has an X-Total-Count header with the number of matching items before paging. Paged responses also get a Link header with the first, previous, next, and last page URLs. _limit is capped at 1,000.

Watch out: Any other query parameter is treated as a filter. If your app sends something like ?apiKey=abc, the list comes back empty because no item has an apiKey field with that value. The same goes for {{#repeat query.count}}: with Collection on, build a fixed number of items and let the caller use _limit. A body that is a single object instead of an array is returned unchanged.

Share, docs, and links

The toolbar

  • Untitled API
    Click to name your API (up to 40 characters). The name shows on the docs page.
  • Base URL
    The live address of your API. Add an endpoint path to it to make a request. Hidden on phone screens.
  • Links
    Copies the endpoint, builder, docs, or OpenAPI spec address.
  • Size bar
    How much of the 50 KB size budget your API uses. It turns amber, then red, as it fills.
  • Share
    Copies a link to the builder with your API loaded. See below.
  • Docs
    Opens the interactive docs in a new tab.
  • Export
    Downloads the API in another format. See Import and export.
  • Clear
    Removes every endpoint from the builder at once.

On screens narrower than a tablet, the endpoint list and the editor are two tabs, Endpoints and Editor. Picking a different endpoint switches to the editor; to reopen the one already selected, tap Editor. On wider screens they sit side by side, and you can drag the divider between them.

Watch out: Clear does not ask first. Copy the URL before you clear if you might want the API back.

Share and short links

Share copies a builder link to your clipboard. A small API, whose packed config is 1,200 characters or fewer, gets the full link. A bigger API is saved on our server instead, and you get a short link that starts with ~, such as /json/mock-api/~Xk3p9QzL2mN8vR4tW7yB1c. The base URL and docs link switch to the short form too.

  • A short link stays valid as long as people keep using it. One that goes unused for a long time can expire.
  • A full link holds the whole API, so it never expires.
  • If saving fails, Share copies the full link instead, so you always get a working link.
  • Anyone with the link can open the API in the builder and change it. Their changes make a new URL and never touch yours.

Interactive docs

Docs opens an API reference built from your endpoints: methods, paths, parameters, and example responses. Each endpoint has a Test Request button that sends a real request to the mock and shows the answer, with the sample request body filled in. Send the docs link to whoever builds against the API. The docs page has its own Share button that copies the docs link, and Edit in Builder opens the same API for editing.

The OpenAPI spec for the docs lives at /api/mock-api-docs/CONFIG/openapi.json, so tools that read OpenAPI can load it directly.

Open a mock you were sent

If someone gave you a mock URL, even just the endpoint an app calls, you can open the whole API from it. Pick whichever of these is easiest:

  • Paste it. On an empty builder, paste the link into Have a mock API URL? and press Enter or the arrow button. Endpoint, docs, and builder links all work.
  • Open it in a browser. Visiting the base URL shows a short overview page with Open in Builder, View Docs, and OpenAPI spec links, unless the API has its own GET / endpoint.
  • Change the address. Replace /api/mock-api/ with /json/mock-api/ and remove the endpoint path after the config.
From an endpoint to the builder and docs
Endpoint:  https://webtoolz.dev/api/mock-api/~Xk3p9QzL2mN8vR4tW7yB1c/users
Builder:   https://webtoolz.dev/json/mock-api/~Xk3p9QzL2mN8vR4tW7yB1c
Docs:      https://webtoolz.dev/json/mock-api/docs/~Xk3p9QzL2mN8vR4tW7yB1c

Responses carry the way back too. A request that matches no endpoint gets a 404 whose JSON body lists the builder, docs, and spec links. Responses from a short link or a small API also carry X-Mock-Builder and X-Mock-Docs headers.

Import and export

Import

Import OpenAPI, below the endpoint list, accepts an OpenAPI 3.x or Swagger 2.0 document in JSON or YAML, or a config you exported from here. Paste it or choose a file. The builder creates an endpoint for each operation, with Faker placeholders matched to field names so the responses look real. Then choose:

  • On an empty builder, Import loads them.
  • If you already have endpoints, Replace all starts over with the imported ones, and Add to current keeps yours and skips any with a method and path you already have.

A spec with more than 100 operations is cut at 100, and anything the importer had to skip, such as an external reference, is listed before you confirm.

Export

  • Config (JSON)
    A backup you can import here later
  • OpenAPI spec
    Swagger UI, code generators, and other OpenAPI tools (OpenAPI 3.1)
  • Postman collection
    A ready-to-send collection pointed at your mock, with path variables and sample request bodies filled in
  • Mockoon environment
    Running the API locally in Mockoon. Sequences, conditional rules, delays, and headers carry over. Placeholders are kept as written, since Mockoon uses its own syntax.
  • json-server db.json
    Seed data for json-server. Only GET endpoints that return an array are included, with placeholders filled in. Sequence and conditional responses are left out.

When an export has to leave something out, a note tells you what.

Templates

From Template, below the endpoint list, loads a complete example API. If you already have endpoints, the builder asks before replacing them.

REST CRUD

A list that pages, filters, and sorts; a delayed lookup; and create, update, delete

  • GET /users
  • GET /users/:id
  • POST /users
  • PUT /users/:id
  • DELETE /users/:id

Health Check

A single status endpoint with the current time

  • GET /health

Auth Endpoints

A conditional login, a refresh that expires on the second call, and logout

  • POST /auth/login
  • POST /auth/refresh
  • POST /auth/logout

Limits

  • Endpoints per API
    100
  • Responses per endpoint
    50
  • Response body you write
    100,000 characters
  • Path length
    500 characters
  • Whole API, compressed
    Aim to stay under 50 KB, the budget the toolbar size bar shows. Past about 70,000 characters the base URL answers 413 Config too large.
  • API name
    40 characters
  • Delay
    10,000 ms
  • Repeated items in one response
    1,000
  • Rendered response
    256,000 characters. Longer output is cut off.
  • Collection _limit
    1,000

The builder does not stop you at the first four. An API over any of them is rejected when it is called: the base URL answers 400 Invalid config.

Troubleshooting

The base URL returns an error instead of my API
The server could not load the API itself, so it never got as far as your endpoints. The message tells you why:
  • 404 Config not found: a short link has expired or was mistyped.
  • 400 Invalid config: the config part of the URL was cut off or changed, or the API is over one of the limits above.
  • 413 Config too large: the API is too big for a URL. Remove endpoints or shorten bodies until the size bar is under 100%.
  • 502 Config temporarily unavailable: a short link could not be looked up just now. Try again in a moment.
I get 404 No matching endpoint
The method and path have to match an endpoint exactly, segment for segment. The 404 body shows the method and path the server received, so compare them with your list. Remember that /users/:id does not match /users/42/posts.
My app still gets the old response
Editing changes the URL. Copy the new base URL from the toolbar, or click Share for a new link, and update your app.
A placeholder shows up as text in the response
It was not recognized. Check the spelling, check that a faker. method exists in one of the supported modules, and check that every {{#repeat}} has a matching {{/repeat}}.
The response is not valid JSON
Look for commas typed between repeated items, an unquoted request value that came out empty, or a Faker method that returns an object placed inside quotes.
A conditional endpoint always returns the fallback
Rules are regular expressions on the raw value. Body rules see the body exactly as sent, spaces included, so write "role":\s*"admin" rather than "role":"admin". The editor warns you when a path param rule names a parameter that is not in the path.
A collection comes back as an empty array
Every query parameter that is not a paging, sorting, or search control is used as a filter. Remove extra parameters such as an API key, or turn off Collection on that response. If the list uses {{#repeat query.count}}, the count parameter is the culprit.
A short link says it has expired
Short links that go unused for a long time are removed. Ask the sender for a fresh link, or keep a Config (JSON) export as a backup you can import again.

FAQ

Do I need an account or anything installed?

No. The builder runs in your browser and the mock server is hosted for you. There is nothing to sign up for.

Where is my API stored?

In the URL. The builder also keeps your current work in this browser so it is there when you come back. When Share makes a short link, it saves a copy on our server so the link can find it.

Can other people see my API?

Anyone with one of its URLs can read every endpoint and response. Do not put real passwords, keys, or personal data in a mock.

Can I use it in automated tests?

Yes. Point your tests at the base URL. For results you can count on, use fixed responses or conditional rules rather than sequences, since the sequence position is kept on the server and can be moved by other callers or reset.

Can an AI agent use it?

Yes. The docs page and the OpenAPI spec describe every endpoint, and each response links back to them in its headers, so an agent handed one URL can find the rest.

I only need one endpoint that returns some JSON. Is there something quicker?

Yes. In JSON Studio, paste your JSON and click Instant API. You get a live endpoint without building anything.