A mock API that fails on purpose
Static mock APIs like JSONPlaceholder only ever return the happy path, so your retry, backoff, and timeout code goes untested until it breaks in production. ReqRes serves 15 deliberate failures on demand, plus scripted multi-step outages. Deterministic, so a flaky failure is finally reproducible.
Free tier is open, no signup. Three failure scenarios are free; all 15 plus scripted chains are on a paid plan. Full tour at reqres.in/agents.
You can't test error handling against data that never errors.
Most mock APIs are fixtures: they return the same 200 OK forever. That is fine for building a happy-path UI, but it means the code that actually matters when things go wrong, your retries, your exponential backoff, your timeout budgets, your error boundaries, your 429 handling, never runs until a real outage triggers it in front of a real user.
ReqRes gives you failure as a first-class input. Ask for a 429, a truncated JSON body, a gateway timeout, or a five-step degrading outage, and you get exactly that, on demand, the same way every time. Point your integration tests at it and the unhappy paths finally get exercised.
Make it fail right now
No signup, no key. Paste this into your terminal to get a real 429 with a Retry-After header:
curl -i https://reqres.in/agent/v1/scenarios/rate-limited
Or the cruellest bug to catch, a 200 OK whose JSON body is truncated mid-object:
curl -i https://reqres.in/agent/v1/scenarios/malformed-json
Every deliberate failure carries X-Agent-Sandbox-Intentional: <name>. Tell your error reporter (Sentry, Bugsnag) to ignore responses with that header so your test failures never pollute your alert queue.
Fifteen failures you can call on demand
Every scenario is a real HTTP response, not a description of one. Free tier unlocks rate-limited, validation-error, and malformed-json; the rest are on a paid plan.
| Scenario | Status | What it exercises |
|---|---|---|
rate-limited | 429 | Throttling with a Retry-After header. Your backoff logic. |
server-error | 500 | Generic 5xx. Your retry path. |
bad-gateway | 502 | Upstream returned garbage. Fallback and backoff. |
timeout | 504 | Gateway timeout. Your timeout budget. |
slow | 200 | Returns a simulated-delay header. Client-side timeouts without the wait. |
validation-error | 422 | Multi-field failure with a field_errors object. |
unauthorized | 401 | Missing or invalid token, with WWW-Authenticate. |
forbidden | 403 | Authenticated but missing scope. |
not-found | 404 | Not found with a "Did you mean?" hint. |
conflict | 409 | Optimistic-concurrency clash with current and your versions. |
too-large | 413 | Payload over the max size, with max_bytes. |
partial-content | 206 | Partial response with Content-Range. Range handling. |
redirect-loop | 302 | A 3-hop redirect chain. Your follow-redirect cap. |
malformed-json | 200 | 200 OK with a truncated JSON body. Your parser's error path. |
empty-response | 200 | 200 OK, Content-Length 0, no body. |
Programmatic list: GET /agent/v1/scenarios returns the same catalog as JSON.
Real outages are sequences, not single errors
A single 500 is easy. What breaks systems is the pattern: fine, fine, throttled, throttled, timeout, recover. Send an X-Agent-Chain header and each call walks one step through a scripted sequence. Deterministic per call count, so the step that fails your test fails it every time.
Intermittent failures that resolve. Proves your retries actually recover instead of giving up.
Sustained 429s. Proves your backoff widens and honours Retry-After.
Latency climbs until it tips into errors. Proves your timeouts and circuit breaker trip in time.
A token that expires partway through. Proves your refresh-and-replay works under load.
Five presets, or define your own with POST /agent/v1/chains. Reset a chain's position with DELETE /agent/v1/chains/<name>/state.
Why not just a static mock?
| Need | Static mock (JSONPlaceholder, fixtures) | ReqRes |
|---|---|---|
| Trigger a 429 / 500 / timeout | Hand-roll and maintain it yourself | One URL, on demand |
| Malformed / truncated body | Not possible | /scenarios/malformed-json |
| Multi-step outage | Not possible | Scripted chains |
| Reproducible in CI | Depends on your setup | Deterministic, pinnable world version |
| Servers to run | Yours to host | None, or self-host the Docker image |
Start testing the unhappy paths
The first three scenarios are free and need no signup. Unlock all 15 plus scripted chains and trajectory logs on a paid plan.