Honest opening: VCR and vcrpy are healthy, maintained, excellent libraries โ this is not a "the project is dead" page. Checked against the registries the week of writing (Sep 2026): Ruby's vcr gem is at 6.4.0 (released Dec 2025, back under a dual MIT license since 6.3.0) with over 160 million downloads; Python's vcrpy is at 8.3.0 (released Jul 2026). Record a real HTTP interaction into a YAML cassette once, replay it in every test run after: deterministic, offline, fast. The pattern earned its decade of popularity.
The reasons people search "vcr alternative" aren't neglect โ they're structural, baked into the cassette-file-in-one-process model. We reproduced every one of them before writing this page (Ruby VCR 6.4.0 via webmock 3.26.4, vcrpy 8.3.0 with requests, Sep 2026 โ every snippet below was actually run).
Authorization header and an X-Api-Key, then read the cassette back. Both tokens were sitting in the YAML, in plaintext, ready to be committed (details below). The fixes โ Ruby's filter_sensitive_data, vcrpy's filter_headers โ are opt-in, per-header, and easy to forget once. A forgotten header in a pushed cassette means rotating keys.curl of the exact same URL sailed straight past the cassette to the live network (run shown below). Same for your browser, your Playwright/Cypress suite, a second service, a mobile app, a teammate's machine: the cassette is a monkey-patch on one process's HTTP stack, and nothing else can see it.once / :once) never re-records. When the real API adds a field, renames one, or changes an error shape, your tests keep passing against the old response โ no warning, no staleness marker. Re-recording means deleting files and running with live credentials from a machine that has them.?limit=3 vs the recorded ?limit=2) raised vcrpy's CannotOverwriteExistingCassetteException; Ruby raised VCR::Errors::UnhandledHTTPRequestError for a new path. Both errors are famous enough to have their own search-suggestion entries. The knobs (match_on, record: :new_episodes) exist โ they're also how cassettes quietly accumulate junk.vcrpy 8.3.0, default configuration, one recorded request against our live demo API:
import vcr, requests, yaml
with vcr.use_cassette('cassette1.yaml'):
requests.get('https://mockbird.mockbird.workers.dev/m/demo/products?limit=2',
headers={'Authorization': 'Bearer super-secret-token',
'X-Api-Key': 'sk-live-123'})
print(yaml.safe_load(open('cassette1.yaml'))['interactions'][0]['request']['headers'])
# โ {'Authorization': ['Bearer super-secret-token'], 'X-Api-Key': ['sk-live-123'], ...}
Ruby VCR 6.4.0 behaved identically โ File.read('rb_cassettes/demo.yml').include?('ruby-secret-token') printed true. Neither library redacts anything unless you configure it to.
And the escape-hatch proof: with the cassette active and its body hand-edited to TAMPERED-BY-EDITOR (so a replay is unmistakable), the in-process client got the tampered recording while a subprocess got the live API:
with vcr.use_cassette('cassette1.yaml', record_mode='none'):
requests.get(URL).text # โ [{"id":1,"name":"TAMPERED-BY-EDITOR"}]
subprocess.run(['curl','-s',URL]) # โ live data. The cassette never existed for curl.
Mockbird has the record-and-replay loop built in, but the "cassette" is a hosted route at a real URL โ so curl, browsers, Playwright, mobile apps, other services, teammates and CI all replay the same recording. Point a project's proxy at the real API and switch recording on:
# 1. create a project (no signup)
curl -X POST https://mockbird.mockbird.workers.dev/api/projects \
-H "content-type: application/json" -d '{"name":"recorded","blank":true}'
# โ {"id":"<id>","adminKey":"<key>","baseUrl":".../m/<id>", ...}
# 2. proxy unmatched paths to the real API, and record what comes back
curl -X PUT https://mockbird.mockbird.workers.dev/api/projects/<id>/settings \
-H "x-admin-key: <key>" -H "content-type: application/json" \
-d '{"proxyBase":"https://dummyjson.com","proxyRecord":true}'
First request for a path forwards upstream and records the 2xx response; every request after that replays locally. The response headers tell you which happened โ from our verification run against dummyjson.com:
$ curl -sD - https://mockbird.mockbird.workers.dev/m/<id>/products/1
x-mockbird-proxied: dummyjson.com
x-mockbird-recorded: 1 โ first hit: fetched upstream + recorded
{"id":1,"title":"Essence Mascara Lash Princess", ...}
$ curl -sD - https://mockbird.mockbird.workers.dev/m/<id>/products/1
(no x-mockbird-proxied header) โ second hit: replayed locally, byte-identical body
How this answers each gotcha above:
Authorization header is forwarded to the upstream during recording but never written into a repo artifact, and the request inspector stores auth headers redacted to their scheme."recorded": true (GET /api/projects/<id>/routes, marked โบ in the dashboard). Re-recording one endpoint = delete that route and hit the URL again; no test-suite-wide re-record, no local credentials ceremony. You can also open a recorded body in the dashboard and edit it โ hand-updating a stub is a click, not YAML surgery.CannotOverwriteExistingCassetteException class exists here. Honest cost: that's less precise than VCR's matchers โ see the wins list below.?mock_status=503 returned a 503, and ?mock_delay=2000/?mock_chaos=0.3/?mock_seq work the same. Replay yesterday's response or make yesterday's endpoint fail on demand โ the drill cassettes can't run. Recipes: testing loading & error states.Honest scope notes: recorded bodies are text-ish (JSON/XML/text) up to 16 KB, non-2xx responses proxy through without being saved, first recording wins, and the 20-routes/project limit applies. Full semantics: docs โ record & replay.
If "record" for you means a browser session, skip the proxy: use your app for a minute with DevTools open, Network โ Export HAR, and POST the file โ the JSON API responses the page actually received become hosted, stateful collections (bodies served verbatim, full CRUD/filtering/pagination on top):
curl -X POST https://mockbird.mockbird.workers.dev/api/projects/import --data-binary @myapp.har
Walkthrough: HAR file โ live mock API.
The same endpoint accepts VCR and vcrpy cassettes directly โ the YAML files sitting in your cassettes/ or fixtures/vcr_cassettes/ directory right now (http_interactions: / interactions:; the JSON serializers work too):
curl -X POST https://mockbird.mockbird.workers.dev/api/projects/import \
--data-binary @spec/cassettes/checkout.yml
# โ {"id":"abc123","kind":"cassette","baseUrl":"โฆ/m/abc123","resources":[{"name":"products","records":24,โฆ}]}
The 2xx JSON responses recorded in the cassette become hosted records, served verbatim โ the data your suite already trusts, now behind a URL any process can hit. Two details that matter here specifically:
Authorization/X-Api-Key values both libraries recorded verbatim (see above) never enter the project, and the import warns you when credential headers were present in the file, so you know to rotate them if the cassette was ever pushed or shared./api/v2/users/42 โ users), polling repeats dedupe by id, {"data":[โฆ]} wrappers unwrap, base64-encoded bodies decode. Interactions that only captured a request body get an inferred schema with seeded data. Cassettes up to 8 MB. Docs โThe other thing teams use cassette libraries for is fixtures: a known data state per test scenario. That's snapshots โ save the project's entire dataset under a name (baseline, empty, edge-cases), restore it in beforeEach, or pin a GET to a named snapshot per request with the X-Mockbird-Snapshot header so parallel workers each see their own state. Full recipe: deterministic test data.
GET /m/<project>/db.json ejects the live dataset, and openapi.json / postman.json / types.ts / msw.js exports come free.| VCR (Ruby) / vcrpy (Python) | Mockbird | |
|---|---|---|
| What it is | free OSS record/replay libraries | free hosted mock API service |
| Maintenance | active โ VCR 6.4.0 (Dec 2025), vcrpy 8.3.0 (Jul 2026), verified | hosted, actively maintained |
| Recording lives | YAML/JSON cassette files in your repo | hosted routes (editable, deletable, listed via API) |
| Secrets by default | recorded verbatim into cassettes (verified both); filtering is opt-in | no repo artifact; inspector redacts auth headers to scheme |
| Reachable from | the one process the library patches | anything with HTTP: curl, browser, mobile, other services, CI, agents |
| Request matching | precise + configurable (method/URI/host/path/body/headers) | method + path only; query ignored (looser โ by design) |
| Staleness | silent โ once mode replays old responses forever | visible recorded: true flags; re-record per route by delete + re-hit |
| Error/latency drills on recordings | no โ replay is verbatim | ?mock_status / ?mock_delay / ?mock_chaos / ?mock_seq on any recorded route |
| Stateful CRUD on recorded data | no | HAR-imported records become real collections with writes |
| Works offline | yes (replay mode) | no โ it's a real network call |
| Latency | zero (in-process) | real network latency |
| Cost / caps | free forever, no caps | free (beta), 10,000 requests/project/day |
Written by the Mockbird maker โ bias disclosed. Where VCR and vcrpy genuinely win: offline, zero-latency replay with zero network flakiness; automatic capture of your real API with full headers and every interaction of a test, matched per-test with real precision; cassette diffs in code review; no request caps; and they mock any host with no per-service setup. If your tests are single-process and you've wired up secret filtering, they remain the right tool. When the recording needs to be a URL โ for a browser, a subprocess, another language, a teammate, or CI against a deployed build โ that's us. (License footnote for completeness: VCR 6.0โ6.2 were Hippocratic-licensed only, which some legal teams flagged; since 6.3.0 it's dual-licensed MIT, so that objection is dated.)
proxyBase + proxyRecord in its dashboard settings and you're recording.Full API reference in the docs. More guides: Polly.JS alternative ยท HAR file โ mock API ยท nock alternative ยท MSW alternative ยท mock API for Ruby ยท mock API for Python ยท deterministic test data ยท testing loading & error states ยท free mock API tools compared. Create your API โ