Complete reference for the SwarmBrowser API. All endpoints return results within 30 seconds. Batch jobs are asynchronous.
Get a free key on the home page, then take your first screenshot:
# Screenshot any URL and save it
curl -X POST https://api.swarmbrowser.net/v1/screenshot \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}' \
--output screenshot.png
You'll get a PNG file back immediately. No setup, no SDK required.
Click a button to hit the real API. These run against a shared free-tier demo key (throttled).
Pass your key in the Authorization header or as a query parameter:
# Header (preferred)
Authorization: Bearer rpk_YOUR_KEY
# Query param (for webhooks and testing)
https://api.swarmbrowser.net/v1/screenshot?key=rpk_YOUR_KEY
Keys begin with rpk_ followed by 32 hex characters. Generate one on the home page — no account needed.
Renders a URL with a real Chromium browser and returns the screenshot as binary PNG or JPEG.
| Field | Type | Default | Description |
|---|---|---|---|
url | string | required | The URL to render. Must include protocol (https://). |
width | integer | 1280 | Viewport width in pixels. |
height | integer | 720 | Viewport height in pixels. |
format | string | png | png or jpeg |
quality | integer | 85 | JPEG quality 1–100. Ignored for PNG. |
fullPage | boolean | false | Capture full scrollable page height. |
region | string | montreal | Rendering node. See Regions. |
waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2 |
blockResources | string[] | [] | Resource types to block: image, stylesheet, font, script |
userAgent | string | Chromium default | Override the browser user agent string. |
timeout | integer | 30000 | Navigation timeout in milliseconds. |
Binary image with headers:
Content-Type: image/png
X-Load-Time: 1243 # ms to load the page
X-Location: frankfurt # region that rendered it
X-Resource-Count: 47 # number of resources loaded
curl -X POST https://api.swarmbrowser.net/v1/screenshot \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{"url":"https://news.ycombinator.com","width":1440,"height":900,"fullPage":true,"region":"singapore"}' \
--output hn-singapore.png
Renders a URL and returns a PDF. Useful for generating reports, invoices, and archiving pages.
| Field | Type | Default | Description |
|---|---|---|---|
url | string | required | URL to render. |
format | string | A4 | Paper size: A4, A3, Letter, Legal, Tabloid |
landscape | boolean | false | Landscape orientation. |
printBackground | boolean | true | Include background colors and images. |
margin | object | 1cm all | {"top":"1cm","bottom":"1cm","left":"1cm","right":"1cm"} |
region | string | montreal | Rendering node. |
waitUntil | string | networkidle2 | Navigation wait strategy. |
timeout | integer | 30000 | Timeout in ms. |
Content-Type: application/pdf — binary PDF with Content-Disposition: attachment; filename="page.pdf".
curl -X POST https://api.swarmbrowser.net/v1/pdf \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{"url":"https://example.com/invoice","format":"A4","landscape":false}' \
--output invoice.pdf
Renders a URL and extracts structured data. Works on JavaScript-heavy SPAs, React apps, and any dynamic page.
| Field | Type | Default | Description |
|---|---|---|---|
url | string | required | URL to render. |
extract | string[] | ["title","meta","text","headings"] | Built-in fields to extract. Options: title, meta, links, text, html, headings, images |
selectors | object | {} | CSS selectors. Key = field name, value = CSS selector string. Returns array if multiple matches. |
waitFor | string | — | CSS selector to wait for before extracting. |
region | string | montreal | Rendering node. |
waitUntil | string | networkidle2 | Navigation wait strategy. |
timeout | integer | 30000 | Timeout in ms. |
{
"url": "https://example.com",
"location": "frankfurt",
"metrics": {
"loadTime": 1243,
"domContentLoaded": 890,
"firstPaint": 342,
"resourceCount": 47,
"totalBytes": 1284032
},
"content": {
"title": "Example Domain",
"meta": { "description": "..." },
"headings": [{ "level": 1, "text": "Example Domain" }],
"text": "Example Domain\nThis domain is for use..."
},
"selectors": {
"price": "$29.99",
"stock": "In stock"
}
}
curl -X POST https://api.swarmbrowser.net/v1/scrape \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{
"url": "https://store.example.com/product/123",
"extract": ["title", "meta"],
"selectors": {
"price": ".product-price",
"rating": ".star-rating",
"stock": "[data-availability]"
},
"waitFor": ".product-price"
}'
Submit up to 10,000 URLs in a single call. Jobs are queued and processed in parallel across the swarm. Poll for completion or receive a webhook.
| Field | Type | Default | Description |
|---|---|---|---|
urls | string[] | required | Array of URLs. Max 10,000 on Business, 1,000 on Pro. |
action | string | scrape | screenshot, pdf, or scrape |
options | object | {} | Same options as the single-URL endpoint for the chosen action. |
webhook | string | — | URL to POST results to when complete. |
region | string | montreal | Rendering region. |
{
"batchId": "bch_a1b2c3d4e5f6",
"total": 5000,
"estimatedSeconds": 180,
"pollUrl": "https://api.swarmbrowser.net/v1/batch/bch_a1b2c3d4e5f6"
}
{
"batchId": "bch_a1b2c3d4e5f6",
"status": "processing", // "queued" | "processing" | "complete" | "failed"
"total": 5000,
"completed": 2341,
"failed": 12,
"results": [ ... ] // populated when status = "complete"
}
Renders the same URL from multiple regions simultaneously. Returns a screenshot and performance metrics per region. Ideal for spotting geo-blocks, CDN differences, and regional content changes.
| Field | Type | Default | Description |
|---|---|---|---|
url | string | required | URL to compare. |
regions | string[] | all regions | Subset of regions to compare. Omit for all 8. |
format | string | png | Screenshot format per region. |
width | integer | 1280 | Viewport width. |
height | integer | 720 | Viewport height. |
waitUntil | string | networkidle2 | Navigation wait strategy. |
{
"url": "https://example.com",
"regions": {
"montreal": {
"loadTime": 312,
"resourceCount": 47,
"screenshot": "base64..." // base64 PNG
},
"frankfurt": {
"loadTime": 287,
"resourceCount": 51,
"screenshot": "base64..."
},
"singapore": {
"loadTime": 891,
"error": "Navigation timeout" // per-region errors don't fail the call
}
}
}
curl -X POST https://api.swarmbrowser.net/v1/compare \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{"url":"https://yoursite.com","regions":["montreal","frankfurt","singapore","sydney"]}'
Like /v1/compare but also performs pixel-by-pixel comparison between regions and returns a diff image highlighting visual differences. Useful for detecting geo-blocked content, CDN staleness, and A/B test leaks.
Same as /v1/compare, plus:
| Field | Type | Default | Description |
|---|---|---|---|
baseline | string | first region | Region to use as the baseline for comparison. |
threshold | number | 0.1 | Pixel difference threshold (0–1). Lower = more sensitive. |
{
"url": "https://example.com",
"baseline": "montreal",
"regions": {
"frankfurt": {
"screenshot": "base64...",
"diff": "base64...", // highlighted diff image
"diffPixels": 1284, // number of different pixels
"diffPercent": 0.83, // percentage of screen changed
"loadTime": 287
}
}
}
Use the region parameter to select where rendering happens. For /v1/compare and /v1/diff, pass an array to regions.
| ID | Location | Continent |
|---|---|---|
montreal | Montreal, Canada | North America |
frankfurt | Frankfurt, Germany | Europe |
london | London, UK | Europe |
warsaw | Warsaw, Poland | Europe |
singapore | Singapore | Southeast Asia |
jakarta | Jakarta, Indonesia | Southeast Asia |
kualalumpur | Kuala Lumpur, Malaysia | Southeast Asia |
sydney | Sydney, Australia | Oceania |
montreal is available. All regions are available on Pro and Business.| Tier | Pages/day | Concurrent | Batch max | Regions |
|---|---|---|---|---|
| Free | 100 | 1 | — | montreal only |
| Pro | 10,000 | 10 | 1,000 URLs | All 8 |
| Business | 100,000 | 50 | 10,000 URLs | All 8 |
Overflow (Pro and Business): $0.50 per 1,000 extra pages. You are never blocked — you pay for what you use. Overflow is pulled automatically from your approved allowance.
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9873
X-RateLimit-Reset: 1713600000
| HTTP code | Error | Meaning |
|---|---|---|
| 400 | missing_url | url field is required. |
| 401 | invalid_key | Key not found or malformed. |
| 429 | rate_limit | Daily page limit reached. Upgrade or wait until midnight UTC. |
| 500 | render_failed | Browser error. Usually a bad URL or timeout. Check the error field. |
| 504 | timeout | Page took longer than timeout ms to load. |
For batch jobs, pass a webhook URL. We'll POST to it with Content-Type: application/json when the job is complete. The payload is identical to the poll response.
curl -X POST https://api.swarmbrowser.net/v1/batch \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{
"urls": ["https://a.com", "https://b.com"],
"action": "screenshot",
"webhook": "https://yourserver.com/webhooks/swarm"
}'
We retry the webhook 3 times with exponential backoff if your server returns a non-2xx response.
Capture Web3 pages and DeFi dashboards with injected wallet state. Pass injectWallet to inject a fake Ethereum provider with a configurable address and balance.
curl -X POST https://api.swarmbrowser.net/v1/screenshot \
-H "Authorization: Bearer rpk_YOUR_KEY" \
-d '{
"url": "https://app.uniswap.org",
"injectWallet": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"balance": "10.5",
"chainId": 1
},
"waitFor": ".token-amount-input",
"fullPage": false
}'
This injects window.ethereum with the specified wallet state before page load, so the dApp renders as if a wallet is connected.
const fs = require('fs');
async function screenshot(url, region = 'frankfurt') {
const res = await fetch('https://api.swarmbrowser.net/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SWARM_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url, region, fullPage: true }),
});
if (!res.ok) throw new Error(await res.text());
const buf = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('out.png', buf);
console.log(`Load time: ${res.headers.get('x-load-time')}ms`);
}
screenshot('https://example.com');
import requests, os
def screenshot(url, region='frankfurt'):
r = requests.post(
'https://api.swarmbrowser.net/v1/screenshot',
headers={'Authorization': f'Bearer {os.environ["SWARM_KEY"]}'},
json={'url': url, 'region': region, 'fullPage': True},
timeout=60
)
r.raise_for_status()
with open('out.png', 'wb') as f:
f.write(r.content)
print(f"Load time: {r.headers['x-load-time']}ms")
screenshot('https://example.com')
const res = await fetch('https://api.swarmbrowser.net/v1/compare', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SWARM_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://yoursite.com',
regions: ['montreal', 'frankfurt', 'singapore', 'sydney'],
}),
});
const data = await res.json();
for (const [region, result] of Object.entries(data.regions)) {
if (result.error) {
console.log(`${region}: ERROR — ${result.error}`);
} else {
console.log(`${region}: ${result.loadTime}ms, ${result.resourceCount} resources`);
}
}