# Examples
This guide provides practical examples for common Sarin use cases.
## Table of Contents
- [Basic Usage](#basic-usage)
- [Request-Based vs Duration-Based Tests](#request-based-vs-duration-based-tests)
- [Headers, Cookies, and Parameters](#headers-cookies-and-parameters)
- [Dynamic Requests with Templating](#dynamic-requests-with-templating)
- [Solving Captchas](#solving-captchas)
- [Request Bodies](#request-bodies)
- [File Uploads](#file-uploads)
- [Using Proxies](#using-proxies)
- [Output Formats](#output-formats)
- [Runtime Logging](#runtime-logging)
- [Docker Usage](#docker-usage)
- [Dry Run Mode](#dry-run-mode)
- [Show Configuration](#show-configuration)
- [Scripting](#scripting)
---
## Basic Usage
Send 1000 GET requests with 10 concurrent workers:
```sh
sarin -U http://example.com -r 1000 -c 10
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
```
Send requests with a custom timeout:
```sh
sarin -U http://example.com -r 1000 -c 10 -T 5s
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
timeout: 5s
```
## Request-Based vs Duration-Based Tests
**Request-based:** Stop after sending a specific number of requests:
```sh
sarin -U http://example.com -r 10000 -c 50
```
YAML equivalent
```yaml
url: http://example.com
requests: 10000
concurrency: 50
```
**Duration-based:** Run for a specific amount of time:
```sh
sarin -U http://example.com -d 5m -c 50
```
YAML equivalent
```yaml
url: http://example.com
duration: 5m
concurrency: 50
```
**Combined:** Stop when either limit is reached first:
```sh
sarin -U http://example.com -r 100000 -d 2m -c 100
```
YAML equivalent
```yaml
url: http://example.com
requests: 100000
duration: 2m
concurrency: 100
```
## Headers, Cookies, and Parameters
**Custom headers:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-H "Authorization: Bearer token123" \
-H "X-Custom-Header: value"
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
headers:
Authorization: Bearer token123
X-Custom-Header: value
```
**Multiple values for the same header (all sent in every request):**
> **Note:** When the same key appears as separate entries (in CLI or config file), all values are sent in every request.
```sh
sarin -U http://example.com -r 1000 -c 10 \
-H "X-Region: us-east" \
-H "X-Region: us-west"
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
headers:
- X-Region: us-east
- X-Region: us-west
```
**Cycling headers from multiple values (config file only):**
> **Note:** When multiple values are specified as an array on a single key, Sarin starts at a random index and cycles through all values in order. Once the cycle completes, it picks a new random starting point.
```yaml
url: http://example.com
requests: 1000
concurrency: 10
headers:
X-Region:
- us-east
- us-west
- eu-central
```
**Query parameters:**
```sh
sarin -U http://example.com/search -r 1000 -c 10 \
-P "query=test" \
-P "limit=10"
```
YAML equivalent
```yaml
url: http://example.com/search
requests: 1000
concurrency: 10
params:
query: "test"
limit: 10
```
**Dynamic query parameters:**
```sh
sarin -U http://example.com/users -r 1000 -c 10 \
-P "id={{ fakeit_Number 1 1000 }}" \
-P "fields=name,email"
```
YAML equivalent
```yaml
url: http://example.com/users
requests: 1000
concurrency: 10
params:
id: "{{ fakeit_Number 1 1000 }}"
fields: "name,email"
```
**Cookies:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-C "session_id=abc123" \
-C "user_id={{ fakeit_UUID }}"
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
cookies:
session_id: abc123
user_id: "{{ fakeit_UUID }}"
```
## Dynamic Requests with Templating
**Dynamic URL paths:**
Test different resource endpoints with random IDs:
```sh
sarin -U "http://example.com/users/{{ fakeit_UUID }}/profile" -r 1000 -c 10
```
YAML equivalent
```yaml
url: http://example.com/users/{{ fakeit_UUID }}/profile
requests: 1000
concurrency: 10
```
Test with random numeric IDs:
```sh
sarin -U "http://example.com/products/{{ fakeit_Number 1 10000 }}" -r 1000 -c 10
```
YAML equivalent
```yaml
url: http://example.com/products/{{ fakeit_Number 1 10000 }}
requests: 1000
concurrency: 10
```
**Generate a random User-Agent for each request:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-H "User-Agent: {{ fakeit_UserAgent }}"
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
headers:
User-Agent: "{{ fakeit_UserAgent }}"
```
Send requests with random user data:
```sh
sarin -U http://example.com/api/users -r 1000 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
```
YAML equivalent
```yaml
url: http://example.com/api/users
requests: 1000
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: '{"name": "{{ fakeit_Name }}", "email": "{{ fakeit_Email }}"}'
```
Use values to share generated data across headers and body:
```sh
sarin -U http://example.com/api/users -r 1000 -c 10 \
-M POST \
-V "ID={{ fakeit_UUID }}" \
-H "X-Request-ID: {{ .Values.ID }}" \
-B '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
```
YAML equivalent
```yaml
url: http://example.com/api/users
requests: 1000
concurrency: 10
method: POST
values: "ID={{ fakeit_UUID }}"
headers:
X-Request-ID: "{{ .Values.ID }}"
body: '{"id": "{{ .Values.ID }}", "name": "{{ fakeit_Name }}"}'
```
Generate random IPs and timestamps:
```sh
sarin -U http://example.com/api/logs -r 500 -c 20 \
-M POST \
-H "Content-Type: application/json" \
-B '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
```
YAML equivalent
```yaml
url: http://example.com/api/logs
requests: 500
concurrency: 20
method: POST
headers:
Content-Type: application/json
body: '{"ip": "{{ fakeit_IPv4Address }}", "timestamp": "{{ fakeit_Date }}", "action": "{{ fakeit_HackerVerb }}"}'
```
> For the complete list of 370+ template functions, see the **[Templating Guide](templating.md)**.
## Solving Captchas
Sarin can solve captchas through third-party services and embed the resulting token into the request. Three services are supported via dedicated template functions: **2Captcha**, **Anti-Captcha**, and **CapSolver**.
**Solve a reCAPTCHA v2 and submit the token in the request body:**
```sh
sarin -U https://example.com/login -M POST -r 1 \
-B '{"g-recaptcha-response": "{{ twocaptcha_RecaptchaV2 "YOUR_API_KEY" "SITE_KEY" "https://example.com/login" }}"}'
```
**Reuse a single solved token across multiple requests via `values`:**
```sh
sarin -U https://example.com/api -M POST -r 5 \
-V 'TOKEN={{ anticaptcha_Turnstile "YOUR_API_KEY" "SITE_KEY" "https://example.com/api" }}' \
-H "X-Turnstile-Token: {{ .Values.TOKEN }}" \
-B '{"token": "{{ .Values.TOKEN }}"}'
```
> See the **[Templating Guide](templating.md#captcha-functions)** for the full list of captcha functions and per-service support.
## Request Bodies
**Simple JSON body:**
```sh
sarin -U http://example.com/api/data -r 1000 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"key": "value"}'
```
YAML equivalent
```yaml
url: http://example.com/api/data
requests: 1000
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: '{"key": "value"}'
```
**Multiple bodies (randomly cycled):**
```sh
sarin -U http://example.com/api/products -r 1000 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"product": "laptop", "price": 999}' \
-B '{"product": "phone", "price": 599}' \
-B '{"product": "tablet", "price": 399}'
```
YAML equivalent
```yaml
url: http://example.com/api/products
requests: 1000
concurrency: 10
method: POST
headers:
Content-Type: application/json
body:
- '{"product": "laptop", "price": 999}'
- '{"product": "phone", "price": 599}'
- '{"product": "tablet", "price": 399}'
```
**Dynamic body with fake data:**
```sh
sarin -U http://example.com/api/orders -r 1000 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{
"order_id": "{{ fakeit_UUID }}",
"customer": "{{ fakeit_Name }}",
"email": "{{ fakeit_Email }}",
"amount": {{ fakeit_Price 10 500 }},
"currency": "{{ fakeit_CurrencyShort }}"
}'
```
YAML equivalent
```yaml
url: http://example.com/api/orders
requests: 1000
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: |
{
"order_id": "{{ fakeit_UUID }}",
"customer": "{{ fakeit_Name }}",
"email": "{{ fakeit_Email }}",
"amount": {{ fakeit_Price 10 500 }},
"currency": "{{ fakeit_CurrencyShort }}"
}
```
**Multipart form data:**
```sh
sarin -U http://example.com/api/upload -r 1000 -c 10 \
-M POST \
-B '{{ body_FormData "username" "john" "email" "john@example.com" }}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 1000
concurrency: 10
method: POST
body: '{{ body_FormData "username" "john" "email" "john@example.com" }}'
```
**Multipart form data with dynamic values:**
```sh
sarin -U http://example.com/api/users -r 1000 -c 10 \
-M POST \
-B '{{ body_FormData "name" (fakeit_Name) "email" (fakeit_Email) "phone" (fakeit_Phone) }}'
```
YAML equivalent
```yaml
url: http://example.com/api/users
requests: 1000
concurrency: 10
method: POST
body: '{{ body_FormData "name" (fakeit_Name) "email" (fakeit_Email) "phone" (fakeit_Phone) }}'
```
> **Note:** `body_FormData` automatically sets the `Content-Type` header to `multipart/form-data` with the appropriate boundary.
## File Uploads
**File upload with multipart form data:**
Upload a local file:
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-B '{{ body_FormData "title" "My Document" "document" "@/path/to/file.pdf" }}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
body: '{{ body_FormData "title" "My Document" "document" "@/path/to/file.pdf" }}'
```
**Multiple file uploads (same field name):**
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-B '{{ body_FormData "files" "@/path/to/file1.pdf" "files" "@/path/to/file2.pdf" }}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
body: |
{{ body_FormData
"files" "@/path/to/file1.pdf"
"files" "@/path/to/file2.pdf"
}}
```
**Multiple file uploads (different field names):**
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-B '{{ body_FormData "avatar" "@/path/to/photo.jpg" "resume" "@/path/to/cv.pdf" "cover_letter" "@/path/to/letter.docx" }}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
body: |
{{ body_FormData
"avatar" "@/path/to/photo.jpg"
"resume" "@/path/to/cv.pdf"
"cover_letter" "@/path/to/letter.docx"
}}
```
**File from URL:**
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-B '{{ body_FormData "image" "@https://example.com/photo.jpg" }}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
body: '{{ body_FormData "image" "@https://example.com/photo.jpg" }}'
```
> **Note:** Files (local and remote) are cached in memory after the first read, so they are not re-read for every request.
**Base64 encoded file in JSON body (local file):**
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"file": "{{ file_Base64 "/path/to/file.pdf" }}", "filename": "document.pdf"}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: '{"file": "{{ file_Base64 "/path/to/file.pdf" }}", "filename": "document.pdf"}'
```
**Base64 encoded file in JSON body (remote URL):**
```sh
sarin -U http://example.com/api/upload -r 100 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"image": "{{ file_Base64 "https://example.com/photo.jpg" }}", "filename": "photo.jpg"}'
```
YAML equivalent
```yaml
url: http://example.com/api/upload
requests: 100
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: '{"image": "{{ file_Base64 "https://example.com/photo.jpg" }}", "filename": "photo.jpg"}'
```
## Using Proxies
**Single HTTP proxy:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-X http://proxy.example.com:8080
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
proxy: http://proxy.example.com:8080
```
**SOCKS5 proxy:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-X socks5://proxy.example.com:1080
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
proxy: socks5://proxy.example.com:1080
```
**Multiple proxies (randomly cycled):**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-X http://proxy1.example.com:8080 \
-X http://proxy2.example.com:8080 \
-X socks5://proxy3.example.com:1080
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
proxy:
- http://proxy1.example.com:8080
- http://proxy2.example.com:8080
- socks5://proxy3.example.com:1080
```
**Proxy with authentication:**
```sh
sarin -U http://example.com -r 1000 -c 10 \
-X http://user:password@proxy.example.com:8080
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
proxy: http://user:password@proxy.example.com:8080
```
## Output Formats
**Table output (default):**
```sh
sarin -U http://example.com -r 1000 -c 10 -o table
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
output: table
```
**JSON output (useful for parsing):**
```sh
sarin -U http://example.com -r 1000 -c 10 -o json
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
output: json
```
**YAML output:**
```sh
sarin -U http://example.com -r 1000 -c 10 -o yaml
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
output: yaml
```
**No output (minimal memory usage):**
```sh
sarin -U http://example.com -r 1000 -c 10 -o none
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
output: none
```
**Hide the progress bar:**
```sh
sarin -U http://example.com -r 1000 -c 10 -p none
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
progress: none
```
## Runtime Logging
`--log-level` selects which runtime logs Sarin emits (comma-separated `info` and `error`, default `error`). `error` covers errors raised while generating a request, `info` covers every completed response (status, duration, headers, body). A request that fails to send is counted in the results rather than logged. Logs appear in the progress log box on an interactive terminal, go to stderr when piped, or go to a file with `--log-file`.
**Log responses and errors:**
```sh
sarin -U http://example.com -r 1000 -c 10 -l info,error
```
**Write logs to a file (the progress bar stays on screen):**
```sh
sarin -U http://example.com -r 1000 -c 10 -l info --log-file ./run.log
```
**Capture logs while keeping results on stdout:**
```sh
sarin -U http://example.com -r 1000 -l info -o json > stats.json 2> run.log
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
logLevel: info
output: json
```
## Docker Usage
**Basic Docker usage:**
```sh
docker run -it --rm aykhans/sarin -U http://example.com -r 1000 -c 10
```
**With local config file:**
```sh
docker run -it --rm -v $(pwd)/config.yaml:/config.yaml aykhans/sarin -f /config.yaml
```
**With remote config file:**
```sh
docker run -it --rm aykhans/sarin -f https://example.com/config.yaml
```
**Interactive mode with TTY:**
```sh
docker run --rm -it aykhans/sarin -U http://example.com -r 1000 -c 10
```
## Dry Run Mode
Test your configuration without sending actual requests:
```sh
sarin -U http://example.com -r 10 -c 1 -z \
-H "X-Request-ID: {{ fakeit_UUID }}" \
-B '{"user": "{{ fakeit_Name }}"}'
```
YAML equivalent
```yaml
url: http://example.com
requests: 10
concurrency: 1
dryRun: true
headers:
X-Request-ID: "{{ fakeit_UUID }}"
body: '{"user": "{{ fakeit_Name }}"}'
```
This validates templates.
## Show Configuration
Preview the merged configuration before running:
```sh
sarin -U http://example.com -r 1000 -c 10 \
-H "Authorization: Bearer token" \
-s
```
YAML equivalent
```yaml
url: http://example.com
requests: 1000
concurrency: 10
showConfig: true
headers:
Authorization: Bearer token
```
## Scripting
Transform requests using Lua or JavaScript scripts. Scripts run after template rendering, before the request is sent.
**Add a custom header with Lua:**
```sh
sarin -U http://example.com/api -r 1000 -c 10 \
-lua 'function transform(req) req.headers["X-Custom"] = "my-value" return req end'
```
YAML equivalent
```yaml
url: http://example.com/api
requests: 1000
concurrency: 10
lua: |
function transform(req)
req.headers["X-Custom"] = "my-value"
return req
end
```
**Modify request body with JavaScript:**
```sh
sarin -U http://example.com/api/data -r 1000 -c 10 \
-M POST \
-H "Content-Type: application/json" \
-B '{"name": "test"}' \
-js 'function transform(req) { var body = JSON.parse(req.body); body.timestamp = Date.now(); req.body = JSON.stringify(body); return req; }'
```
YAML equivalent
```yaml
url: http://example.com/api/data
requests: 1000
concurrency: 10
method: POST
headers:
Content-Type: application/json
body: '{"name": "test"}'
js: |
function transform(req) {
var body = JSON.parse(req.body);
body.timestamp = Date.now();
req.body = JSON.stringify(body);
return req;
}
```
**Load script from a file:**
```sh
sarin -U http://example.com/api -r 1000 -c 10 \
-lua @./scripts/transform.lua
```
YAML equivalent
```yaml
url: http://example.com/api
requests: 1000
concurrency: 10
lua: "@./scripts/transform.lua"
```
**Load script from a URL:**
```sh
sarin -U http://example.com/api -r 1000 -c 10 \
-js @https://example.com/scripts/transform.js
```
YAML equivalent
```yaml
url: http://example.com/api
requests: 1000
concurrency: 10
js: "@https://example.com/scripts/transform.js"
```
**Chain multiple scripts (Lua runs first, then JavaScript):**
```sh
sarin -U http://example.com/api -r 1000 -c 10 \
-lua @./scripts/auth.lua \
-lua @./scripts/headers.lua \
-js @./scripts/body.js
```
YAML equivalent
```yaml
url: http://example.com/api
requests: 1000
concurrency: 10
lua:
- "@./scripts/auth.lua"
- "@./scripts/headers.lua"
js: "@./scripts/body.js"
```
**Fetch a value from another endpoint before each request:**
Scripts can send their own requests with `http`. Here each request first fetches a CSRF token and sends it in a header and cookie. See [HTTP Requests in Scripts](configuration.md#http-requests-in-scripts) for all options.
```sh
sarin -U http://example.com/api/orders -r 1000 -c 10 \
-M POST \
-lua 'function transform(req)
local res = http("http://example.com/csrf")
req.headers["X-CSRF-Token"] = json.decode(res.body).token
req.cookies["session"] = res:cookie("session")
return req
end'
```
YAML equivalent
```yaml
url: http://example.com/api/orders
requests: 1000
concurrency: 10
method: POST
lua: |
function transform(req)
local res = http("http://example.com/csrf")
req.headers["X-CSRF-Token"] = json.decode(res.body).token
req.cookies["session"] = res:cookie("session")
return req
end
```
**Log in once per worker and reuse the token (JavaScript):**
```yaml
url: http://example.com/api/profile
requests: 1000
concurrency: 10
js: |
let token;
function login() {
const res = http("http://example.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user: "test", password: "secret" }),
});
if (res.status !== 200) throw new Error("login failed: " + res.status);
return res.json().token;
}
function transform(req) {
if (!token) token = login();
req.headers["Authorization"] = "Bearer " + token;
return req;
}
```
**Chain two calls, then use the result in the URL path (Lua):**
```yaml
url: http://example.com/api/placeholder
requests: 500
concurrency: 10
lua: |
function transform(req)
local token = json.decode(http("http://example.com/login", {
method = "POST",
body = json.encode({ user = "test", password = "secret" }),
headers = { ["Content-Type"] = "application/json" },
}).body).token
local orders = json.decode(http("http://example.com/api/orders", {
headers = { Authorization = "Bearer " .. token },
}).body)
req.path = "/api/orders/" .. orders[1].id
req.headers["Authorization"] = "Bearer " .. token
return req
end
```
**Refresh a token only when it expires (JavaScript):**
```yaml
url: http://example.com/api/profile
requests: 5000
concurrency: 20
js: |
let token;
let expiresAt = 0;
function transform(req) {
if (Date.now() >= expiresAt) {
const res = http("http://example.com/login", { method: "POST", body: "{}" });
token = res.json().token;
expiresAt = Date.now() + 60000; // refresh once a minute per worker
}
req.headers["Authorization"] = "Bearer " + token;
return req;
}
```
**Keep sending the main request even when the extra call fails:**
A failed `http` call raises an error, which skips the main request and records the failure in the results. Catch it to decide yourself:
```sh
sarin -U http://example.com/api/items -r 1000 -c 10 \
-lua 'function transform(req)
local ok, res = pcall(http, "http://example.com/api/flags")
if ok and res.status == 200 then
req.headers["X-Flags"] = res.body
else
req.headers["X-Flags"] = "default"
end
return req
end'
```
The JavaScript equivalent:
```sh
sarin -U http://example.com/api/items -r 1000 -c 10 \
-js 'function transform(req) {
try {
req.headers["X-Flags"] = http("http://example.com/api/flags").body;
} catch (e) {
req.headers["X-Flags"] = "default";
}
return req;
}'
```