api-forge
CLI tool for API testing, load testing, and mock server generation.
Why api-forge?
Testing APIs shouldn't require heavy frameworks or complex setup. api-forge is a lightweight CLI tool that provides:
- Test Suites — Define API tests in simple YAML files
- Assertions — Validate status codes, headers, JSON paths, response times
- Variable Chaining — Extract values from responses for use in subsequent requests
- Load Testing — Stress test endpoints with configurable concurrency
- Mock Server — Spin up mock APIs from YAML for development/testing
- Quick Requests — cURL-like commands with pretty output
Installation
pip install api-forge-cli
Quick Start
# Make a quick GET request
api-forge get https://jsonplaceholder.typicode.com/posts/1
# Run a test suite
api-forge test suite.yaml
# Load test an endpoint
api-forge load https://api.example.com/health -n 50 -d 10
# Start a mock server
api-forge mock mock.yaml
# Generate example configs
api-forge init --type test -o suite.yaml
Commands
get / post / put / delete — Quick Requests
# GET request
api-forge get https://api.example.com/users
# POST with JSON body
api-forge post https://api.example.com/users -d '{"name": "Alice"}'
# With custom headers
api-forge get https://api.example.com/users -H "Authorization:Bearer token"
# PUT request
api-forge put https://api.example.com/users/1 -d '{"name": "Bob"}'
# DELETE request
api-forge delete https://api.example.com/users/1
test — Run API Test Suites
api-forge test suite.yaml
api-forge test tests/api-tests.yaml -v
Test suites are defined in YAML:
name: "User API Tests"
base_url: "https://api.example.com"
headers:
Authorization: "Bearer {{token}}"
variables:
token: "your-api-token"
requests:
- name: "Get all users"
url: "/users"
method: GET
assertions:
- type: status
expected: 200
- type: response_time
expected: 2000
- name: "Create user"
url: "/users"
method: POST
body:
name: "Alice"
email: "alice@example.com"
assertions:
- type: status
expected: 201
- type: json_path
path: "id"
expected: 1
variables:
new_user_id: "id" # Extract for next request
- name: "Get created user"
url: "/users/{{new_user_id}}"
method: GET
assertions:
- type: status
expected: 200
load — Load Testing
# Quick load test
api-forge load https://api.example.com/health -n 50 -d 30
# From config file
api-forge load -c loadtest.yaml
| Option | Description |
|---|---|
-n, --concurrency |
Number of concurrent connections (default: 10) |
-d, --duration |
Test duration in seconds (default: 10) |
--rps |
Requests per second limit (0 = unlimited) |
-c, --config |
Load test config YAML file |
Load test config:
url: "https://api.example.com/users"
method: GET
concurrency: 100
duration_seconds: 60
requests_per_second: 500
timeout: 10.0
headers:
Authorization: "Bearer token"
Output includes:
- Total requests, success/failure counts
- Requests per second achieved
- Latency percentiles (min, avg, p50, p95, p99, max)
- Status code distribution
- Error samples
mock — Mock Server
# Start mock server from config
api-forge mock mock.yaml
# Custom port
api-forge mock mock.yaml -p 3000
# Default server (returns 404 for all)
api-forge mock
Mock server config:
name: "Test API"
port: 8000
cors: true
endpoints:
- path: "/health"
method: GET
status: 200
body:
status: "healthy"
- path: "/users"
method: GET
status: 200
headers:
X-Total-Count: "100"
body:
- id: 1
name: "Alice"
- id: 2
name: "Bob"
- path: "/users"
method: POST
status: 201
body:
id: 3
message: "Created"
- path: "/slow"
method: GET
status: 200
delay_ms: 2000 # Simulate latency
body:
message: "Slow response"
- path: "/users/*" # Wildcard matching
method: GET
status: 200
body:
id: 1
name: "User"
init — Generate Examples
api-forge init --type test -o suite.yaml
api-forge init --type load -o loadtest.yaml
api-forge init --type mock -o mock.yaml
Assertion Types
| Type | Description | Example |
|---|---|---|
status |
HTTP status code | expected: 200 or expected: [200, 201] |
header |
Header value contains | header: Content-Type, expected: json |
body_contains |
Body contains string | expected: "success" |
json_path |
JSON path equals value | path: user.id, expected: 123 |
body_regex |
Body matches regex | expected: "\\d{5}" |
response_time |
Max response time (ms) | expected: 500 |
Variable Substitution
Use {{variable_name}} syntax in URLs, headers, and body:
variables:
api_key: "secret123"
user_id: "456"
requests:
- name: "Get user"
url: "/users/{{user_id}}"
headers:
X-API-Key: "{{api_key}}"
Extract variables from responses for request chaining:
requests:
- name: "Login"
url: "/auth/login"
method: POST
body:
username: "admin"
password: "secret"
variables:
auth_token: "token" # Extract $.token from response
- name: "Protected endpoint"
url: "/protected"
headers:
Authorization: "Bearer {{auth_token}}"
Examples
Full Test Suite with Chaining
name: "E-commerce API"
base_url: "https://api.shop.example.com"
requests:
- name: "Create product"
url: "/products"
method: POST
body:
name: "Widget"
price: 29.99
assertions:
- type: status
expected: 201
variables:
product_id: "id"
- name: "Get product"
url: "/products/{{product_id}}"
assertions:
- type: json_path
path: "name"
expected: "Widget"
- name: "Delete product"
url: "/products/{{product_id}}"
method: DELETE
assertions:
- type: status
expected: 204
Load Test with Detailed Config
url: "https://api.example.com/search"
method: POST
concurrency: 200
duration_seconds: 120
requests_per_second: 1000
timeout: 5.0
headers:
Content-Type: "application/json"
body:
query: "test"
limit: 10
Development
git clone https://github.com/SanjaySundarMurthy/api-forge.git
cd api-forge
pip install -e ".[dev]"
pytest tests/ -v
Author
Sanjay Sundar Murthy
- GitHub: @SanjaySundarMurthy
- Email: sanjaysundarmurthy@gmail.com
License
MIT License — see LICENSE for details.
Metadata
Release files for api-forge-cli 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| api_forge_cli-1.0.0.tar.gz | 21.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| api_forge_cli-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.2 kB
Release files / api_forge_cli-1.0.0.tar.gz
| Download URL | api_forge_cli-1.0.0.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bdcbb4966892fffff448e50d9b2a1bd067b33f9675cee9bb88e96cf2cac54e64
|
|
BLAKE2b-256 checksum How to use checksums |
88eda2a2ec63037fb8340299a61178407f1bebf05edf5a5c385e9b9c34e0fd93
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Release files / api_forge_cli-1.0.0-py3-none-any.whl
| Download URL | api_forge_cli-1.0.0-py3-none-any.whl |
|---|---|
| Size | 18.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0e2daf870ec540a65a3338020917dc7c6b69b3bc10a23841f80e0ac1dd80f254
|
|
BLAKE2b-256 checksum How to use checksums |
770ccb73e5458f46d61fefe9139c47a355415d58339f0497d9419fdc49cdad68
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Security for api-forge-cli 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.