data.placeholder.com/api

Mock REST API

A free fake REST API for prototypes, tutorials, tests and AI agents, in the style of JSONPlaceholder and json-server. Eight related resources, filters, sorting, paging and simulated writes. Same URL, same bytes, forever.

const posts = await fetch("https://data.placeholder.com/api/posts?userId=1").then(r => r.json());

Resources

ResourceCountBelongs toNested routes
/api/users10/api/users/1/posts, /albums, /todos, /orders
/api/posts100userId/api/posts/1/comments
/api/comments500postId
/api/albums100userId/api/albums/1/photos
/api/photos5000albumId
/api/todos200userId
/api/products50
/api/orders100userId

Children are blocked per parent like JSONPlaceholder: user 1 owns posts 1 to 10, post 1 owns comments 1 to 5, album 1 owns photos 1 to 50. /api returns a machine-readable index of all of this.

Users are synthetic personas: user N is persona <country>-N, with a realistic name, address, example.com e-mail, fictional-range phone and a matching avatar. By default the ten users cycle through US, GB, SE and NO; ?country=SE makes them all Swedish (SE, NO, GB, US available).

Examples

Query parameters

ParameterMeaning
<field>=<value>Exact match. Dot paths reach nested fields (address.city). Repeat a key for OR (?id=1&id=2).
<field>_ne, _like, _gte, _lte, _gt, _ltNot equal, case-insensitive substring, and comparisons (numbers or strings).
qCase-insensitive full-text search over all string values.
_sort, _order_sort=title&_order=desc or _sort=-title. Comma lists sort by several fields.
_page, _limitPagination. _limit defaults to 10 when _page is set. Adds an RFC 8288 Link header (first, prev, next, last).
_start, _end, _limitSlicing, json-server style.
_embedInclude children: /api/posts?_embed=comments, /api/users/1?_embed=posts,todos.
_expandInclude the parent: /api/posts?_expand=user, /api/comments/1?_expand=post.
countryUsers from one persona country; also applies to _expand=user.

List responses carry X-Total-Count (matches before paging). Both it and Link are exposed to browser JavaScript via CORS. Bad parameters return 400 with a JSON message saying what is allowed.

Writes

Writes behave like a real API but nothing is stored, and the next GET returns the original data:

RequestResponse
POST /api/<resource>201, your body with a new id (count + 1) and a Location header. A nested POST /api/posts/1/comments also sets postId.
PUT / PATCH /api/<resource>/<id>200, the stored object shallow-merged with your body
DELETE /api/<resource>/<id>200, {}
curl -X POST https://data.placeholder.com/api/posts \
  -H "Content-Type: application/json" \
  -d '{"title": "Hello", "userId": 1}'
# 201 Created, Location: /api/posts/101
# {"title": "Hello", "userId": 1, "id": 101}

Bodies must be JSON objects, up to 1 MB. Write responses are Cache-Control: no-store.

Images in the data

photos.url, photos.thumbnailUrl, products.image and products.thumbnail point at classic via.placeholder.com images, and users.avatar at an avatar. All of them are caught by placeholder-check if they end up in a build.

Determinism

All GET responses are frozen (v1) and cached for a year. Every value is derived from a hash of a fixed key, never from the clock or a random number generator, so tests can assert on exact values.