Payments, exchange rates, weather, a partner's REST API β sooner or later your app depends on an API you don't control. And then your tests do too, which is how you end up with:
The fix is standard: point your app at a mock of the third-party API during tests. This guide shows three ways to build that mock on Mockbird β recorded from real traffic, hand-built, or seeded β and then, the part most mocks can't do, how to drill the failure modes against it. Free, no signup, no code to maintain.
The best mock data is the data the real API actually returned. If your app (or the provider's dashboard) talks to the API from a browser, you already have a recorder open:
curl -X POST "https://HOST/api/projects/import?name=thirdparty-mock" \
--data-binary @recording.har
Every 2xx JSON response in the recording becomes hosted records, served back verbatim. URL paths become resources (/v1/invoices β invoices, numeric/UUID segments are treated as ids), static assets and analytics noise are ignored, {"data":[...]} wrappers are unwrapped, and repeated polling of the same endpoint is deduplicated. A recording that contained GET /v1/invoices and GET /v1/customers comes back as:
{
"baseUrl": "https://HOST/m/<id>",
"resources": [
{ "name": "invoices", "records": 2, "url": ".../m/<id>/invoices" },
{ "name": "customers", "records": 2, "url": ".../m/<id>/customers" }
]
}
β¦and GET /m/<id>/invoices returns exactly the JSON your app received from the real API β same fields, same values. Full CRUD, filtering, sorting, and pagination work on top of it, so a test can also create an invoice and read it back. Details and limits in docs β HAR import. Prefer clicking to curling? Open the importer in the dashboard and pick the .har file.
This is record & replay of a recording you made β not a live man-in-the-middle proxy. Mockbird never contacts the third-party API. That's a feature (no keys, no traffic to the real service from your tests) and a limitation (the snapshot doesn't update itself; re-record when the contract changes).
Often you only call two or three endpoints of a big API. Custom routes let you replicate exactly those, with path params and response templating. Say your app calls an exchange-rates API:
curl -X POST https://HOST/api/projects/<id>/routes \
-H "x-admin-key: <adminKey>" \
-H "content-type: application/json" \
-d '{
"method": "GET",
"path": "/rates/:base",
"status": 200,
"body": "{\"base\":\"{{params.base}}\",\"rates\":{\"EUR\":0.91,\"GBP\":0.78},\"fetched_at\":\"{{now}}\"}"
}'
$ curl https://HOST/m/<id>/rates/USD
{"base":"USD","rates":{"EUR":0.91,"GBP":0.78},"fetched_at":"2026-07-28T15:42:31.583Z"}
Templates can echo {{params.x}}, {{query.x}}, {{body.x}}, {{now}}, {{uuid}} and more; you control status, content type, headers, and per-route delay. A trailing /* catch-all can swallow every path you didn't bother to mock. Full reference: mock any endpoint.
No recording handy? Define the resources and let Mockbird seed realistic data β or paste a JSON/CSV sample of the provider's response shape into the importer. If the provider publishes an OpenAPI spec, even better: import the spec directly and every schema becomes a seeded endpoint.
Here's the part that separates "mock so tests pass" from "mock so you find bugs". Every Mockbird URL accepts simulation params β no configuration, just query strings β so you can point your retry and error-handling code at realistic failures:
Rate-limit handling. Does your client actually back off on 429?
$ curl -i "https://HOST/m/<id>/invoices?mock_status=429"
HTTP/2 429
{ "error": "simulated 429 error (mock_status)" }
Intermittent failures. Real third-party outages aren't all-or-nothing β they're a fraction of requests failing. mock_chaos fails that fraction with realistic statuses (here: 30% of requests, drawn from 429/503), so a test loop exercises the retry path and the success path:
$ for i in 1 2 3 4 5 6; do
curl -s -o /dev/null -w "%{http_code} " \
"https://HOST/m/<id>/invoices?mock_chaos=0.3&mock_chaos_status=429,503"
done
429 200 200 503 200 200
Slowness and timeouts. Is your HTTP client's timeout actually configured? Does the UI show a spinner or freeze?
curl "https://HOST/m/<id>/invoices?mock_delay=2000" # responds after 2s
All of these compose, and none of them require touching the mock's data. The full list (mock_jitter, snapshot pinning for parallel test workers, and more) is in the docs and the error-states guide.
The pattern is the same in every language: the third-party base URL comes from config, and tests override it.
// config
const RATES_API = process.env.RATES_API_URL ?? "https://api.real-provider.com";
// test / CI environment
RATES_API_URL=https://HOST/m/<id>
Can't change the app's config? In browser tests, rewrite the host at the edge of the test instead β e.g. Playwright:
await page.route("https://api.real-provider.com/**", (route) => {
const url = new URL(route.request().url());
route.continue({ url: "https://HOST/m/<id>" + url.pathname + url.search });
});
stripe-mock are excellent), use it for deep integration tests β and use Mockbird for the fast, keyless, failure-drillable everyday layer.| In-process libs (nock, responses, respx, VCR) | WireMock proxy/record | Beeceptor | Official sandboxes | Mockbird | |
|---|---|---|---|---|---|
| What it is | language-specific interception in your test process | self-hosted Java server (hosted version is paid) | hosted; free tier 50 req/day per endpoint | provider-run test env | free hosted mock API |
| Reachable from | that process only | anything, once you run it | anything | anything with keys | anything with HTTP |
| Record real traffic | VCR-style cassettes (per-language) | yes β live proxy | yes (proxy mode) | n/a | HAR replay (browser recording) |
| Failure injection | hand-coded per test | yes (fault DSL) | rules (3 on free) | rarely | ?mock_status/?mock_chaos/?mock_delay on any URL |
| Setup | per-language, per-suite | install/host it | low | signup + keys | one curl, no signup |
| Provider-exact behavior | as good as your fixtures | as good as your recording | as good as your rules | exact | as good as your recording/routes |
Written by the Mockbird maker β bias disclosed. In-process libraries (nock, responses, VCR.py) are the right call for pure unit tests: offline, zero latency, no service involved. Official sandboxes are unbeatable for provider-exact integration tests. Mockbird's lane is everything between: a real URL that browsers, mobile apps, backends, CI, and teammates can all hit, with recorded-from-reality data and failure drills, at zero cost and zero setup. Free-tier figures checked July 2026.
The shared demo project is live β pretend it's your third-party API:
curl -i "https://HOST/m/demo/products?mock_status=429"
Then create your own project in one click or import a HAR as shown above. Full API reference in the docs. More guides: deterministic test data Β· testing loading & error states Β· send test webhooks Β· free mock API tools compared. Create your API β