Tutorial: Capture And Replay HTTP Calls
This tutorial captures a real HTTP request from normal Python app startup, replays it locally, compares the replay report, and generates a pytest provider replay guard.
The example uses a public JSON endpoint. For private APIs, keep header values and secrets out of tutorial output. ReplayLab records safe header names by default, not header values.
ReplayLab matches HTTP replay calls by a deterministic request envelope: provider, method, canonical URL, query params, safe header names, and supported body content. Query values participate in matching; header values do not.
Supported public-alpha request shapes are:
- query params as mappings, such as
params={"page": 1}, or ordered two-item pairs, such asparams=(("tag", "a"), ("tag", "b")) - request bodies through exactly one of
json=..., text or bytesdata=..., or text or bytescontent=... - JSON, text, and bytes response bodies
ReplayLab still rejects file uploads, multipart bodies, streaming uploads, streaming downloads, and multiple request body sources in one call.
Setup
Install dependencies:
python -m venv .venv
source .venv/bin/activate
pip install replaylab requests
For local repo development before package publication:
uv sync --all-packages --all-groups
uv pip install requests
Startup Instrumentation
Create tutorial_http_app.py.
The app keeps normal requests code.
ReplayLab setup happens near startup, then handle.capture(...) scopes the HTTP work you want stored as one capsule.
import replaylab
import requests
from replaylab import CapturePayloadPolicy
def fetch_slideshow_title() -> str:
response = requests.get(
"https://httpbin.org/json",
timeout=10,
)
response.raise_for_status()
payload = response.json()
return str(payload["slideshow"]["title"])
def main() -> None:
handle = replaylab.init(
project_name="tutorial-http",
auto_patch_integrations=("requests",),
capture_payload_policy=CapturePayloadPolicy.FULL,
)
with handle.capture(
"httpbin_slideshow_lookup",
labels=("tutorial", "http"),
) as capture:
print(fetch_slideshow_title())
if capture.capsule is not None:
print(f"ReplayLab capsule: {capture.capsule.capsule_path}")
if __name__ == "__main__":
main()
Capture Scope
Capture uses your normal app command. There is no ReplayLab wrapper process in this production-style path.
uv run python tutorial_http_app.py
What good looks like:
Sample Slide Show
ReplayLab capsule: .replaylab/capsules/<capsule_id>
Capsule List
uv run replaylab capsule list --local-store-root .replaylab
Pick the capsule whose integrations include requests, auto_patch, and same_process.
Inspect
uv run replaylab capsule inspect <capsule_id> --local-store-root .replaylab
What good looks like:
Boundaries: 1
Providers: requests
Payloads: 2
Boundary 0: http requests GET https://httpbin.org/json (succeeded)
Replay
Replay is local regression tooling.
It runs the same application command under replaylab replay; when the request matches the capsule, ReplayLab serves a ReplayHttpResponse instead of calling the live HTTP provider.
The app keeps the same startup replaylab.init(...) and handle.capture(...) code in replay mode; ReplayLab treats that capture scope as a no-op while the CLI-owned replay runtime serves provider calls.
uv run replaylab replay <capsule_id> \
--local-store-root .replaylab \
--auto-patch-integrations requests \
--report-id replay_tutorial_http \
-- python tutorial_http_app.py
Compare
uv run replaylab report compare \
<capsule_id> \
.replaylab/replays/replay_tutorial_http/report.json \
--local-store-root .replaylab
What good looks like:
Status: succeeded
Expected boundaries: 1
Replayed: 1
Problems: 0
Generate-Test
uv run replaylab generate-test <capsule_id> \
--output tests/regression/test_tutorial_http_replay.py \
--fixture-root tests/fixtures/replaylab/capsules \
--app-root . \
--auto-patch-integrations requests \
-- python tutorial_http_app.py
Run the generated test:
uv run pytest tests/regression/test_tutorial_http_replay.py