Skip to content
flatbrowser

docs · rest api

REST API

Base URL https://gw.flatbrowser.com. Every endpoint except /healthz needs your API key (Authorization: Bearer …, or ?token=). Bodies are JSON, at most 256 KB on the native /v1 routes. Errors use one envelope, described in errors & limits.

Endpoints

endpointreturns
POST /v1/sessionsa session you attach to over WebSocket
GET /v1/sessionsyour active sessions
DELETE /v1/sessions/:idends a session
POST /v1/screenshotimage/png
POST /v1/pdfapplication/pdf
POST /v1/contentJSON {"html": …}
POST /v1/scrapeJSON {"data": […]}
GET /json/versionthe CDP discovery document
GET /healthzgateway health (no key needed)

Each screenshot, PDF, content or scrape call starts its own short session in one of your workers, so it uses one concurrency slot while it runs and frees it when it returns. Nothing it produces is stored: the image, PDF or HTML is streamed back in the response and gone from our side once sent.

Sessions

POST /v1/sessions

Creates a session that outlives connections (see sessions). All body fields are optional; unknown fields are refused.

fieldtypemeaning
idleTimeoutMsintegerend after this long with no client attached (default 300000)
maxDurationMsintegertotal lifetime cap (default and maximum 43200000 = 12 h)
proxyUrlstringyour upstream proxy — see proxies
localestringBCP-47 browser locale, e.g. de-DE (navigator.language, Accept-Language)
timezonestringIANA timezone, e.g. Europe/Berlin
argsstring[]extra Chromium flags from the allowlist (max 32)

The response’s connectUrl deliberately has no key in it; append ?token=<your key> when you connect.

sessions.sh
curl -X POST https://gw.flatbrowser.com/v1/sessions -H 'Authorization: Bearer flat_live_YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"idleTimeoutMs":300000,"proxyUrl":"http://user:pass@proxy.example.net:8000","locale":"de-DE","timezone":"Europe/Berlin"}'
# → 201 {"id":"3f0c…","connectUrl":"wss://gw.flatbrowser.com/session/3f0c…","wsPath":"/session/3f0c…"}

curl https://gw.flatbrowser.com/v1/sessions -H 'Authorization: Bearer flat_live_YOUR_KEY'
# → {"sessions":[{"id":"3f0c…","status":"active","source":"rest","workerId":"…","startedAt":"…"}]}

curl -X DELETE https://gw.flatbrowser.com/v1/sessions/3f0c… -H 'Authorization: Bearer flat_live_YOUR_KEY'
# → {"ok":true}

POST /v1/screenshot

  • url (required): a public http(s) URL
  • fullPage: capture the whole scrollable page (default false)
  • width / height: viewport in CSS pixels (default 1280 × 720, max 7680 × 4320)
  • waitUntil: load (default), domcontentloaded, networkidle or commit
screenshot.sh
curl -X POST https://gw.flatbrowser.com/v1/screenshot \
  -H 'Authorization: Bearer flat_live_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","fullPage":true,"width":1440,"height":900}' \
  -o page.png

POST /v1/pdf

  • url (required)
  • format: Letter, Legal, Tabloid, Ledger or A0–A6 (default A4)
  • waitUntil: load (default), domcontentloaded, networkidle or commit
pdf.sh
curl -X POST https://gw.flatbrowser.com/v1/pdf \
  -H 'Authorization: Bearer flat_live_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","format":"A4","waitUntil":"networkidle"}' \
  -o page.pdf

POST /v1/content

The rendered page’s HTML after waitUntil, as JSON. The Browserless-style alias POST /content returns it as text/html instead.

content.sh
curl -X POST https://gw.flatbrowser.com/v1/content \
  -H 'Authorization: Bearer flat_live_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com"}'
# → {"html":"<!DOCTYPE html><html>…"}

POST /v1/scrape

Text and inner HTML of every element matching each selector you send (up to 32 selectors, 2,000 matches each). A selector’s optional timeout (max 30000 ms) waits for it to appear; a miss is an empty result, not an error. An invalid CSS selector is a 400. You choose the selectors — we ship no site-specific scrapers.

scrape.sh
curl -X POST https://gw.flatbrowser.com/v1/scrape \
  -H 'Authorization: Bearer flat_live_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","elements":[{"selector":"h1"},{"selector":"a","timeout":2000}]}'
# → {"data":[{"selector":"h1","results":[{"text":"Example Domain","html":"Example Domain"}]}, …]}

Query parameters on session-creating calls

POST /v1/sessions and the four actions also read the same query parameters as the WebSocket URL: proxy, timeout, queueTimeout, locale, timezone, launch and --chromium-flag parameters. On an action, timeout is the guard for the whole call (default 60 s, max 180 s). Unknown parameters are refused with a 400; parameters we accept but do not act on are listed, by name only, in the X-Flatbrowser-Ignored-Params response header. The full list is on the compatibility page.

Browserless-style aliases

POST /screenshot, /pdf, /content and /scrape (also under /chromium/ and /chrome/) accept Browserless-shaped bodies — for example html instead of url, gotoOptions, options, viewport and waitForSelector — and run on the same implementation as /v1. What is and is not supported there is listed in the migration guide.

Which URLs you can open

  • Only public http(s) hosts. Private, loopback and cloud-metadata addresses are refused, and so are hostnames that resolve to them.
  • Hostnames on our abuse blocklist are refused with 403 target_blocked before a slot is used. Inside CDP sessions the same hostnames do not resolve (net::ERR_NAME_NOT_RESOLVED).
  • A navigation that fails on the target side (DNS, TLS, connection refused) is a 424 with the browser’s error in the message.