API reference
Base URL: https://shotlane.dev. All endpoints accept GET with query parameters or POST with a JSON (or form) body. Successful responses return the raw file bytes with the matching Content-Type.
Authentication
Send your API key in any of these ways (header recommended for server-side code):
X-API-Key: sl_your_key
Authorization: Bearer sl_your_key
?access_key=sl_your_key # handy for <img src> embeds
Don't have a key? Get one free — 100 renders included.
GET|POST /v1/screenshot
Capture a web page or an HTML snippet. Costs 1 credit per successful render.
| Parameter | Default | Description |
|---|---|---|
url | — | Page to capture. https:// is assumed if omitted. Either url or html is required. |
html | — | Raw HTML to render instead of a URL (POST recommended, max 2 MB). |
format | png | png, jpeg, webp or pdf. |
width / height | 1280 / 800 | Viewport size in CSS pixels (240–3840 × 240–2160). |
full_page | false | Capture the entire scrollable page. |
scale | 1 | Device scale factor, 1–3. Use 2 for retina-quality images. |
selector | — | CSS selector; captures only the first matching element. |
dark_mode | false | Emulate prefers-color-scheme: dark. |
hide_cookie_banners | false | Hide common cookie consent popups (OneTrust, Cookiebot, Didomi, Quantcast, …). |
block_ads | false | Block requests to common ad & tracking networks. |
delay | 0 | Extra wait after load, in ms (0–10000). |
wait_until | load | load, domcontentloaded or networkidle. |
quality | 80 | JPEG/WebP quality, 1–100. |
user_agent | — | Override the browser user agent. |
download | false | Add a Content-Disposition: attachment header. |
device | — | Preset viewport + scale: desktop, desktop_hd, laptop, ipad, iphone, iphone_se, pixel, galaxy. Widths ≤ 480px automatically get a mobile user agent and touch emulation. |
omit_background | false | Transparent background for PNG (the page must not set its own background). |
css | — | Custom CSS injected before capture. |
js | — | JavaScript run in the page before capture (async/await allowed). Errors are returned as 422. |
hide_selectors | — | Comma-separated CSS selectors (or JSON array) to hide, e.g. .chat-widget,#promo. |
click | — | CSS selector to click before capture (e.g. open a tab or dismiss a modal). |
wait_for_selector | — | Wait (up to 10s) until this element is visible. |
headers | — | Extra HTTP headers as a JSON object, e.g. {"Authorization":"Bearer …"}. Sent only to the target host, never to third-party resources. |
cookies | — | Cookies for the target URL: session=abc; theme=dark. Useful for pages behind a login. |
cache_ttl | 0 (86400 for signed URLs) | Seconds to cache the result (max 30 days). Identical requests within the TTL are served from cache for free — no credit used. |
curl "https://shotlane.dev/v1/screenshot?url=example.com&width=1440&scale=2&format=jpeg" \ -H "X-API-Key: $SHOTLANE_KEY" -o example.jpg
GET|POST /v1/pdf
Same parameters as /v1/screenshot (format defaults to pdf), plus:
| Parameter | Default | Description |
|---|---|---|
paper | A4 | A3, A4, A5, Letter, Legal, Tabloid. |
landscape | false | Landscape orientation. |
print_background | true | Include background colors/images and use screen styles. Set false to use your @media print CSS. |
Tip: control margins and page breaks with CSS — @page { margin: 18mm } and break-before: page.
curl -X POST https://shotlane.dev/v1/pdf \
-H "X-API-Key: $SHOTLANE_KEY" -H "Content-Type: application/json" \
-d '{"html":"<h1>Hello PDF</h1>","paper":"Letter"}' -o hello.pdf
GET|POST /v1/extract · /v1/markdown
Load a page in the browser (JavaScript executed, so SPAs work) and get its content as clean Markdown — ready for LLMs, RAG pipelines, search indexing or archiving. 1 credit per page. /v1/extract returns JSON; /v1/markdown returns the Markdown as text/markdown.
| Parameter | Default | Description |
|---|---|---|
url / html | — | Page to load, or raw HTML. |
main_content | true | Extract just the main article/content (drops navigation, footers, ads). false converts the whole page. |
selector | — | Convert only the first element matching this CSS selector. |
include_links | true | Keep hyperlinks (made absolute). |
include_images | false | Keep images as . |
format | json | json or markdown. |
All page options above also work here: wait_until, wait_for_selector, delay, click, js, headers, cookies, block_ads, hide_cookie_banners, cache_ttl…
curl "https://shotlane.dev/v1/extract?url=https://en.wikipedia.org/wiki/Headless_browser" -H "X-API-Key: $SHOTLANE_KEY"
{
"url": "https://en.wikipedia.org/wiki/Headless_browser",
"title": "Headless browser - Wikipedia",
"description": "",
"author": null,
"published": "2014-09-24",
"markdown": "# Headless browser\n\nA **headless browser** is a [web browser](https://…) without a …",
"word_count": 1077
}
GET /v1/account
Returns your remaining credits: {"email": "...", "credits": 1234}. Free — doesn't consume a credit.
MCP server for AI agents
Let Claude, Cursor, Windsurf or any MCP client look at web pages. Shotlane runs a remote MCP server (Streamable HTTP) with four tools: screenshot (returns the image directly to the model), read_page (page → clean Markdown, JavaScript rendered), html_to_pdf and credits.
https://shotlane.dev/mcp
Clients that support OAuth (ChatGPT, Claude, Cursor, VS Code, Claude Code) just need that URL — they'll open a sign-in window. Everything else can send your API key as X-API-Key / Authorization: Bearer, or append ?key=sl_your_key. Step-by-step setup for each app: Integrations.
Claude Code:
claude mcp add --transport http shotlane https://shotlane.dev/mcp
Cursor (mcp.json):
{
"mcpServers": {
"shotlane": { "url": "https://shotlane.dev/mcp" }
}
}
Prefer headers? Send Authorization: Bearer sl_your_key instead of the key parameter. Each screenshot or PDF costs 1 credit, same as the REST API. Machine-readable docs: /llms.txt · /openapi.json
Response headers
X-Credits-Remaining | Credits left after this request. |
X-Render-Time-Ms | Server-side render time. |
Errors
Errors return JSON: {"error": "message"}. Failed renders are never charged.
400 | Invalid parameter, or the host is unreachable / not allowed (private networks are blocked). |
401 | Missing or invalid API key. |
402 | Out of credits — top up. |
413 | HTML payload too large. |
422 | The page could not be loaded, or the selector wasn't found. |
500 | Unexpected rendering error. Retry once; contact us if it persists. |
Signed URLs for <img> embeds
The response is the image itself, so a Shotlane URL can go straight into an <img src> — ideal for link previews, directories and dashboards. Never put your secret key in public HTML; use a signed URL instead. It carries your public key (pub_…, on your dashboard) and an HMAC signature, so nobody can change the parameters or reuse your account for other pages.
Signed requests are cached for 24 hours by default (override with cache_ttl), and cache hits are free — a popular page costs you one credit per day, not one per visitor.
How to sign: take every parameter except key and signature, sort by name, URL-encode as a query string, and compute HMAC-SHA256 with your secret API key. Append key and signature (hex).
# Python import hmac, hashlib from urllib.parse import urlencode def shotlane_url(params, public_key, secret_key): qs = urlencode(sorted((k, str(v)) for k, v in params.items())) sig = hmac.new(secret_key.encode(), qs.encode(), hashlib.sha256).hexdigest() return f"https://shotlane.dev/v1/screenshot?{qs}&key={public_key}&signature={sig}" shotlane_url({"url": "https://example.com", "format": "webp"}, "pub_…", "sl_…")
// Node.js import crypto from "node:crypto"; function shotlaneUrl(params, publicKey, secretKey) { const qs = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)]) .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))).toString(); const sig = crypto.createHmac("sha256", secretKey).update(qs).digest("hex"); return `https://shotlane.dev/v1/screenshot?${qs}&key=${publicKey}&signature=${sig}`; }
Note: Python's urlencode encodes spaces as +, like URLSearchParams — both produce identical signatures.