
JSONPlaceholder API: Free Fake REST API by Typicode
JSONPlaceholder is a free fake REST API by Typicode at https://jsonplaceholder.typicode.com. It serves fixed sample data (posts, comments, albums, photos, todos and users) over plain HTTP. You need no signup and no API key, and it answers cross-origin requests from the browser. Writes (POST, PUT, PATCH, DELETE) return realistic responses, but nothing is saved.
Below is everything you need to use it well: the endpoints, working fetch and Python examples, the query parameters it actually supports (tested in October 2026), its limits, and when to switch to something else.
JSONPlaceholder endpoints and resources
The JSONPlaceholder home page lists six resources. The counts below were checked against the live API in October 2026.
| Resource | List endpoint | Items | Linked by |
|---|---|---|---|
| Posts | /posts |
100 | userId |
| Comments | /comments |
500 | postId |
| Albums | /albums |
100 | userId |
| Photos | /photos |
5,000 | albumId |
| Todos | /todos |
200 | userId |
| Users | /users |
10 |
Every resource supports the standard routes: GET /posts, GET /posts/1, POST /posts, PUT /posts/1, PATCH /posts/1 and DELETE /posts/1.
One level of nesting is supported (guide):
/posts/1/comments/albums/1/photos/users/1/albums/users/1/todos/users/1/posts
/posts/1/comments returns the same five comments as /comments?postId=1.
A user object is the richest record. It has nested address (with geo coordinates) and company objects, which makes it useful for testing nested rendering:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": { "street": "Kulas Light", "city": "Gwenborough", "geo": { "lat": "-37.3159", "lng": "81.1496" } },
"company": { "name": "Romaguera-Crona" }
}
(Shortened. The real response also has suite, zipcode, phone, website and more company fields.) Photo records now point to picsum.photos image URLs.
Make your first request
JavaScript (fetch)
This works in any modern browser console and in Node.js 18 or later, both of which have a global fetch:
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const todo = await res.json();
console.log(todo);
// { userId: 1, id: 1, title: 'delectus aut autem', completed: false }
Create a post (fake write)
const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-type': 'application/json; charset=UTF-8' },
body: JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }),
});
console.log(res.status); // 201
console.log(await res.json()); // { title: 'foo', body: 'bar', userId: 1, id: 101 }
PUT /posts/1 echoes back the object you send, PATCH /posts/1 returns post 1 with your changed fields merged in, and DELETE /posts/1 returns 200 with {}.
Python (requests)
import requests
BASE = "https://jsonplaceholder.typicode.com"
posts = requests.get(f"{BASE}/posts", params={"userId": 1}, timeout=10).json()
print(len(posts)) # 10
new = requests.post(f"{BASE}/posts", json={"title": "foo", "body": "bar", "userId": 1}, timeout=10)
print(new.status_code, new.json()["id"]) # 201 101
curl
curl https://jsonplaceholder.typicode.com/users/1/todos
Filtering, pagination, sorting and relations
The official guide only documents basic filtering. JSONPlaceholder runs on json-server (the classic v0.17 behavior), so more query parameters work. Each line below was tested against the live API in October 2026:
| What you want | Request | Result |
|---|---|---|
| Filter by field | /posts?userId=1 |
10 posts |
| Combine filters | /todos?userId=1&completed=true |
user 1's completed todos |
| Match any of several values | /comments?postId=1&postId=2 |
10 comments |
| Paginate | /posts?_page=2&_limit=3 |
posts 4, 5, 6 |
| Slice | /posts?_start=5&_end=8 |
posts 6, 7, 8 |
| Sort | /posts?_sort=id&_order=desc&_limit=3 |
posts 100, 99, 98 |
| Range | /posts?id_gte=98 |
posts 98 to 100 |
| Full-text search | /posts?q=qui est esse |
matching posts |
| Embed children | /posts/1?_embed=comments |
post with a comments array |
| Expand parent | /posts/1?_expand=user |
post with a user object |
Paginated responses include an X-Total-Count header (100 for posts) and a Link header with first, prev, next and last URLs. Both are exposed through CORS, so browser code can read them:
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_page=1&_limit=10');
const total = Number(res.headers.get('X-Total-Count')); // 100
const pages = Math.ceil(total / 10); // 10
Because these parameters are not part of the official guide, treat them as convenient but undocumented.
Common mistakes and limitations
- Nothing persists. After
POST /postsreturnsid: 101,GET /posts/101returns404, andPUT /posts/101fails with a server error. If your UI flow creates and then edits an item, keep the created object in local state rather than re-fetching it. - Every new post gets id 101. That is the next id after the 100 fixed posts. If your app uses ids as React keys, two "created" posts will collide.
- Send JSON with a Content-Type header. Without
Content-type: application/json, the body is not parsed as JSON and the response is just{ "id": 101 }, without your fields. - Fixed placeholder data. You cannot add fields or change values, and the text is lorem ipsum. Error cases also cannot be simulated, apart from
404for ids that don't exist. - It is a shared public service. Responses are cached at the CDN. Don't use it for load testing, and don't make production code or automated CI tests depend on it. For CI, run a local mock (see below) so a network blip can't fail your build.
When to use something else
| Need | Better option |
|---|---|
| Your own schema, persisted writes, offline or CI | json-server locally: npx json-server db.json serves your own db.json at http://localhost:3000 |
| Product, cart or auth-shaped fake data | DummyJSON |
| Mocking requests inside frontend tests | Mock Service Worker (MSW) intercepts fetch in the browser and Node |
| A shared, hosted mock for a team | an API platform's mock server, such as Postman's |
Note: as of October 2026, the current json-server on npm is the v1 beta. It uses different query syntax than JSONPlaceholder, for example _per_page instead of _limit and views:gt=100 instead of views_gte=100, so check its README before copying URLs over.
For more free APIs to practice with, browse DevHunt's fake data and testing APIs and the full free APIs directory. If you are choosing an API client for these experiments, see the free Postman alternatives and our Postman basics guide.
FAQ
Does JSONPlaceholder need an API key?
No. There is no signup, key or token. Every endpoint is public over https (and http), and the home page says it is free for everyone.
Does JSONPlaceholder work with CORS from the browser?
Yes. It answers preflight requests and returns Access-Control-Allow-Origin for your page's origin, so fetch from localhost or any site works without a proxy.
Why doesn't my POST, PUT or DELETE change the data?
By design. The guide says write requests are faked: you get the response a real API would send, but the resource is not created, updated or deleted on the server. For real persistence, run json-server locally with your own db.json.
What is typicode?
Typicode is the GitHub handle of the developer who created JSONPlaceholder, json-server and lowdb. That is why the API lives at jsonplaceholder.typicode.com.
Is there a JSONPlaceholder API documentation page?
Yes, the official docs are short: the resource list on the home page and the examples on the guide page. The pagination, sorting and embed parameters shown above come from json-server and are not listed there.