Test Data — entry 009 of 19
JSONPlaceholder
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.
GreatAPIs Score
Auth quickstart
- 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.
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
{"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
https://jsonplaceholder.typicode.com- 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 withnull: a liveGET /posts/999this 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/abcthis run returned the identical{}at 404 — the API never validates the ID's type, it just fails the lookup. - CORS echoes the request's
Originback verbatim instead of sending a wildcard: a live GET withOrigin: https://greatapis.comthis run returnedaccess-control-allow-origin: https://greatapis.comon that exact value, while the same GET sent with noOriginheader comes back with noaccess-control-allow-originat all — thoughaccess-control-allow-credentials: trueandvary: Originare 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 /postsreturns HTTP 201 with a fakeid: 101that is never actually persisted: a live POST this run got back{"title":"foo","body":"bar","userId":1,"id":101}, but an immediate follow-upGET /posts/101still 404s with the same literal{}— treat every write as a simulation, not real state.- An
OPTIONSpreflight against/posts/1(sent withOrigin+Access-Control-Request-Method: GET) returns 204 withaccess-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETEand the same echoed-origin header, confirming a real browser cross-origin call would succeed end to end.