Minimal curl wrapper for AI agents - test APIs with filtered, token-efficient output
Project description
reqcap
Minimal HTTP client that returns only what you need — status, timing, and the specific JSON fields you ask for. Run templates, save snapshots, test your features e2e quickly.
[!NOTE] ✨ Built for AI agents too. Filtered output saves tokens,
--list-templateslets agents discover existing test scenarios,--diffcatches regressions, and--install-skillteaches Claude Code to use reqcap automatically.
Why reqcap?
A typical curl -s https://httpbin.org/json dumps 42 lines. reqcap gives you just the fields you asked for:
reqcap GET https://httpbin.org/json -f "slideshow.title,slideshow.author"
STATUS: 200
TIME: 45ms
BODY:
{
"slideshow": {
"title": "Sample Slide Show",
"author": "Yours Truly"
}
}
Save requests as reusable templates, snapshot known-good responses, diff them later to catch regressions — all from the CLI.
reqcap -t list-users
reqcap -t list-users --snapshot baseline
reqcap -t list-users --diff baseline
reqcap --list-templates
Features
- Templates — reusable YAML request scenarios with variables, auth, and chaining
- Snapshots — save responses and diff to detect regressions
- Response filtering — extract nested fields, array slices, specific indices
- Assertions —
--assert status=200exits 1 on failure - Request chaining — export response values and feed them into the next request
- Template dependencies — declarative
depends:for multi-step flows - Config file — base URLs, default headers, env var substitution
- Placeholders —
{{uuid}},{{date}},{{env.VAR}}auto-expand in templates - Auth types — bearer, API key, basic auth in config or per-template
- Form data —
--formfor multipart uploads - Curl import — paste a curl command and run it through reqcap
- History — replay recent requests
- Project init — scaffold config and directories
Install
Requires uv.
uv tool install reqcap
Quick start
Scaffold config and directories:
reqcap --init
This creates .reqcap.yaml, templates/, and snapshots/.
Test an endpoint with filtered output:
reqcap GET https://httpbin.org/json -f "slideshow.title,slideshow.author"
POST with a JSON body:
reqcap POST https://httpbin.org/post -b '{"name": "test", "email": "test@example.com"}'
Run a saved template:
reqcap -t login -v email=admin@test.com -v password=secret
Snapshot a response, then diff later:
reqcap GET /api/health --snapshot baseline
reqcap GET /api/health --diff baseline
See what templates are available:
reqcap --list-templates
Templates
Templates are standalone YAML files — each template is its own file, not embedded in the config. This keeps templates portable, version-controllable, and easy to share.
[!TIP] For AI agents: An agent can run
reqcap --list-templatesat the start of a task to discover what test scenarios already exist, then re-run them as it makes changes — like lightweight e2e tests it didn't have to write.
Creating a template
Create a YAML file in your templates/ directory:
# templates/login.yaml
description: Authenticate user
url: /api/auth/login
method: POST
body:
email: ""
password: ""
fields:
- name: email
path: email
- name: password
path: password
exports:
token: body.access_token
filter:
body_fields: [access_token, expires_in]
Using templates
Run by name (resolves to templates/login.yaml):
reqcap -t login -v email=admin@test.com -v password=secret
Run by file path:
reqcap -t templates/login.yaml -v email=admin@test.com -v password=secret
List all available templates:
reqcap --list-templates
Template file location
Templates are resolved in this order:
- Exact file path (e.g.
reqcap -t path/to/my-template.yaml) templates_dirfrom config + name (e.g.custom-templates/login.yaml)./templates/+ name (e.g.templates/login.yaml)
Template properties
| Property | Description |
|---|---|
name |
Optional identifier (defaults to filename) |
description |
Shown in --list-templates |
url |
Endpoint path (appended to base_url) |
method |
HTTP method: GET, POST, PUT, PATCH, DELETE |
base_url |
Override the default base_url for this template |
headers |
Template-specific headers (merged with defaults) |
auth |
Template-specific auth (overrides default) |
body |
Request body template (JSON object) |
fields |
Variables injected into body via -v |
exports |
Auto-export response values (see Request chaining) |
filter |
Template-specific response filter |
depends |
List of template names to run first (see Template dependencies) |
snapshot |
Auto-save response: {enabled: true, name: "..."} |
Template fields
The fields array defines variables that get injected into the request body at the specified JSON path.
# templates/chat.yaml
url: /api/chat
method: POST
body:
messages:
- role: "user"
content: ""
fields:
- name: prompt
path: messages[0].content
reqcap -t chat -v prompt="Hello, world"
The body becomes {"messages": [{"role": "user", "content": "Hello, world"}]}.
Template with per-request auth
# templates/create-user.yaml
url: /api/users
method: POST
auth:
type: bearer
token: "{{token}}"
body:
id: "{{uuid}}"
name: ""
email: ""
created_at: "{{date}}"
fields:
- name: name
path: name
- name: email
path: email
filter:
body_fields: [id, name, email]
reqcap -t create-user -v token=sk-abc123 -v name=Alice -v email=alice@test.com
Snapshots
Save responses and diff against them to detect regressions.
reqcap GET /api/health --snapshot baseline
reqcap GET /api/health --diff baseline
reqcap --list-snapshots
Templates can auto-save snapshots with snapshot: {enabled: true}.
[!TIP] For AI agents: Snapshot a response before making changes, then
--diffafter to verify nothing broke. Agents get a clear pass/fail signal without eyeballing JSON.
Response filtering
The -f/--filter flag extracts only the fields you specify from the response body.
[!TIP] For AI agents: Filtered output keeps responses small and token-efficient. An agent checking a 500-field response only sees the 3 fields it asked for.
Syntax
| Pattern | Meaning |
|---|---|
field |
Top-level field |
nested.field |
Nested object field |
data[].id |
Field from every array element |
data[0].id |
Field from a specific array index |
data[-1] |
Last element of an array |
data[2:5] |
Slice of array elements |
headers[Content-Type] |
Bracket notation for dict keys |
* |
No filtering (return everything) |
Examples
reqcap GET /api/users -f "total,page"
reqcap GET /api/user/1 -f "profile.name,profile.role"
reqcap GET /api/users -f "data[].id,data[].name"
reqcap GET /api/users -f "data[-1].name"
reqcap GET /api/users -f "data[0:3].id"
reqcap GET /api/users -f "total,data[].id,data[].email"
Given this response:
{
"total": 150,
"page": 1,
"data": [
{"id": 1, "name": "Alice", "email": "alice@co.com", "metadata": {"large": "..."}},
{"id": 2, "name": "Bob", "email": "bob@co.com", "metadata": {"large": "..."}}
]
}
reqcap GET /api/users -f "total,data[].id,data[].name" returns:
{
"total": 150,
"data": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}
Output modes
Default output includes status, timing, and filtered body. --verbose adds response headers. --raw outputs JSON body only, with no status/time lines — useful for piping:
reqcap GET /api/users --raw | jq '.data[0]'
Assertions
Assert response values and exit with code 1 on failure:
reqcap GET /api/health --assert status=200
reqcap GET /api/user/1 --assert "body.role=admin"
reqcap POST /api/items --assert "status!=500" --assert "body.id!=null"
[!TIP] For AI agents: Assertions give a clear exit code agents can branch on — no output parsing needed.
Request chaining
Chain requests together by exporting values from one response and using them in the next.
Template auto-exports
Define exports in a template to automatically extract and export values from the response:
# templates/login.yaml
url: /api/auth/login
method: POST
body: { email: "", password: "" }
fields:
- { name: email, path: email }
- { name: password, path: password }
exports:
token: body.access_token
user_id: body.user.id
Export statements are printed to stderr as shell export commands. Capture them with eval:
eval $(reqcap -t login -v email=a@b.com -v password=x 2>&1 1>/dev/null)
reqcap -t get-users -v token=$reqcap_token
CLI --export flag
Export arbitrary fields from any response:
reqcap GET /api/me --export token=body.access_token
reqcap GET /api/me --export id
The shorthand --export id is equivalent to --export id=body.id.
How chaining works
--exportprintsexport reqcap_<name>=<value>to stderr- Wrap with
eval $(... 2>&1 1>/dev/null)to set env vars in your shell - Pass them to the next request with
-vor-H
For declarative chaining, use depends: in templates instead (see Template dependencies).
Template dependencies
Templates can declare depends: to run prerequisite templates automatically. Exported variables flow through the chain.
# templates/login.yaml
method: POST
url: /api/auth/login
body: { email: "", password: "" }
fields:
- { name: email, path: email }
- { name: password, path: password }
exports:
token: body.access_token
# templates/get-users.yaml
depends:
- login
method: GET
url: /api/users
headers:
Authorization: "Bearer {{token}}"
Running reqcap -t get-users -v email=admin -v password=secret executes login first (exporting token), then get-users with the token injected.
Dependencies execute depth-first. Circular dependencies are detected and reported.
[!TIP] For AI agents: A single
reqcap -t get-userscan handle login + auth + the actual request. Agents don't need to manage multi-step auth flows manually.
Config file
Create a .reqcap.yaml in your project root (or pass -c path/to/config.yaml). The config sets defaults — templates are separate files (see Templates).
reqcap auto-discovers config files in this order: .reqcap.yaml, .reqcap.yml, reqcap.yaml, reqcap.yml.
Minimal config
defaults:
base_url: http://localhost:3000
headers:
Content-Type: application/json
With this config, reqcap GET /api/users resolves to http://localhost:3000/api/users.
Full config structure
defaults:
base_url: ${API_BASE_URL}
env_file: .env
timeout: 30
templates_dir: templates
headers:
Content-Type: application/json
auth:
type: bearer
token: ${API_TOKEN}
filter:
status: true
headers: false
body_fields: []
Environment variables
Config values can reference environment variables with ${VAR_NAME}. These are resolved from:
- A
.envfile (specified bydefaults.env_file) - The shell environment
defaults:
base_url: ${API_BASE_URL}
auth:
token: ${API_TOKEN}
# .env
API_BASE_URL=http://localhost:3000
API_TOKEN=sk-abc123
Placeholders
In templates (URLs, bodies, headers):
| Placeholder | Value |
|---|---|
{{env.VAR_NAME}} |
Environment variable |
{{uuid}} |
Random UUID v4 |
{{timestamp}} |
Unix timestamp (seconds) |
{{timestamp_ms}} |
Unix timestamp (milliseconds) |
{{date}} |
ISO 8601 datetime |
{{my_var}} |
Variable from -v flag or dep export |
In config files (.reqcap.yaml): use ${VAR_NAME} for environment variable substitution.
Example
# templates/create-item.yaml
url: /api/items
method: POST
body:
id: "{{uuid}}"
created_at: "{{date}}"
owner: "{{env.USER_ID}}"
name: ""
fields:
- name: name
path: name
Auth types
Configure authentication in defaults.auth or per-template auth:
Bearer token
auth:
type: bearer
token: ${API_TOKEN}
API key (custom header)
auth:
type: api-key
token: ${API_KEY}
header: X-API-Key
Basic auth
auth:
type: basic
username: ${USER}
password: ${PASS}
Form data
Submit form data and file uploads with --form:
reqcap POST /api/upload --form name=test --form file=@photo.jpg
Curl import
Parse and execute a curl command directly:
reqcap --import-curl "curl -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
-d '{\"name\": \"test\"}'"
Supports -X, -H, -d, --data, --data-raw, --json, and quoted strings.
History
reqcap saves the last 50 requests (auth headers excluded).
reqcap --history
reqcap --replay 0
reqcap --replay 0 -f "id,name"
Project init
Scaffold a new project with config and directories:
reqcap --init
This creates .reqcap.yaml, templates/, and snapshots/.
[!TIP] For AI agents: Run
reqcap --install-skill claudeto install an agent skill that teaches Claude Code how to use reqcap — no docs reading needed.
CLI flags reference
| Flag | Short | Description |
|---|---|---|
METHOD URL |
Direct request (positional args) | |
--template |
-t |
Run a template file (name or path) |
--config |
-c |
Config file path |
--templates-dir |
Override templates directory | |
--var |
-v |
Variable as key=value (repeatable) |
--body |
-b |
Request body (JSON string) |
--header |
-H |
HTTP header as Name: Value (repeatable) |
--filter |
-f |
Comma-separated body fields to extract |
--timeout |
Request timeout in seconds | |
--verbose |
Include response headers | |
--raw |
JSON body only (for piping) | |
--export |
Export response field as env var (repeatable) | |
--assert |
Assert status=200 or body.field!=val (exit 1 on fail) |
|
--snapshot |
Save response as named snapshot | |
--diff |
Diff response against saved snapshot | |
--list-snapshots |
List saved snapshots | |
--snapshots-dir |
Override snapshots directory | |
--form |
Form field KEY=VALUE or KEY=@FILE (repeatable) |
|
--import-curl |
Parse and run a curl command | |
--history |
Show request history | |
--replay |
Replay history entry by index | |
--list-templates |
List template files in templates dir | |
--init |
Scaffold project (config + directories) | |
--install-skill |
Install skill data to agent directory | |
--help |
Full help with all syntax docs |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reqcap-1.0.2.tar.gz.
File metadata
- Download URL: reqcap-1.0.2.tar.gz
- Upload date:
- Size: 85.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b60de817f1ed9d5e54ed6fccccb7e51173c45f4924c7587d77761f811a81d86
|
|
| MD5 |
d01472e405233a6fa973d0251a8c9f2b
|
|
| BLAKE2b-256 |
49d455d41312b1ed9a8ff46a74f06f61585179a6f207970a294d01977d270232
|
Provenance
The following attestation bundles were made for reqcap-1.0.2.tar.gz:
Publisher:
publish.yml on atsaplin/reqcap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reqcap-1.0.2.tar.gz -
Subject digest:
2b60de817f1ed9d5e54ed6fccccb7e51173c45f4924c7587d77761f811a81d86 - Sigstore transparency entry: 946437570
- Sigstore integration time:
-
Permalink:
atsaplin/reqcap@292827e5e7a978b56598d856b95768f3cdf8b83f -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/atsaplin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@292827e5e7a978b56598d856b95768f3cdf8b83f -
Trigger Event:
release
-
Statement type:
File details
Details for the file reqcap-1.0.2-py3-none-any.whl.
File metadata
- Download URL: reqcap-1.0.2-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9a9abd6dc8860397bea67cd66223eddcb44bf9c7e9a52242c66a90fea0fea6e
|
|
| MD5 |
21139c61f737e0d4480a5c801fb9e376
|
|
| BLAKE2b-256 |
379920cc77c7d30bcbedc73db77aaf105e62dc06ecaa017522e74bc98c8f5708
|
Provenance
The following attestation bundles were made for reqcap-1.0.2-py3-none-any.whl:
Publisher:
publish.yml on atsaplin/reqcap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reqcap-1.0.2-py3-none-any.whl -
Subject digest:
f9a9abd6dc8860397bea67cd66223eddcb44bf9c7e9a52242c66a90fea0fea6e - Sigstore transparency entry: 946437610
- Sigstore integration time:
-
Permalink:
atsaplin/reqcap@292827e5e7a978b56598d856b95768f3cdf8b83f -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/atsaplin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@292827e5e7a978b56598d856b95768f3cdf8b83f -
Trigger Event:
release
-
Statement type: