← All guides

How to mock a third-party API for testing

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.

Option 1: record the real API once, replay it forever (HAR import)

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:

  1. Open DevTools β†’ Network tab, click through the flows you care about.
  2. Right-click the request list β†’ Save all as HAR (Chrome: "Export HAR…").
  3. Post the file:
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).

Option 2: hand-build just the endpoints you use (custom routes)

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.

Option 3: seed it (when you don't have a recording)

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.

The payoff: failure drills the real API won't give you

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.

Pointing your app at the mock

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 });
});

What this deliberately doesn't do

Honest comparison

In-process libs (nock, responses, respx, VCR)WireMock proxy/recordBeeceptorOfficial sandboxesMockbird
What it islanguage-specific interception in your test processself-hosted Java server (hosted version is paid)hosted; free tier 50 req/day per endpointprovider-run test envfree hosted mock API
Reachable fromthat process onlyanything, once you run itanythinganything with keysanything with HTTP
Record real trafficVCR-style cassettes (per-language)yes β€” live proxyyes (proxy mode)n/aHAR replay (browser recording)
Failure injectionhand-coded per testyes (fault DSL)rules (3 on free)rarely?mock_status/?mock_chaos/?mock_delay on any URL
Setupper-language, per-suiteinstall/host itlowsignup + keysone curl, no signup
Provider-exact behavioras good as your fixturesas good as your recordingas good as your rulesexactas 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.

Try the failure drill right now (zero setup)

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 β†’