# 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; }' ```