Your screen is too small. Please widen your browser window or use a device with a screen width of at least 300px to use this app.
Seven free APIs for testing, compared by the data they serve, whether writes stick, and the limits they document. Then the harder question: what to do when a shared public API cannot give you the shape, the status code or the delay your screen actually needs.
Every free public API in this article exists to answer one question: does my HTTP client work. You are learning a framework, evaluating a data fetching library, or checking that a proxy config is right, and you need a URL that returns valid JSON without an API key or a signup form. For that job these services are excellent, and the right one is usually whichever you can remember the hostname of.
They start to strain the moment the question changes from does my client work to does my screen work. A screen has states. It has a loading state, an empty state, a single result layout, a hundred result layout, a permission error, a network timeout, and a row whose title is long enough to push the price off the card. A shared dataset that always returns the same twenty posts in the same order can prove exactly one of those.
So the useful way to read the table below is not which API is best, it is which of these matches the shape I need, and how far can it take me before I have to do something else.
All seven are free, need no API key for the routes described, and answer a browser directly. Details change, so treat this as a starting point and check the service before you depend on it.
| API | What it serves | Write requests | Documented limit | Best for |
|---|---|---|---|---|
| JSONPlaceholder | Posts, comments, albums, photos, todos, users | Accepted, nothing persists | None documented | The first fetch in a tutorial |
| DummyJSON | Products, carts, users, recipes, quotes, posts | Simulated, nothing persists | None documented | A realistic ecommerce shape |
| ReqRes | Users, plus login and register routes | Simulated, nothing persists |
One entry in that table is doing a different job from the rest. httpbin serves no dataset at all. It echoes, and in doing so it lets you ask for the responses the other services will not give you: a specific status code, a deliberate delay, a chosen set of response headers.
# a 503, on demand
curl -i https://httpbin.org/status/503
# a response that takes 3 seconds
curl -i https://httpbin.org/delay/3
# whatever headers you want echoed back
curl -i https://httpbin.org/response-headers?X-Total-Count=42This is genuinely the fastest way to check that your error handler fires and your spinner appears. What it cannot do is combine the two halves. You can have a 503, or you can have your data shape, but you cannot have a 503 on the endpoint that serves your invoice list, and you cannot have that same list come back empty on the next route your screen calls. Wiring your app to two unrelated hosts to test one feature is where this approach stops paying.
A second entry does something the others do not. DummyJSON has a custom response tool. You paste a JSON body, pick a method, and it hands back a hosted URL that serves it, with no account and no signup. If a fixed shape behind a URL is the whole of what you need, that is the shortest path there is and you should take it.
Knowing where it stops matters before you build on it. The URL expires after 90 days. The body is fixed to the URL it was created with, so changing a response means generating a new one and updating your app. The payload caps at 300 KB. And the tool takes a body and a method and nothing else, so the status code and the delay are not yours to choose.
That last limit is the same wall httpbin hits from the opposite side. httpbin gives you the status code and the delay but not your data. A pasted body gives you your data but not the status code or the delay. A screen needs both at once, on the same endpoint.
None of these are flaws. They are the cost of a dataset that belongs to everybody.
Your screen renders an invoice with a tax breakdown. The public API serves posts and todos. You end up writing a mapping layer that exists only in the mock, and testing that layer instead of your UI.
A shared service is built to demonstrate success. Getting a 500 out of it on demand, repeatably, for one specific route, is not something it offers, so the error branch stays untested.
The moment you find yourself writing a mapping layer to turn todos from a public dataset into your invoices, the shared API has stopped saving you time. The alternative is not standing up a backend. It is writing down the response you already know you need and putting it behind a URL.
That is what this site does. You paste the JSON body your screen expects, choose a status anywhere from 100 to 599, set a delay up to 60,000 ms, and get a public HTTPS URL back. The same feature can have one endpoint returning the full list, one returning an empty list, and one returning a 500 after two seconds, which is the combination httpbin cannot give you and a fixed dataset has no room for.
It has its own limit, and it is the same one the services above have: nothing persists between calls, so a POST does not change what a later GET returns. If your test needs a write to stick, you need a real backend or one of the stateful services compared on mock API tools, and no public dataset on this page is going to substitute for one. For everything short of that, mock data covers where a mock belongs and how to make the values realistic enough to be worth testing against.
Write the response you actually want and call it over HTTPS a minute later.
| 250 a day, per IP |
| Auth flows and paged lists |
| Fake Store API | Products, categories, carts, users | Simulated, nothing persists | None documented | A storefront grid |
| PokeAPI | Deeply nested reference data | Read only | Fair use, caching requested | Large nested payloads |
| httpbin | Whatever you ask it to echo | Echoes your request back | None documented | Status codes, headers, delays |
| RandomUser | Generated user profiles with photos | Read only | Fair use | Avatars and varied names |
Skeletons, spinners and timeout branches only run when a response is slow. A snappy public API hides every one of them until a user on a train finds them for you.
Every service in the table above accepts a POST and returns something that looks created. None of them keep it. Optimistic updates and refetch after mutate cannot be exercised against that.
A free endpoint used by every tutorial reader on the internet is a dependency you do not control. When it is slow or down, your test suite is red for reasons that have nothing to do with your code.
An empty list, a single result, a name long enough to wrap, a null where you expected a string. These are the states that break layouts, and a fixed public dataset has no room for them.