Skip to content
flatbrowser

docs · errors & limits

Errors and limits

Every error from the gateway and the REST API has the same JSON shape, a stable machine-readable code and a message written for the person debugging — when a parameter is the problem, the message starts with its name.

The envelope

response
HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json

{"error":{"code":"concurrency_limit_reached","message":"plan allows 2 concurrent sessions (2 active)","retryAfter":2}}

Every 429 and 503 carries a Retry-After header in seconds, repeated as error.retryAfter in the body: Playwright's connectOverCDP error shows the body of a refused connection but not its headers. Branch on code, not on the message text.

Codes

statuscodewhat to do
400invalid_requestA value is malformed (bad proxy URL, invalid selector, body not JSON). The message names the field.
400unsupported_paramAn unknown parameter, or a Browserless feature we do not offer (profiles, stealth, recording, headful). See the compatibility table.
400residential_proxy_not_availableNo built-in proxy pool: pass your own with proxy=. See proxies.
400playwright_protocol_not_supportedUse chromium.connectOverCDP(), not chromium.connect().
401unauthorizedThe key is missing, wrong or revoked.
403no_active_subscriptionThe account has no active plan, or a failed payment is past its grace period.
403org_suspendedThe account is suspended; the email with the statement of reasons explains why and how to contest it.
403forbiddenThe target is a private, loopback or metadata address.
403target_blockedThe target hostname is on our abuse blocklist.
404not_foundUnknown path. WebSocket 404s list the supported paths.
404session_not_foundNo active session with that ID in your account (it may have ended).
408action_timeoutA REST action ran out of time. Raise timeout (max 180000).
413invalid_requestThe request body is too large.
424navigation_failedThe target site failed (DNS, TLS, refused). The browser’s error is in the message.
429concurrency_limit_reachedAll your sessions are in use. Retry after Retry-After, pass queueTimeout, or upgrade.
429rate_limitedToo many requests in a short window. Retry after Retry-After.
503no_worker_availableYour workers are starting or being replaced. Retry after Retry-After.
503worker_unreachableA worker did not answer. Retry; tell us if it persists.
503gateway_drainingThe gateway is restarting for a deploy. Retry after Retry-After.
5xxinternalOur fault. Retry with backoff; tell us if it persists.

WebSocket specifics

Before the upgrade, errors are ordinary HTTP responses with the envelope above — Puppeteer and Playwright surface them as Unexpected server response: 429. After the upgrade, a failure closes the socket with a code and a short reason:

close codemeaning
1000Normal close: the session ended (your close, DELETE, idle timeout or maximum duration).
1001The gateway is restarting. A keepalive or REST-created session keeps running: the reason names the /session/<id> path to re-attach to.
1008Policy: the API key was revoked, the account was suspended or closed, or the plan is no longer active.
1009A message from your client was larger than 64 MB.
1011Unexpected error on our side.
1013Try again later: the browser could not be reached.

Retrying

retry.mjs
// connect with a bounded retry that honours Retry-After
async function connectWithRetry(url, attempts = 5) {
  for (let i = 0; ; i++) {
    try {
      return await puppeteer.connect({ browserWSEndpoint: url });
    } catch (err) {
      // puppeteer/ws report the handshake status in the message, e.g.
      // "Unexpected server response: 429"
      const status = Number(/response: (\d{3})/.exec(String(err))?.[1]);
      if (i >= attempts || ![429, 503].includes(status)) throw err;
      await new Promise((r) => setTimeout(r, 2 ** i * 1000));
    }
  }
}
// or let the gateway wait for you: append &queueTimeout=15000 to the URL

Limits

limitvalue
concurrent sessionsSolo 2 · Team 6 · Scale 20
session duration12 h maximum (default and cap)
idle timeout5 min default for REST-created sessions
REST action60 s default, 180 s maximum (timeout=)
queueTimeout60 s maximum
request body256 KB on /v1, 5 MB on the Browserless aliases
scrape32 selectors, 2,000 matches each, 30 s wait per selector
screenshot viewport7680 × 4320 px
launch JSON4,096 characters, 32 Chromium flags
proxy URL2,048 characters
CDP message64 MB client → browser, 256 MB browser → client

Fair use

Hours and pages are not metered. The Terms add a fair-use rule on sustained traffic volume far outside normal automation patterns, applied only after notice to you — and never to exporting your data or switching provider.