LoadTester runs real load from 4 continents. No account, no subscription. Pay per test, get your PDF report, we forget you.
The fastest way to stress-test a URL. Raw HTTP requests, up to 100,000 per second.
| Parameter | Free | Standard ($19.37) | Heavy ($97.53) |
|---|---|---|---|
| RPS | 100 | 10,000 | 100,000 |
| Duration | 1 minute | 10 minutes | 60 minutes |
| Regions | 1 | All 4 | All 4 |
Simulate real user journeys through your app using Playwright browser automation.
.loadtest file — keep these on your computer.loadtest files and set the percentage split (must total 100%)| Parameter | Free | Standard ($47.21) | Heavy ($197.53) |
|---|---|---|---|
| Virtual browsers | 5 | 100 | 500 |
| Duration | 1 minute | 10 minutes | 60 minutes |
| Regions | 1 | All 4 | All 4 |
Write or paste a Playwright script. LoadTester runs it in a headless Chromium browser for each virtual user.
Export a single async function that receives a browser instance:
// login-flow.js — example scenario script module.exports = async function(browser) { const page = await browser.newPage(); // Navigate await page.goto('https://yourapp.com/login'); // Fill form await page.fill('#email', 'load-test@example.com'); await page.fill('#password', 'TestPassword123!'); await page.click('[type="submit"]'); // Wait for navigation await page.waitForURL('**/dashboard', { timeout: 10000 }); // Verify something is on screen await page.waitForSelector('.dashboard-title'); // Screenshot (captured in report) await page.screenshot({ path: 'dashboard.png' }); await page.close(); };
Record your interactions with your site. We open it in a real browser and capture every click, fill, and navigation as Playwright steps.
The recording captures: navigate, click, fill, select, check, keyboard events, and page assertions.
Build a scenario step-by-step with no code. Select a step type, fill in the parameters, and click Add Step.
| Type | Parameters | Description |
|---|---|---|
navigate | url | Go to a URL |
click | selector | Click an element (CSS selector) |
fill | selector, value | Type text into an input field |
wait | ms | Pause execution for N milliseconds |
screenshot | filename | Capture a screenshot (appears in report) |
assert | selector, text | Verify element contains expected text |
navigate("https://shop.example.com")
wait(1000)
click(".products-grid .product-card:first-child")
wait(500)
screenshot("product-page")
assert(".product-title", "Running Shoes")
click(".add-to-cart")
navigate("https://shop.example.com/cart")
assert(".cart-count", "1")
A .loadtest file is an encrypted binary blob. You keep it — we store nothing. Upload it when you are ready to run the test.
The file contains AES-256-GCM encrypted JSON with the following structure:
// Decrypted content (only our servers can read this) { "version": 1, "name": "Login flow", "created": 1712345678000, "type": "script" | "visual" | "recorded", "steps": [...] // or "script": "..." }
# Binary layout: [IV 12 bytes][Auth Tag 16 bytes][Ciphertext N bytes]
The encryption key never leaves our servers. The file is useless without us — that is intentional. We cannot be used as a DDoS relay because only we control when execution happens.
Before any test runs, you must prove you control the target domain. This prevents LoadTester from being used to attack sites you don't own.
lt_a3f9b2c1)/.well-known/loadtester-verify/{test-id}{test-id}:verified:{year} (shown on the test page)# Bash / any Linux server mkdir -p /var/www/html/.well-known/loadtester-verify echo "lt_a3f9b2c1:verified:2026" > /var/www/html/.well-known/loadtester-verify/lt_a3f9b2c1 # nginx — add to server block location /.well-known/loadtester-verify/ { root /var/www/html; add_header Content-Type text/plain; } # Node.js / Express app.get('/.well-known/loadtester-verify/:id', (req, res) => { res.send(`${req.params.id}:verified:2026`); });
The verification is valid for the duration of the test. After the test ends, you can remove the file. Each new test requires a new verification (new test ID).
The number of HTTP requests (or browser scenario iterations) completed per second. Reported per-region and as an aggregate. In scenario mode, this counts completed Playwright script executions, not individual page actions.
| Metric | Meaning | Target |
|---|---|---|
| P50 (median) | Half of requests are faster than this | < 200ms for typical APIs |
| P95 | 95% of requests complete within this time | < 500ms for most apps |
| P99 | 99% of requests complete within this time. Captures outliers and worst-case | < 1000ms |
A large gap between P50 and P99 indicates high variance — some requests are much slower than average, often a sign of database lock contention, GC pauses, or cold cache hits.
Percentage of requests that returned a 4xx or 5xx HTTP status code, timed out, or (in scenario mode) failed to complete the Playwright script. Anything above 0.1% under load is worth investigating.
Total bytes transferred from the server to LoadTester per second. Useful for detecting bandwidth bottlenecks.
Each Playwright step is timed individually. The PDF report shows a waterfall chart of time spent on: navigation, waiting for elements, form fills, and assertions. This pinpoints exactly which part of your flow is slow.
Every test produces a PDF report. Download it immediately when the test completes. We delete all data after the session ends — the PDF is your only record.
Integrate LoadTester into your CI/CD pipeline. All endpoints accept and return JSON. Authentication via X-LoadTester-Key header (API access keys are generated from your account dashboard).
https://api.theloadtester.com
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/create-scenario | Encrypt Playwright steps → .loadtest file |
| POST | /api/record-start | Start a browser recording session |
| POST | /api/record-stop/:sessionId | Stop recording, returns captured steps |
| POST | /api/simple-test | Start a simple HTTP load test |
| POST | /api/scenario-test | Start a scenario test |
| GET | /api/test/:id/stream | SSE stream of live metrics |
| GET | /api/test/:id/report | Download PDF report |
| POST | /api/verify-domain | Check domain verification file |
{
"url": "https://api.example.com/health",
"rps": 10000,
"duration": 600,
"regions": ["americas", "europe-de", "europe-uk", "asia"],
"testId": "lt_a3f9b2c1"
}
// Response:
{ "id": "lt_a3f9b2c1", "status": "running", "streamUrl": "/api/test/lt_a3f9b2c1/stream" }
# Each event data is JSON: { "rps": 9823, "p50": 48, "p95": 134, "p99": 289, "errRate": 0.003, "elapsed": 45, "duration": 600, "regions": { "americas": { "rps": 3950, "latency": 42 }, "europe-de": { "rps": 1980, "latency": 61 }, "europe-uk": { "rps": 2010, "latency": 67 }, "asia": { "rps": 1883, "latency": 95 } } }
curl -X POST https://api.theloadtester.com/api/verify-domain \
-H "Content-Type: application/json" \
-d '{"testId":"lt_a3f9b2c1","verifyUrl":"https://yoursite.com/.well-known/loadtester-verify/lt_a3f9b2c1"}'
curl -X POST https://api.theloadtester.com/api/simple-test \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/health",
"rps": 100,
"duration": 60,
"regions": ["americas"],
"testId": "lt_a3f9b2c1"
}'
curl -N https://api.theloadtester.com/api/test/lt_a3f9b2c1/stream
curl -o report.pdf https://api.theloadtester.com/api/test/lt_a3f9b2c1/report
# .github/workflows/load-test.yml name: Load Test on Deploy on: workflow_run: workflows: ["Deploy to Production"] types: [completed] jobs: load-test: runs-on: ubuntu-latest if: github.event.workflow_run.conclusion == 'success' steps: - name: Setup verification file run: | TEST_ID="lt_ci_$(date +%s)" echo "$TEST_ID" > /tmp/test_id # Deploy verification file to your server ssh user@yourserver.com \ "echo '$TEST_ID:verified:2026' > /var/www/.well-known/loadtester-verify/$TEST_ID" - name: Verify domain run: | TEST_ID=$(cat /tmp/test_id) curl -f -X POST https://api.theloadtester.com/api/verify-domain \ -H "Content-Type: application/json" \ -d "{\"testId\":\"$TEST_ID\",\"verifyUrl\":\"https://yoursite.com/.well-known/loadtester-verify/$TEST_ID\"}" - name: Start load test run: | TEST_ID=$(cat /tmp/test_id) curl -f -X POST https://api.theloadtester.com/api/simple-test \ -H "Content-Type: application/json" \ -d "{\"url\":\"https://yoursite.com/api/health\",\"rps\":100,\"duration\":60,\"regions\":[\"americas\"],\"testId\":\"$TEST_ID\"}" - name: Wait and download report run: | sleep 75 TEST_ID=$(cat /tmp/test_id) curl -o load-test-report.pdf \ https://api.theloadtester.com/api/test/$TEST_ID/report - name: Upload report artifact uses: actions/upload-artifact@v4 with: name: load-test-report path: load-test-report.pdf
# .gitlab-ci.yml — add after deploy stage load-test: stage: verify needs: [deploy] script: - export TEST_ID="lt_ci_$(date +%s)" # Serve verification file on your target - ssh user@server "echo '$TEST_ID:verified:2026' > /var/www/.well-known/loadtester-verify/$TEST_ID" # Verify ownership - | curl -f -X POST https://api.theloadtester.com/api/verify-domain \ -H "Content-Type: application/json" \ -d "{\"testId\":\"$TEST_ID\",\"verifyUrl\":\"https://yoursite.com/.well-known/loadtester-verify/$TEST_ID\"}" # Start test - | curl -f -X POST https://api.theloadtester.com/api/simple-test \ -H "Content-Type: application/json" \ -d "{\"url\":\"https://yoursite.com\",\"rps\":100,\"duration\":60,\"regions\":[\"americas\"],\"testId\":\"$TEST_ID\"}" - sleep 75 - curl -o report.pdf "https://api.theloadtester.com/api/test/$TEST_ID/report" artifacts: paths: - report.pdf expire_in: 7 days
page.goto(url, options) — Navigate to a URLpage.click(selector, options) — Click an elementpage.fill(selector, value) — Fill an inputpage.type(selector, text) — Type character by characterpage.selectOption(selector, value) — Select dropdown optionpage.waitForSelector(selector, options) — Wait for elementpage.waitForURL(url, options) — Wait for navigationpage.waitForTimeout(ms) — Pausepage.screenshot(options) — Capture screenshotpage.evaluate(fn) — Run JavaScript in page contextexpect(page).toHaveURL(url) — Assert URLexpect(locator).toHaveText(text) — Assert textwaitForSelector after navigation instead of fixed delaysscreenshot() calls at key steps — they appear in error reportswaitForURLsleep calls — use Playwright's built-in waits| ID | Location | Coverage |
|---|---|---|
americas | Eastern United States | North & South America |
europe-de | Frankfurt, Germany | Central Europe |
europe-uk | London, United Kingdom | Western Europe |
asia | Singapore | Asia-Pacific |
Free tier tests run from americas only. Paid tiers run all selected regions simultaneously, with traffic split evenly across regions.
| Tier | RPS | Duration | Regions | Price |
|---|---|---|---|---|
| Free | 100 | 1 min | 1 | $0 |
| Standard | 10,000 | 10 min | All 4 | $19.37 |
| Heavy | 100,000 | 60 min | All 4 | $97.53 |
| Tier | Virtual Users | Duration | Regions | Price |
|---|---|---|---|---|
| Free | 5 | 1 min | 1 | $0 |
| Standard | 100 | 10 min | All 4 | $47.21 |
| Heavy | 500 | 60 min | All 4 | $197.53 |
All payments in USDC or USDT on Demo L2 (chain ID 845302). Non-refundable. No subscriptions, no recurring charges.