The best mock data isn't hand-written fixtures โ it's what the real API actually returned. And if your app talks to an API from a browser, you already own a recorder: every DevTools Network tab exports a HAR file containing every request and response body from your session. (Not just DevTools: Polly.JS recordings are standard HAR too โ the same import works on them verbatim.)
Mockbird turns that file into a hosted mock API in one curl. The JSON responses your app received are served back verbatim as records at a real URL โ no proxy to configure, no stub files to write, and the result is a living REST API you can filter, page, write to, and break on purpose.
If your browser offers a “sanitized” and a full export, either works โ the importer only reads URL paths and JSON bodies. Headers and cookies in the file are never read or stored. Still: a HAR is a full recording of your session, so treat the file itself as sensitive.
curl -X POST "https://mockbird.mockbird.workers.dev/api/projects/import?name=recorded-api" \
--data-binary @myapp.har
That's the entire setup. The response tells you what it found:
{
"id": "y9b9d2pf98",
"kind": "har",
"baseUrl": "https://mockbird.mockbird.workers.dev/m/y9b9d2pf98",
"resources": [
{ "name": "products", "records": 20, "url": ".../m/y9b9d2pf98/products" },
{ "name": "orders", "records": 21, "url": ".../m/y9b9d2pf98/orders" },
{ "name": "customers", "records": 20, "url": ".../m/y9b9d2pf98/customers" }
]
}
Prefer clicking to curling? Open the importer in the dashboard and pick the .har file โ same result, plus a data browser.
This output is from a real session we recorded against our own demo store (browse page, detail page, an order placed) and re-imported. The mock returns byte-for-byte what the real API returned:
# the exact record the real API served during recording
curl https://mockbird.mockbird.workers.dev/m/<id>/products/1
# โ {"id":1,"name":"Game Year","price":881.55,"category":"electronics",...}
# even the order the session CREATED is in the recording โ the 201 response body became a record
curl https://mockbird.mockbird.workers.dev/m/<id>/orders/26
# โ {"id":26,"status":"pending","total":49.99,"customerId":3}
But unlike a static fixture file, it's a full REST API on top of that data:
curl "https://mockbird.mockbird.workers.dev/m/<id>/products?category=books" # exact-match filters
curl "https://mockbird.mockbird.workers.dev/m/<id>/products?_page=2&_limit=5" # pagination + X-Total-Count
curl "https://mockbird.mockbird.workers.dev/m/<id>/products?_sort=price&_order=desc"
curl -X POST https://mockbird.mockbird.workers.dev/m/<id>/products \
-H 'content-type: application/json' -d '{"name":"new thing","price":1.23}' # writes persist
And every failure drill works against yesterday's real data: ?mock_status=503, ?mock_delay=3000, ?mock_seq=503,503,200 for deterministic retry tests, ?mock_chaos=0.3 for flaky-network chaos. Recording real traffic and then breaking it on purpose is the failure-testing setup the real API will never give you.
| In the HAR | What happens |
|---|---|
| 2xx responses with JSON bodies | become records, verbatim โ grouped into a resource by URL path (/api/v2/users?page=1 and /api/v2/users/42 both land in users; numeric/UUID segments are treated as ids) |
| HTML, JS, CSS, images, fonts, video | ignored automatically โ export the full page, no need to filter by XHR first |
| The same endpoint polled 10 times | deduplicated (by id when present, by content otherwise) |
{"data":[โฆ]} / {"items":[โฆ]} wrappers | unwrapped โ the array inside becomes the records |
| Endpoints that only appear as writes (a POST body, no useful response) | schema inferred from the request body, seeded with fake data |
| Base64-encoded response bodies (some DevTools exports) | decoded |
| Headers, cookies, auth tokens | never read, never stored |
Limits: HAR files up to 8 MB, first 3,000 entries scanned, up to 20 resources and 1,000 records per resource. Details in the docs.
Our own verification recording had 11 entries โ an HTML page, an analytics-style beacon, three polls of the same list, a wrapped {"data":[โฆ]} response, a detail fetch, and one POST. It came back as exactly 3 clean resources: the page and beacon skipped, the polls and the wrapper's duplicates collapsed into the same 20 records, the POST's 201 response preserved as record 26.
| Tool | How it replays | Best when |
|---|---|---|
Playwright routeFromHAR | inside the browser Playwright controls | your tests are already Playwright and only the browser needs mocking โ zero extra infrastructure |
| WireMock record & playback | proxy records upstream, replays stubs with exact request matching | you need per-request precision: headers, query permutations, non-JSON bodies, recorded status sequences (self-hosted Java) |
| mitmproxy | local intercepting proxy + Python scripting | you want to inspect/rewrite live traffic, not host a mock |
| Mockbird HAR import | hosted REST API built from the recorded JSON | you want a shareable URL โ curl, mobile apps, backends, teammates, CI and deploy previews all hit the same mock; plus CRUD, filters and failure drills on top |
Honest limitations, stated plainly: only JSON bodies import (no XML/HTML/binary), only 2xx responses become data, and replay is resource-shaped rather than exact-request-matched โ two distinct endpoints whose paths end in the same segment merge into one collection, and a recorded 404 or per-query-param response variation won't be reproduced (use ?mock_status=404 to simulate errors instead). If you need byte-exact stub matching per request, WireMock or routeFromHAR are the right tools. Written by the Mockbird maker โ bias disclosed. Every command and number on this page was verified by running it against a real Playwright-recorded HAR, August 2026.
beforeEach or pin it per-request with ?mock_snapshot= โ see deterministic test data.GET /m/<id>/db.json exports everything in json-server format โ no lock-in.GET /api/projects/<id>/requests.har): what your app or agent sent to the mock, as a file DevTools can open. HAR in, HAR out.