Blast is a fast, config-driven API load tester and traffic generator written in Rust.
Describe your API once in a blast.config.json file, then hit every endpoint with a single command. Blast supports fake data generation, response variable extraction, and request chaining — so you can register a user with a random email, log in, grab the access token, and use it in later requests without writing a single line of code.
- Config-driven — describe your endpoints in one JSON file, no scripting required
- Fake data templating — drop placeholders like
{{fake.email}}or{{fake.uuid}}into request bodies and headers - Request chaining — extract values from responses (e.g.
data.access_token) and reuse them in later requests as{{access_token}} - Status assertions — declare the status code each endpoint should return; mismatches are reported as failures
- Latency reporting — per-request latency in milliseconds
- Database seeding — tag endpoints with
"seed"and runblast seedto populate your database with N iterations of fake data, with configurable concurrency - Load testing — tag endpoints with
"run"and fireblast runto send traffic at a fixed requests-per-second rate for a set duration, with live progress and p50/p95/p99/p999 latency output - Setup phase — declare a
setupblock to run authentication or warm-up requests once before a load test, with extracted values (e.g. tokens) automatically passed into every subsequent request - CI friendly — non-zero exit code when any endpoint fails, so it slots straight into a pipeline
Build from source (requires a recent Rust toolchain):
git clone https://github.com/Walon-Foundation/blast.git
cd blast
cargo install --path .# 1. Create a starter blast.config.json in the current directory
blast init
# 2. Edit the config to match your API, then sanity-check it
blast validate
# 3. Hit every endpoint once and verify status codes
blast check
# 4. Seed the database with 50 fake records, 5 at a time
blast seed --count 50 --concurrency 5
# 5. Fire 20 req/sec at tagged endpoints for 60 seconds
blast run --rps 20 --duration 60Example check output:
✓ health check GET /health 4ms
✓ register user POST /api/v1/auth/register 31ms
✓ login POST /api/v1/auth/login 27ms
3/3 passed
Example seed output:
seeding 10 iterations × 2 endpoints (concurrency: 1)
Iterations: 10
Passed: 10
Total requests: 20
all iterations passed
Example run output:
elapsed: 1s sent: 20 success: 20 p99: 14ms
elapsed: 2s sent: 40 success: 40 p99: 12ms
...
Total requests: 600
Duration: 30s
Success rate: 100.0%
Latency
p50: 8ms
p95: 13ms
p99: 18ms
p999: 45ms
| Command | Description |
|---|---|
blast init [path] |
Create a starter blast.config.json in the given directory (default: current directory) |
blast check |
Hit every endpoint once, verify status codes, and report latency |
blast validate |
Validate blast.config.json and report any issues |
blast seed |
Run all endpoints tagged "seed" N times to populate a database with fake data |
blast run |
Fire requests at a fixed rate for a set duration and report latency percentiles |
All commands accept --config <path> to point at a different config location.
| Flag | Default | Description |
|---|---|---|
--count |
10 |
Number of seeding iterations to run |
-j / --concurrency |
1 |
Maximum number of iterations running in parallel |
| Flag | Default | Description |
|---|---|---|
--rps |
10 |
Target requests per second |
-d / --duration |
30 |
How long to run the load test, in seconds |
A blast.config.json looks like this:
{
"base_url": "http://localhost:3000/",
"headers": {
"Content-Type": "application/json"
},
"setup": [
{
"name": "login",
"method": "POST",
"path": "/api/v1/auth/login",
"body": {
"email": "admin@example.com",
"password": "Admin1234!"
},
"expect_status": 200,
"extract": {
"access_token": "data.access_token"
}
}
],
"endpoints": [
{
"name": "health check",
"method": "GET",
"path": "/health",
"expect_status": 200,
"tags": ["check", "seed", "run"]
},
{
"name": "register user",
"method": "POST",
"path": "/api/v1/auth/register",
"body": {
"email": "{{fake.email}}",
"password": "{{fake.password}}"
},
"expect_status": 201,
"tags": ["seed"]
},
{
"name": "list users",
"method": "GET",
"path": "/api/v1/users",
"headers": {
"Authorization": "Bearer {{access_token}}"
},
"expect_status": 200,
"tags": ["run"]
}
]
}| Field | Required | Description |
|---|---|---|
base_url |
yes | Base URL prepended to every endpoint path |
headers |
no | Headers sent with every request (endpoint headers override these) |
endpoints |
yes | List of endpoints executed by check, seed, and run |
setup |
no | Requests run once before a load test to bootstrap context (e.g. login to get a token) |
| Field | Required | Description |
|---|---|---|
name |
yes | Human-readable name shown in output |
method |
yes | One of GET, POST, PUT, PATCH, DELETE |
path |
yes | Path appended to base_url |
headers |
no | Per-endpoint headers, merged over the global ones |
body |
no | JSON request body; supports {{...}} placeholders |
expect_status |
no | Expected status code; if omitted, any status below 500 passes |
extract |
no | Map of variable name → dot path to pull values out of the JSON response |
tags |
no | List of string tags used to select which endpoints a command targets (see Tags) |
Use these anywhere in headers or request bodies:
| Placeholder | Generates |
|---|---|
{{fake.email}} |
Random email address |
{{fake.username}} |
Random username |
{{fake.password}} |
Random 8–16 character password |
{{fake.name}} |
Full name |
{{fake.firstname}} / {{fake.lastname}} |
First / last name |
{{fake.word}} / {{fake.sentence}} / {{fake.paragraph}} |
Lorem text |
{{fake.company}} |
Company name |
{{fake.city}} / {{fake.country}} |
Location names |
{{fake.uuid}} |
Random UUID v4 |
Tags let you group endpoints so different commands target different subsets.
{
"name": "register user",
"method": "POST",
"path": "/api/v1/auth/register",
"body": { "email": "{{fake.email}}", "password": "{{fake.password}}" },
"expect_status": 201,
"tags": ["seed"]
}blast seedruns only endpoints that include the"seed"tag.blast runruns only endpoints that include the"run"tag.- If no endpoint in the config has any tags, both commands fall back to running all endpoints.
- An endpoint can carry multiple tags (
["seed", "run"]) and will be included whenever any of its tags match.
The optional setup array runs once before blast run, in order, before any load traffic is sent. It works exactly like a regular endpoint sequence — responses are parsed and extract rules populate a shared context that is then passed to every load-test request. If any setup step fails, blast aborts with an error rather than firing incorrect load.
A typical use: log in once and extract an access token so that all subsequent load-test requests carry a valid Authorization header without each iteration needing to authenticate.
Endpoints run in order and share a context. When an endpoint declares an extract rule, the value at the given dot path (e.g. data.access_token, including array indices like items.0.id) is stored under the variable name and can be referenced by any later endpoint:
{
"name": "get profile",
"method": "GET",
"path": "/api/v1/me",
"headers": {
"Authorization": "Bearer {{access_token}}"
},
"expect_status": 200
}Contributions are welcome — see CONTRIBUTING.md to get started. For security issues, please read SECURITY.md.
Blast is released under the MIT License.