Test Data — entry 009 of 19

JSONPlaceholder

Verified Jul 2026

JSONPlaceholder is one of the longest-running free fake REST APIs, serving a fixed set of 100 posts, 500 comments, 10 users, 100 albums, 5,000 photos, and 200 todos as JSON. Every standard verb works against each resource, including nested routes like /posts/1/comments, though POST/PUT/PATCH/DELETE only simulate a write and return a fake response without persisting any change. Its data and IDs never change, which is exactly why it's the default backend in countless framework tutorials and testing guides.

JSONPlaceholder is a free, keyless fake REST API serving a fixed, never-changing dataset -- 100 posts, 500 comments, 10 users, 100 albums, 5,000 photos, 200 todos -- with every standard verb wired up against each resource, including nested routes like /posts/1/comments. A live GET this run confirmed both http:// and https:// resolve the same host directly with no redirect either way, and every response (200 or 404) carries an undocumented x-ratelimit-limit: 1000 / x-ratelimit-remaining / x-ratelimit-reset header trio that appears nowhere in the project's own docs.

mock-datatestingprototypingrest-api
AuthenticationNone requiredCall it straight away — no key, no signup.
HTTPSSupportedTraffic is encrypted in transit.
CORSEnabledCallable directly from browser JavaScript.
PricingFreeNo paid tier — free for the documented use case.
FormatsJSONResponses can be requested as JSON.

GreatAPIs Score

Score74out of 100
Authentication25/25No authentication required
Pricing20/20Free to use
Docs0/20No docs or spec available
Formats9/15Single response format
Freshness20/20Verified within 6 months

Embed this badge

Scored 74 on greatapis.com
<a href="https://greatapis.com/api/jsonplaceholder-test-data/"><img src="https://greatapis.com/badge/jsonplaceholder-test-data.svg" alt="Scored 74 on greatapis.com"></a>

Auth quickstart

  1. No API key, header, or signup required for any route. This run's undocumented x-ratelimit-limit header consistently reads 1000, but the paired x-ratelimit-reset value is not stable across probes -- the window it implies varies from one request to the next and between edge servers, so it can't be read as a dependable 'N requests per minute' figure. Treat the counter as headroom to watch, not a number to plan around.
Stored keyNo key stored

Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.

Get a single post

GEThttps://jsonplaceholder.typicode.com/posts/1

200 application/json; charset=utf-8

{"userId":1,"id":1,"title":"sunt aut facere repellat provident occaecati excepturi optio reprehenderit","body":"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"}

Try it

Developer reference

Base URLhttps://jsonplaceholder.typicode.com
Key endpoints
  • GET/posts/{id}
  • GET/posts/{id}/comments
  • GET/users

Gotchas & limits

  • A not-found ID is HTTP 404 with a literal `{}` body (`content-length: 2`), not an empty array or a 200 with `null`: a live `GET /posts/999` this run returned exactly that -- branch on status code, never on an empty-looking body, since there's no array shape here to check.
  • A non-numeric ID also 404s the same way rather than erroring: a live `GET /posts/abc` this run returned the identical `{}` at 404 -- the API never validates the ID's type, it just fails the lookup.
  • CORS echoes the request's `Origin` back verbatim instead of sending a wildcard: a live GET with `Origin: https://greatapis.com` this run returned `access-control-allow-origin: https://greatapis.com` on that exact value, while the same GET sent with no `Origin` header comes back with no `access-control-allow-origin` at all -- though `access-control-allow-credentials: true` and `vary: Origin` are still present either way, so it's that one header that's conditional, not CORS support in general. Verified on cache-busted requests (`cf-cache-status: MISS`) so the edge cache isn't masking the difference.
  • `POST /posts` returns HTTP 201 with a fake `id: 101` that is never actually persisted: a live POST this run got back `{"title":"foo","body":"bar","userId":1,"id":101}`, but an immediate follow-up `GET /posts/101` still 404s with the same literal `{}` -- treat every write as a simulation, not real state.
  • An `OPTIONS` preflight against `/posts/1` (sent with `Origin` + `Access-Control-Request-Method: GET`) returns 204 with `access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE` and the same echoed-origin header, confirming a real browser cross-origin call would succeed end to end.