---
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. Use when a task needs a real web browser, a specific browser or version, a proxy or residential IP, or many browsers in parallel.
---

# cdpfleet — skill for AI agents

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,
   and always close it when done — `browser.close()` ends the session and stops the clock.
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.

## 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 | 155.0.8059.84 | stable, beta, dev; version `154`, `153` | 1.60 |
| `edge` | Microsoft Edge | chromium | 155.0.4283.62 | stable, beta, dev | 1.60 |
| `brave` | Brave | chromium | 1.97.59 | stable, beta | 1.60 |
| `opera` | Opera | chromium | 137.0.6036.63 | stable, developer | 1.60 |
| `whale` | Naver Whale | chromium | 5.39.412.57 | stable | 1.60 |
| `yandex` | Yandex Browser | chromium | 26.8.1.1111 | 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 | 152.0.4 | stable; version `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:
- `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.

## 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. 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, invite-only): 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 5 / 10 / 20 open pages and 3 GB / 4 GB / 8 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`, `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
- 61 worked cases with programs in five languages: https://cdpfleet.com/docs/cases
- Index of everything for LLMs: https://cdpfleet.com/llms.txt
