---
name: cdpfleet
description: Real cloud browsers (Chrome, Edge, Brave, Opera, Firefox, Camoufox, WebKit, stealth Chromium builds) that you launch over HTTP and drive with Playwright, Puppeteer or raw CDP, plus Functions that return a page's screenshot, Markdown or extracted data in one HTTP call. Use when a task needs a real web browser, a rendered page from a given country, a specific browser or version, a proxy or residential IP, many pages or browsers in parallel, or a page checked for changes.
---

# cdpfleet — skill for AI agents

## Try it in one minute

**Why cdpfleet:** 12 real browsers (Chrome, Edge, Brave, Opera, Whale, Yandex, Chromium, Patchright,
Rebrowser, Camoufox, Firefox, WebKit) in the cloud, with beta/dev channels and older versions;
your own proxies, pools and per-host rules, or residential IPs by country from $0.50/GB; live view,
video recordings and persistent profiles; and dated measurements of what gets through Cloudflare,
DataDome, Akamai and HUMAN with each browser (https://cdpfleet.com/bot-protection).

**1. Get an API key** at https://cdpfleet.com/app (sign up free; every plan includes a free shared proxy, used when you
leave `proxy` out, so no proxy of your own is needed to start; what a paid plan adds: https://cdpfleet.com/upgrade).

**2a. From an AI assistant (MCP):** add the hosted server and sign in with OAuth:

```bash
claude mcp add --transport http cdpfleet https://cdpfleet.com/mcp
```

Or in any MCP client's config: `{ "mcpServers": { "cdpfleet": { "url": "https://cdpfleet.com/mcp" } } }`.
Local alternative: `npx -y cdpfleet-mcp` with `CDPFLEET_API_KEY` set. Tools: launch_browser, navigate,
snapshot, click, type, press, wait_for, screenshot, evaluate, live_view, close_browser, downloads,
list_browsers, account. Example prompts and the tool calls they make: https://cdpfleet.com/docs/mcp/prompts

**2b. From code (Playwright, any language):**

```js
const res = await fetch('https://starter.cdpfleet.com/chrome/session', {
  method: 'POST', headers: { 'x-api-key': process.env.CDPFLEET_API_KEY, 'content-type': 'application/json' },
  body: JSON.stringify({ headless: 'new' }),
});
const { wsUrl } = await res.json();
const browser = await chromium.connect(wsUrl, { headers: { 'x-api-key': process.env.CDPFLEET_API_KEY } });
```

Or with the SDK (npm/PyPI `cdpfleet`, Go module; it retries when the fleet is busy):
`const session = await new Cdpfleet().launch('chrome', { headless: 'new' }); const browser = await session.connect(chromium);`
— all five languages, runnable as they are: https://cdpfleet.com/docs/quickstart

**2c. No browser to drive (Functions):** one HTTP call returns a page as Markdown, a screenshot, extracted
fields or the same page from several countries:

```bash
curl -s https://cdpfleet.com/v1/functions/markdown -H "x-api-key: $CDPFLEET_API_KEY" \
  -H "content-type: application/json" -d '{"url":"https://books.toscrape.com/"}'
```

Also `screenshot`, `pdf`, `scrape` (with `extract`), `map`, `geo` and `livetab`; on paid plans, batch and crawl
jobs over any number of URLs and monitors that send a diff when a page changes: https://cdpfleet.com/docs/functions

Every page of this site is also available as Markdown: add `.md` to its URL, or send
`Accept: text/markdown`. Everything at once: https://cdpfleet.com/llms-full.txt


cdpfleet runs real browsers on its servers. You launch one with an HTTP request and drive it
with Playwright (any language), Puppeteer or CDP as if it were local. This page is written for
AI agents; humans have the docs at https://cdpfleet.com/docs.

## Rules for agents

1. **The API key is in `CDPFLEET_API_KEY`.** Send it as the `x-api-key` header. Never print it,
   log it, commit it or put it in a URL. If it's missing, ask the user to create one at
   https://cdpfleet.com/app → API keys.
2. **Check the key before anything else**, without launching a browser:
   `curl -s https://cdpfleet.com/v1/me -H "x-api-key: $CDPFLEET_API_KEY"` → 200 with the account's plan
   (`subscription`), or 401 for a bad key. `GET /v1/me/usage` shows today's usage.
3. **A launch uses the account's threads and minutes.** Launch only when the task needs a browser
   (for a page's Markdown, screenshot or a few fields, call a Function: next section),
   and always close it when done — `browser.close()` ends the session and stops the clock.
   Jobs and monitors keep using them until they end: stop jobs and delete monitors you no longer need.
4. **Proxies:** without one the free Community proxy is used (shared, slower, capped). Use the user's own
   proxy or a cdpfleet token they agreed to pay for when the task matters; don't invent one.
5. No browser of your own is needed: install the Playwright *client* only.

## Functions: a page in one call, no browser to drive

When the task needs a page's *result* (its Markdown, a screenshot, some fields, the same page from several
countries), call a Function: one request, a fresh tab in a real browser on our side, closed for you. Drive a
browser (below) for multi-step flows, a session you keep, or a specific browser. All plans; a call holds one
thread while it runs (`geo`: one per country), billed per second like a session.

`POST https://cdpfleet.com/v1/functions/<name>` with `x-api-key` and a JSON body. Every function takes `url` (public http/https),
`country` (two letters, default `"random"`), `max_wait` (1–60 s, default 20) and `proxy` (default: the Community
proxy in `country`). Errors are the launch codes plus `408 page_timeout` and `503 country_unavailable`; a page
still loading at `max_wait` comes back 200 with `x-cdpfleet-partial: load_timeout`.

```bash
fn() { curl -s https://cdpfleet.com/v1/functions/$1 -H "x-api-key: $CDPFLEET_API_KEY" -H "content-type: application/json" -d "$2"; }
fn screenshot '{"url":"https://books.toscrape.com/","full_page":true}' > shot.png   # or "format":"jpeg"
fn markdown '{"url":"https://books.toscrape.com/","country":"us"}'                  # text/markdown
fn scrape '{"url":"https://books.toscrape.com/","formats":[],"extract":{"title":"h1","prices":{"selector":".price_color","all":true}}}'
# {"url":"https://books.toscrape.com/","status":200,"redirects":[],"extract":{"title":"All products","prices":["£51.77","£53.74",…]}}
fn geo '{"url":"https://example.com/","countries":["us","de"],"formats":["markdown"]}'
# {"results":{"us":{"status":200,"url":"https://example.com/","markdown":"…"},"de":{…}},"diff":{"markdown":{"de":""}}}
fn livetab '{"url":"https://example.com/","max_duration":600}'
# {"livetab_id":"…","live_url":"https://cdpfleet.com/live/…#t=…","ws_url":"wss://…","token":"…","expires_at":"…"}
```

- `scrape`: `formats` any of `html` (default), `markdown`, `text`, `links`, `screenshot` (base64), `requests`, `cookies`,
  `console`; `[]` with `extract` returns only the fields. `wait_for` (selector or ms), `actions` (≤ 20 steps:
  `{"click": sel}`, `{"type": [sel, text]}`, `{"press": "Enter"}`, `{"scroll": "bottom"}`…), `selector`. Check `status` too:
  an error page has an `h1` as well.
- `geo`: 2–5 countries, the first is the baseline; `diff` per text format vs the first: `""` = identical, `null` = that
  country (or the first) failed, see its `error`. Each `url` is where that country ended up.
- `livetab`: one tab a person controls at `live_url` (sign in by hand, solve their own CAPTCHA). Anyone with the
  link controls the tab: give it to your user only. `GET https://cdpfleet.com/v1/functions/livetab/{livetab_id}` → `status`, and after
  it closes `final_url` and `cookies` for 10 min; `DELETE` closes it.
- Also `pdf` and `map` (`{"url", "max_urls"}` → a site's `urls` from sitemaps and links, ≤ 5,000). Paid plans: add
  `"browser": "chrome"` (any key below) and `launch` to run in a browser of your choice. Reference: https://cdpfleet.com/docs/functions

## Jobs and monitors (paid plans)

Community gets `403 jobs_not_available`. A **job** works through any number of pages in the background on
`concurrency` threads (default 2, up to the plan's), billed while it runs; no time limit of its own.

```bash
fn batch '{"urls":["https://books.toscrape.com/","https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"],"formats":[],"extract":{"title":"h1"}}'
# 202 {"job_id":"…","concurrency":2,"urls_count":2,"status_url":"https://cdpfleet.com/v1/jobs/…"} → JOB=<job_id>
curl -s https://cdpfleet.com/v1/jobs/$JOB -H "x-api-key: $CDPFLEET_API_KEY"               # status: running → done|failed|stopped|interrupted; progress
curl -s https://cdpfleet.com/v1/jobs/$JOB/results.jsonl -H "x-api-key: $CDPFLEET_API_KEY"  # one line per page
# {"url":"https://books.toscrape.com/","status":200,"extract":{"title":"All products"}}
curl -s -X DELETE https://cdpfleet.com/v1/jobs/$JOB -H "x-api-key: $CDPFLEET_API_KEY"      # stop (results kept); POST …/resume continues
```

- Same page fields as `scrape` (`formats`, `extract`, `actions`…). A line with `error` didn't load: send those URLs
  again as a new batch. Long lists: `"urls_url"` (a text file, one URL per line). Results are kept 14 days.
- `crawl` `{"url", "max_depth": 1, "max_pages": 100, "include"/"exclude": ["/catalogue/**"], …}` follows links;
  `map` with `"job": true` lists a whole site; `watch` streams a page's XHR/WebSocket/DOM changes; `warmup` browses
  like a person into a persistent profile. Live progress: `GET /v1/jobs/{id}/events` (SSE). https://cdpfleet.com/docs/jobs
- `"deliver": {"webhook": {"url": "https://…", "events": ["done"]}, "email": true}`: `events` from `page` (each page),
  `done` (default; also on a stop, with `"status": "stopped"`), `event` (watch) and `archive` (with `"format": "archive"`, the zip's `url`).

A **monitor** loads a page on a schedule and reports what changed:

```bash
curl -s https://cdpfleet.com/v1/monitors -H "x-api-key: $CDPFLEET_API_KEY" -H "content-type: application/json" \
  -d '{"url":"https://books.toscrape.com/","every":"1d","selector":".price_color","deliver":{"webhook":{"url":"https://example.com/hooks/cdpfleet"}}}'
# 201 {"monitor_id":"…","every":"1d","active":true,"next_run_at":"…",…} → MON=<monitor_id>
curl -s -X DELETE https://cdpfleet.com/v1/monitors/$MON -H "x-api-key: $CDPFLEET_API_KEY"  # {"deleted":true}
```

- `every` (`"15m"`, `"1h"`, `"1d"`, `"1w"`; at least 15 min on shared plans, 5 on dedicated) or `cron` (5 fields, UTC);
  `format` `markdown`|`text`|`html`, `selector`, `remove`, `ignore` (regexes). The first check is the baseline; then
  a `monitor.change` webhook `data: {monitor_id, change_id, url, added, removed, diff, snapshot_url}`, and
  `monitor.error` after three failed checks. `GET /v1/monitors/{id}/changes`; `PATCH {"active": false}` pauses. https://cdpfleet.com/docs/monitors

**Webhooks** (jobs and monitors): POST JSON `{type, created_at, data}` to a public https URL, with `x-cdpfleet-event`,
`x-cdpfleet-delivery` (the same id on retries: dedupe on it) and `x-cdpfleet-signature: t=<unix>,v1=<hex>`.
Accept only if `v1` = hex HMAC-SHA256 of `"<t>.<raw body>"` keyed with the whole `whsec_…` string from
`GET /v1/me/webhook-secret` (a secret: never print it), and `t` is within 5 min. Answer 2xx; failures are
retried after 10 s, 1 min, 5 min, 30 min and 2 h. Verification code in five languages: https://cdpfleet.com/docs/webhooks

## Prefer MCP if your app supports it

The hosted MCP server needs no install and no key in the conversation: add
`https://cdpfleet.com/mcp` as a remote MCP server (Claude, ChatGPT, Gemini and others sign in with OAuth),
or send `Authorization: Bearer <API key>` from scripts. Local alternative: `npx cdpfleet-mcp`.
Setup per app: https://cdpfleet.com/docs/mcp/setup. The rest of this page is for driving browsers from code.

## Launch → connect → close

1. **Launch:** `POST https://starter.cdpfleet.com/<browser>/session` with `x-api-key` and a JSON body of launch
   options. Minimal body: `{"proxy": "<proxy>", "headless": "new"}` (Firefox, Camoufox and WebKit
   take `"headless": true`). Response:
   `{ "sessionId", "wsUrl", "browser", "resource_class", "weight", "expires_at" }`.
2. **Connect within 60 s:** Playwright `<family>.connect(wsUrl, { headers: { "x-api-key": KEY } })`,
   where family is `chromium`, `firefox` or `webkit` (table below). The Playwright client must be
   the version in the table (1.60.x; Rebrowser 1.52.x). `wsUrl` is single-use: when you disconnect,
   the session ends.
3. **Use it** like a local browser: `newContext()`, `newPage()`, `goto`, locators, screenshots.
4. **Close:** `await browser.close()`. A session with no traffic for 120 s ends by itself
   (set `inactivity_timeout` up to 10 min on shared plans, 5 on Community, 24 h on dedicated — idle minutes count),
   so launch again if the connection is gone. End any of the account's sessions with `DELETE https://cdpfleet.com/v1/me/threads/{sessionId}`.

**One step instead (connect URL):** connecting to `wss://router01.cdpfleet.com/connect/<browser>?headless=new` (header
`x-api-key`) launches and connects at once; `/connect/<browser>/cdp?…` for connectOverCDP/Puppeteer. Options as query
parameters, or the JSON body base64url-encoded in `launch=`. It waits up to 60 s when busy. Details: https://cdpfleet.com/docs/connect.

Node.js (`npm install playwright@1.60.0`):

```js
// npm install playwright@1.60.0
// Browsers run on cdpfleet, so no `npx playwright install` is needed.
import { chromium } from 'playwright';

const KEY = process.env.CDPFLEET_API_KEY;

// 1. Launch the browser
const res = await fetch('https://starter.cdpfleet.com/chromium/session', {
  method: 'POST',
  headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
  body: JSON.stringify({
    proxy: 'http://user:pass@proxy.example.com:8080',
    headless: 'new',
  }),
});
if (!res.ok) throw new Error(`launch failed: ${res.status} ${await res.text()}`);
const { wsUrl } = await res.json();

// 2. Connect and drive it
const browser = await chromium.connect(wsUrl, { headers: { 'x-api-key': KEY } });
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close(); // ends the session and stops billing
```

Python (`pip install playwright==1.60.0 requests`):

```python
# pip install playwright==1.60.0 requests
# Browsers run on cdpfleet, so no `playwright install` is needed.
import os
import requests
from playwright.sync_api import sync_playwright

KEY = os.environ["CDPFLEET_API_KEY"]

# 1. Launch the browser
res = requests.post(
    "https://starter.cdpfleet.com/chromium/session",
    headers={"x-api-key": KEY},
    json={
        "proxy": "http://user:pass@proxy.example.com:8080",
        "headless": "new",
    },
    timeout=60,
)
res.raise_for_status()
ws_url = res.json()["wsUrl"]

# 2. Connect and drive it
with sync_playwright() as p:
    browser = p.chromium.connect(ws_url, headers={"x-api-key": KEY})
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()  # ends the session and stops billing
```

The code builder (https://cdpfleet.com/docs/builder) writes the same program in Java, C# and Go, and for any
browser and options. SDKs that wrap launch + connect: npm `cdpfleet`, PyPI `cdpfleet`,
Go `github.com/cdpfleet/cdpfleet/sdk/go` (https://cdpfleet.com/docs/sdk).

## Browsers

Use the key in the launch URL (`https://starter.cdpfleet.com/<key>/session`). Pick a channel with `"channel"`
(e.g. `"beta"`) or an older major, where listed, with `"version": "<major>"`.

| key | browser | Playwright family | default version | channels; older majors | client |
|---|---|---|---|---|---|
| `chrome` | Google Chrome | chromium | 157.0.8103.78 | stable, beta, dev; version `156`, `155` | 1.60 |
| `edge` | Microsoft Edge | chromium | 157.0.4341.57 | stable, beta, dev | 1.60 |
| `brave` | Brave | chromium | 1.99.57 | stable, beta | 1.60 |
| `opera` | Opera | chromium | 140.0.6074.18 | stable, developer | 1.60 |
| `whale` | Naver Whale | chromium | 5.41.429.5 | stable | 1.60 |
| `yandex` | Yandex Browser | chromium | 26.10.0.1882 | stable | 1.60 |
| `chromium` | Chromium | chromium | 148.0.7778.96 | — | 1.60 |
| `patchright` | Patchright | chromium | 1.60.2 | — | 1.60 |
| `rebrowser` | Rebrowser | chromium | 1.52.0 | — | 1.52 |
| `camoufox` | Camoufox | firefox | 156.0.1-beta.34 | stable; version `152`, `150` | 1.60 |
| `firefox` | Firefox | firefox | 150.0.2 | — | 1.60 |
| `webkit` | WebKit | webkit | 26.4 | — | 1.60 |

- Want genuine Chrome for a site → `chrome`. Firefox with a generated, consistent persona → `camoufox` (options `os`, `locale`, `geoip`, `fonts`…). Patched Chromium → `patchright`.
- Raw CDP / Puppeteer: add `"cdp": true` on `chrome`, `edge`, `brave`, `opera`, `yandex`, `chromium` → the response
  also has `cdpUrl` (Playwright `connectOverCDP`, any client version, or `puppeteer.connect`).
- Per-browser options and notes: https://cdpfleet.com/docs/browsers/<key>.

What options are for:
- `proxy` — Every session that needs a particular exit: your own proxy for the IP and country you choose, a list as a pool to spread requests across IPs, or a `cdpfleet-resi` token for residential IPs billed per GB.
- `proxy_rules` — Scrapers that want an expensive exit only where it matters: residential for the target site, a cheaper proxy for images, CDNs and APIs, or a different country per host.
- `proxy_updatable` — Long sessions that must change IP without losing state: rotate after a block, switch country mid-flow, or move a logged-in session to a fresh exit.
- `inactivity_timeout` — Freeing the thread quickly when a script dies (short), or giving a person or a slow step time to think (long), up to your plan's cap.
- `overall_timeout` — A hard time budget per job, so a stuck loop can't hold a thread for hours: batch scrapers and agents with a deadline.
- `memory` — Memory-heavy work that would otherwise end with `session_memory_limit`: many tabs at once, big single-page apps, long infinite scrolls, or large PDFs and screenshots.
- `headless` — Bulk scraping and tests where nobody watches: lower memory and faster launches. Leave it headful (the default) when a site checks for headless tells, or when you want a recording or live view without extra options.
- `headless` — Bulk Firefox jobs where nobody watches. Headful (the default) is closer to a real desktop and is needed for live view and recordings on Firefox.
- `headless` — Bulk Camoufox jobs where nobody watches; `"virtual"` keeps headful behaviour on a virtual display for sites that notice true headless.
- `channel` — QA teams checking a site against the next browser release before users get it (beta, dev), and scrapers that want to see a change coming.
- `version` — Reproducing a bug on an older browser, keeping a scraper on a known-good build while you test the new one, or matching a customer's browser.
- `os` — Matching the persona to who a site expects (a Windows desktop for most consumer sites, macOS for design or creative tools), and keeping one account on the same OS every time.
- `locale` — Localisation checks and geo work: the site sees the language you choose in `navigator.languages`, Accept-Language and number and date formats. Leave it unset to follow the proxy's country.
- `geoip` — Any session behind a proxy in another country: timezone, locale and geolocation follow the exit IP, so the persona doesn't contradict its own address.
- `humanize` — Pages that score mouse behaviour (forms, checkouts, sign-ups). Turn it off for bulk scraping, where instant moves are faster.
- `block_images` — Bulk text scraping where images are dead weight: fewer requests, less traffic (and less residential spend), faster pages. Measured in the camoufox-block-images recipe: 30 requests down to 10 on a catalogue page.
- `block_webrtc` — Sessions behind a proxy that must not show any WebRTC address at all. With `vpn_tunnel` you get the exit IP instead, which looks more like a home connection.
- `block_webgl` — Pages where any WebGL fingerprint is a risk and graphics aren't needed. A missing WebGL is itself unusual, so prefer `webgl_config` when the site expects a GPU.
- `disable_coop` — Scrapers and agents that must click inside a cross-origin iframe — a CAPTCHA widget, an embedded login or payment frame — on Camoufox. Leave it off otherwise: a site can detect it.
- `screen` — Keeping generated personas within a screen range you expect, for example common laptop sizes, without writing a whole fingerprint.
- `window` — Layout-sensitive scraping and screenshots that need the same window size on every launch.
- `fingerprint` — Replaying one exact device you already have, such as a persona captured from a real browser, instead of a generated one.
- `config` — Changing one or two properties of a generated persona, such as the core count, while Camoufox keeps everything else consistent.
- `fonts` — Personas for audiences whose machines have extra fonts (design tools, office suites, CJK fonts), so the font list looks lived-in.
- `custom_fonts_only` — Tests of how a site reacts to a minimal or specific font list; rarely what a normal persona needs.
- `webgl_config` — Matching the GPU to the persona when a site checks the WebGL renderer, for example an Intel laptop GPU for a Windows laptop persona.
- `ff_version` — Sites that only support certain Firefox versions, or reproducing what an older Firefox user sees.
- `screencast` — Watching or recording a headless page from your own code as a stream of JPEG frames, for debugging dashboards or human review, without a full live view.
- `keep_alive` — Agents and step-by-step workflows that disconnect between steps, and code that may lose its connection, without losing the logged-in browser.
- `profile` — Staying logged in between sessions: log in once, then every later launch starts with the same cookies and storage (and, on Camoufox, the same device).
- `record` — Keeping a video of each run for debugging, QA evidence or audits. Set `false` when you don't need it, to keep the recording quota for runs that matter.
- `downloads` — Downloads you can't catch in code: a person clicking in live view or Launch interactive, an AI agent working through the MCP server, a script that crashed or disconnected (`keep_alive`), or reports and exports you want to collect later.
- `live_view` — A person watching or taking over the session in the browser (a login, a 2FA step, a check that needs a human), or recording a headless session.
- `start_url` — A person logs in or passes a check by hand in live view first; then your code connects and carries on in the same page.
- `screen_size` — Responsive and visual tests at a given resolution, and personas that need a common desktop or laptop screen instead of the server default.
- `cdp` — Puppeteer users and tools that speak raw CDP (Lighthouse, custom DevTools clients), or connecting with Playwright's connectOverCDP to reach Chromium-only features like network throttling and performance traces.
- `vpn_tunnel` — Work that needs WebRTC or HTTP/3 to look like a home connection, such as video calls or sites that compare the WebRTC address with the page's IP, on a sticky residential exit.
- `vpn` — Dedicated-plan users who already run their own VPN (WireGuard, AmneziaWG, OpenVPN, sing-box): the whole browser, UDP included, leaves through it, so pages and WebRTC show the VPN's address.
- `main_world_eval` — Reading values exactly as the page's own scripts see them, and calling functions the page defines, from `page.evaluate`.

## Proxies

- **Your own proxy:** `"proxy": "http://user:pass@host:port"` (also `https://`, `socks5://`), an object
  `{ "server", "username", "password" }`, or a list used round-robin. Leave `proxy` out for the free Community proxy.
- **Own VPN instead of a proxy** (dedicated plans only): `"vpn": "<name of a VPN saved in the dashboard>"`, or
  `"vpn": { "type": "wireguard"|"amneziawg"|"singbox"|"openvpn", "config": "<text>" }`, and no `proxy`. Prefer the saved name;
  otherwise read the config from a file or env var, never print it. Errors: `403 vpn_requires_dedicated`,
  `400 vpn_not_available` / `invalid_vpn_config` / `vpn_connect_failed`.
- **Different hosts, different proxies:** `"proxy_rules": [{ "hosts": ["*.cdn.example.com"], "proxy": "…" }]`;
  hosts no rule matches use `proxy`.
- **cdpfleet residential proxy** (no proxy account needed, billed per GB from the account's prepaid
  USD balance): `"proxy": "cdpfleet-resi[-tier-N][-country-XX][-os-windows|android|ios|mac][-session-NAME[-lifetime-MIN]]"`.
  Tiers per GB: Tier 1 $5.00, Tier 2 $2.50, Tier 3 $1.00, Tier 4 $0.50 (no tier = Tier 1); `-os-` only on Tier 1/2.
  `-session-NAME` keeps the same IP (default 30 min). Balance: `GET /v1/me/proxy-balance`;
  when it's used up, launches return `402 proxy_balance_exhausted`. Ask the user before spending it.
- **VPN Tunnel** (`"vpn_tunnel": true`, only with a Tier 1/2 `cdpfleet-resi` default proxy): all traffic incl. UDP goes
  through the residential exit, so WebRTC shows the exit IP and HTTP/3 works. Not a VPN: the proxy still opens the
  connections, so the site sees the exit device's TCP/IP fingerprint either way. Without it a proxied session sends
  no UDP (WebRTC shows only an mDNS name). Prefer a sticky `-session-…` token with it, and no `proxy_rules` (UDP can then leave through a rule's proxy). Wrong proxy →
  `400 tunnel_not_available`.
- **Community proxy** (free, shared, slower than own or residential proxies): `cdpfleet-plan[-country-XX][-os-…][-session-NAME[-lifetime-MIN]]`
  as `proxy` for a country or a sticky exit. On the Community plan leaving `proxy` out uses it; paid plans can use it too by sending the token, but it shares the Community capacity cap (`503 community_capacity_full` when busy) — not for work that must run on time.
- Details: https://cdpfleet.com/docs/proxies.

## Plans and limits

- **Threads** = concurrent sessions, weighted: a headless browser uses 1, a headful one 2.
- **Community** (free): 2 threads, 60 browser-minutes per thread per day, sessions up
  to 15 min, no recordings, profiles or `keep_alive`.
- **Shared** plans: 60 / 120 / 240 / 480 browser-minutes per thread per day; idle timeout up to 10 min (Community 5 min).
- **Dedicated:** unlimited minutes, sessions and inactivity timeouts up to 24 h, `keep_alive`.
- **Per session:** at most 50 / 50 / 50 open pages and 2 GB / 4 GB / 6 GB of memory
  (Community / shared / dedicated). Over the page limit the extra page is closed (Chromium) or the
  session ends with `page_limit`; over memory it ends with `session_memory_limit`. Reuse pages
  and close the ones you're done with.
- Prices: https://cdpfleet.com/docs/pricing.

## Errors — what to do

| status | error | do this |
|---|---|---|
| 400 | `proxy_required`, `invalid_proxy`, `unsupported_option`, `invalid_start_url`, `invalid_channel`, `invalid_version`, `tunnel_not_available`… | Fix the request; the body says which field. `args` and `extensions` aren't supported. |
| 401 | `invalid_api_key` | Check `CDPFLEET_API_KEY`; ask the user for a valid key. |
| 402 | `proxy_balance_exhausted` | The residential balance is used up: tell the user (top up in the dashboard). |
| 403 | `engine_not_allowed`, `profiles_disabled`, `vpn_requires_dedicated` | The plan doesn't include it: pick another browser or option. |
| 429 | `threads_exceeded`, `rate_limited` | Wait `Retry-After` seconds (or close a session) and retry. |
| 429 | `daily_quota_exhausted`, `hourly_quota_exhausted` | The plan's minutes are used up: stop and tell the user. |
| 503 | `shared_capacity_full`, `community_capacity_full`, `version_unavailable`, `resi_unavailable` | Retry after `Retry-After` with backoff. |

Full list: https://cdpfleet.com/docs/errors. How a session ended: `GET /v1/me/sessions` (`end_reason`).

## Gotchas

- Match the Playwright client to the table (1.60.x, Rebrowser 1.52.x) or `connect` fails.
- Camoufox runs `page.evaluate` in an isolated world; launch with `"main_world_eval": true` and
  prefix the script with `mw:` to read the page's own variables.
- `page.setContent` hangs on Rebrowser: navigate to a `data:` URL instead.
- `page.pdf()` works on Chromium-family browsers only.
- On Camoufox, IP-lookup hosts (api.ipify.org, ipinfo.io, icanhazip.com…) always use the default `proxy`,
  never a `proxy_rules` rule; to check a rule's exit there, use ident.me. Other browsers follow the rules.
- Headless Chromium says `HeadlessChrome` in its user agent; use `"headless": false` (2 threads)
  where that matters.

## More

- Docs: https://cdpfleet.com/docs · Errors: https://cdpfleet.com/docs/errors · Account API: https://cdpfleet.com/docs/api
- 89 worked cases with programs in five languages: https://cdpfleet.com/docs/cases
- Index of everything for LLMs: https://cdpfleet.com/llms.txt
