Mocks drift.
Copies of production leak.
Record real traffic instead.

Stubsmith records how an API really behaves, failures included, and turns that into deterministic integration tests. Body values are masked inside your own process, and only the ones you choose to keep reach us.

1. Record real traffic

Shell

$ pip install "stubsmith[requests]"

Your app entrypoint

import stubsmith
# key read from STUBSMITH_API_KEY
stubsmith.install()

That is the whole integration. Outbound requests and httpx calls are captured from here, masked on the way out. Every value stays masked until you approve a keep rule for that exact field. Try one in the playground below.

2. Replay it in your tests

Your test file

import stubsmith

def test_charge():
    # one pass per recorded response: the 200, the 402, the real 500
    for attempt in stubsmith.replay_all():
        with attempt:
            result = charge_customer(amount_cents=950_000)
        assert result.handled

Every response the API really returned, failures included, served from a bundle stubsmith pull wrote to disk. No API key and no network at test time.

Start free →

Free plan: 1 seat, 1 project, 50 fingerprints, 3 samples per response variant. No card to sign up. Paid plans from €9 a month.

Open source SDK on GitHub · Capture runs through the Python SDK today; Node, Go and Java come later.

Your mocks and the API disagree

A mock is written once, from the docs. Then the API changes: a field becomes nullable, an error grows a new code, a 200 starts carrying a partial result. The mock does not, so the suite stays green and the mismatch surfaces in production.

A recording cannot drift, because it is what the API did. If you already use VCR.py or pytest-recording, this is the same replay model without the cassettes. Their filters are opt-in and fail open, so one field you forgot lands in version control. Here the recording is masked before it is written, and the only body values that survive are the ones you named. See how the tools compare.

  • Schema drift fails a test, not a deploy. The stub is generated from the real API, so a change shows up at the next record cycle.
  • The error shapes are real too. A hand-written failure mock is a guess. A recorded one is the response.
Read the security architecture →

Paste a request. Watch it go dark.

This is the SDK's masking, running in this page on your machine. Nothing is sent anywhere. Edit the payload, approve a keep rule, watch the fingerprint change.

POST/v1/chargescontent-type: application/jsonfail-closed
Your request (editable)…
…
Keep rules: click a field to let its value through
Variant keys: fold a value into the fingerprint
Default masking: value type → substituted value
number→0integer→0boolean→falsestring→<masked>

This is the SDK's default output. Format-preserving placeholders are opt-in; the docs list the types and setup. There is no card type: a card-shaped value is masked, never reshaped into another card number.

… values masked… keptfingerprint …Run this on real traffic →

Masked values keep their type

Opt in to shape-preserving placeholders and a masked value keeps its format: a UUID stays a parseable UUID, a timestamp stays ISO 8601, and an email becomes a synthetic address on your placeholder domain, so a stub parses and routes the way the real payload did.

How it works

Four steps from real traffic to test fixtures.

  1. Capture real traffic

    Add the Python SDK with a single line. It records outbound HTTP request/response pairs into a local capture buffer. Python is the only language with capture support today.

  2. SDK masks at the edge, before data leaves

    Before a body value leaves your infrastructure, the SDK applies your masking rules, as the playground above shows. The documented exceptions are listed under privacy below.

  3. Stubsmith stores masked samples and fingerprints

    Stubsmith stores the masked body and a structural fingerprint of the request shape, enough to detect duplicates and organize stubs.

  4. Generate stubs, fixtures, and replay in CI

    Pull a replay bundle, or fetch fixtures, and replay in CI. Replaying a bundle already on disk needs no live API calls and no API key, and is deterministic. Pulling a bundle, and calling stubsmith.fixtures(), which fetches at test collection, do use a key. Replay intercepts requests; httpx is captured but not yet replayed.

What you get

Stubs that match the API, kept honest by the traffic they were recorded from.

  • Stubs from real traffic, failures included

    Samples are kept per response variant, so the 429 and the 500 sit alongside the 200. Loop your test over all of them and the failures you cannot reproduce on demand become ordinary test cases.

  • Fingerprint deduplication

    Stubsmith deduplicates traffic by structural fingerprint, so you get one canonical stub per API shape, not thousands of near-identical captures.

  • Open-source SDK

    The Python SDK, the part that masks your data, is open source on GitHub, so you can read what runs inside your process before you trust it.

  • EU-sovereign hosting

    Hosted on UpCloud and Scaleway in the EU. Captures, samples and the database are stored in Amsterdam, NL.

Privacy stance

We don't want your customers' data

Most API tools process production traffic on their servers. Stubsmith is built on the opposite premise: personal data in a request or response body should not reach us in the first place.

  • Masked before it leaves, fail-closed

    The SDK masks every body value unless you keep it, inside your own infrastructure, before any network call is made.

  • Structure, plus only what you keep

    Stubsmith's servers receive masked bodies and field names. We see a field called receipt_email of type string, not the address in the body, unless you approve a keep rule for that exact field. Two things sit outside that rule and are sent as written: the transport headers a stub needs for replay, and an unrecognised path segment. The Security page has the full list.

  • Retention is a count, not a clock

    We keep a fixed number of recent masked samples per response variant, set by your plan. Nothing expires on a timer: an older sample rolls off only when a newer one arrives. Fingerprints and generated stubs are never deleted to make room.

  • The fingerprint is real

    A request shape, not its contents. The digest is built from structure alone, so no body value reaches it unless you opt that field in as a variant key. See what the digest covers.

Frequently asked questions

Does Stubsmith see my API request/response bodies?
Not unless you ask us to. The SDK masks request and response bodies fail-closed, inside your own process, so Stubsmith receives typed placeholders and field names. Values arrive in the clear only on scalars you gave a keep rule, and by default there are none. Two things sit outside that rule: a path segment the SDK cannot recognise as an identifier, and the transport headers a stub needs in order to be replayed. The Security page has the full list.
If everything is masked, will my tests still exercise the right code paths? Which fields need a keep rule?
Yes, for the fields you keep, and keeping is deliberate. Default masking wipes the values a test branches on, such as a status or an error code, so you approve a keep rule for each scalar your tests assert on. Each rule is exact: keeping an object does not keep the scalars inside it. Kept strings still pass the SDK's pattern scrub for email- and card-shaped values, which is a backstop rather than a substitute for choosing what to keep. A new field stays masked until you decide. The docs explain which fields to keep.
What's a fingerprint?
A fingerprint is a request shape: the method and path template, plus a blake2b-64 digest over the request's sorted key-paths, query-parameter names and content type. Values are not an input unless you opt a field in as a variant key. Fingerprints are capped per plan; at the cap, new shapes are not stored, existing ones keep capturing, and the Billing page shows a notice. We never delete one to make room; you can delete them yourself, and closing your workspace removes them. The docs cover how fingerprints work and path templates.
How long are samples kept?
Stubsmith keeps a rolling window of recent masked samples for each distinct response a request shape returns: 3 per response variant (Free), 5 (Solo), 10 (Team), 25 (Business). Older samples are deleted as newer ones arrive, and the window is a count, not an age. Fingerprints, request types and generated stubs are kept indefinitely. The docs describe sample windows in detail.
Where is my data hosted?
What we store runs in the EU: compute and the database on UpCloud (a Finnish company) in Amsterdam, object storage on Scaleway (a French company) in Amsterdam, and transactional email through Scaleway in Paris. No capture, sample or stub is processed outside the EU or by a US-headquartered provider. Our mailboxes are hosted by Infomaniak in Switzerland, which holds an EU adequacy decision; that is correspondence only, and no sample data reaches a mailbox. See the Security page for details.
How does billing and VAT work?
Prices include 21% NL VAT, or exclude it if your browser suggests you are outside Europe; the pricing page toggle switches between the two. Creem, our merchant of record, handles checkout, charges and remits the VAT for your billing country, and emails your receipt. Cancel any time from the dashboard. Annual plans are available at a discount.

Start with the free plan

One project, 50 fingerprints, no credit card. Try the full flow on one service.