cdpfleet / Proxies
Every session
Your exit, your rules.
A cdpfleet browser never browses from our servers' own IPs. Send your own proxy, a pool, or different proxies for different hosts; change the exit of a running browser without restarting it; or use the cdpfleet residential proxy by tier, country and sticky session. Leave proxy out and the free Community proxy is used.
- SOCKS5with auth, on every browser
- $0.50–$5per GB, residential
- 0second IPs leaked over WebRTC
Bring your own
- Formats:
http://user:pass@host:8080,https://…,socks5://user:pass@host:1080, a barehost:8080(taken as HTTP), or an object{ "server", "username", "password" }. Authenticated SOCKS5 works on every browser. - Pools: pass a list and requests are spread across it round-robin.
- Per-host rules:
proxy_rulessends hosts you name (with*wildcards) through their own proxy or pool; everything else usesproxy. One SOCKS5 host, one pool and residential for the rest, in one browser (case 07). - Your proxies are free on every plan; only the cdpfleet residential proxy is billed, by the GB.
{
"proxy": [
"http://u:[email protected]:8080",
"socks5://u:[email protected]:1080"
],
"proxy_rules": [
{
"hosts": [
"*.cloudfront.net",
"static.*"
],
"proxy": "http://u:[email protected]:4444"
}
]
}Change the exit while the browser runs
- One API call to the host of your
wsUrlchanges a running session's proxy and rules; the browser, its cookies and its login stay (case 06). Every session can be swapped; no launch option is needed. - New connections use the new exit at once; open ones finish on the old one. Keep-alive connections are the trap: move them over by carrying
storageStateinto a fresh context (case 06). - The update replaces the whole route: send
proxyand the completeproxy_rulesyou want. - When a site pushes back: wait out
Retry-After, then move the session to another exit — the crawler that did got 8 of 8 pages with its login intact; the one that retried at once got 3–4 (case 53).
No proxy of your own: the cdpfleet residential proxy
Send a cdpfleet-resi token as proxy and pick the price tier (Tier 1 $5, Tier 2 $2.50, Tier 3 $1, Tier 4 $0.50 per GB), the exit country, the device OS of the IP (Tier 1 and 2 only) and whether the IP stays the same (-session-ID, -lifetime-MIN). One prepaid USD balance pays for every tier; every account starts with $5 free credit (case 25).
Mix tiers per host: the page on Tier 1, its image hosts on Tier 4 with proxy_rules — the bytes billed per tier matched what the browser counted, and a gallery load cost well under half (case 51). Details and pricing: residential proxy.
// npm install [email protected]
// 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: 'cdpfleet-resi-country-us-session-cart1-lifetime-30',
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
# 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": "cdpfleet-resi-country-us-session-cart1-lifetime-30",
"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
// Maven: com.microsoft.playwright:playwright:1.60.0 and com.google.code.gson:gson:2.11.0
// Run with PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1: browsers run on cdpfleet.
import com.google.gson.JsonParser;
import com.microsoft.playwright.*;
import java.net.URI;
import java.net.http.*;
import java.util.Map;
public class Example {
public static void main(String[] args) throws Exception {
String key = System.getenv("CDPFLEET_API_KEY");
// 1. Launch the browser
String body = """
{
"proxy": "cdpfleet-resi-country-us-session-cart1-lifetime-30",
"headless": "new"
}
""";
HttpResponse<String> res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://starter.cdpfleet.com/chromium/session"))
.header("x-api-key", key)
.header("content-type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofString());
if (res.statusCode() != 200) {
throw new RuntimeException("launch failed: " + res.statusCode() + " " + res.body());
}
String wsUrl = JsonParser.parseString(res.body()).getAsJsonObject().get("wsUrl").getAsString();
// 2. Connect and drive it
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connect(wsUrl,
new BrowserType.ConnectOptions().setHeaders(Map.of("x-api-key", key)));
Page page = browser.newPage();
page.navigate("https://example.com");
System.out.println(page.title());
browser.close(); // ends the session and stops billing
}
}
}
// dotnet add package Microsoft.Playwright --version 1.60.0
// Browsers run on cdpfleet, so no `playwright install` is needed.
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Playwright;
var key = Environment.GetEnvironmentVariable("CDPFLEET_API_KEY")!;
// 1. Launch the browser
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", key);
var body = """
{
"proxy": "cdpfleet-resi-country-us-session-cart1-lifetime-30",
"headless": "new"
}
""";
var res = await http.PostAsync("https://starter.cdpfleet.com/chromium/session",
new StringContent(body, Encoding.UTF8, "application/json"));
if (!res.IsSuccessStatusCode)
throw new Exception($"launch failed: {(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");
var wsUrl = (await res.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("wsUrl").GetString()!;
// 2. Connect and drive it
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectAsync(wsUrl, new BrowserTypeConnectOptions
{
Headers = new Dictionary<string, string> { ["x-api-key"] = key },
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");
Console.WriteLine(await page.TitleAsync());
await browser.CloseAsync(); // ends the session and stops billing
// go get github.com/playwright-community/[email protected]
//
// One-time driver setup (needs Node.js 18+): playwright-go can no longer download the
// 1.60.0 driver itself, so build it from npm and point PLAYWRIGHT_DRIVER_PATH at it:
// mkdir -p ~/.cache/pw-driver-1.60.0 && cd ~/.cache/pw-driver-1.60.0 &&
// curl -sL https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz | tar xz &&
// ln -sf "$(command -v node)" node
// export PLAYWRIGHT_DRIVER_PATH=~/.cache/pw-driver-1.60.0
// Browsers run on cdpfleet, so no browsers are downloaded (SkipInstallBrowsers).
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/playwright-community/playwright-go"
)
func main() {
key := os.Getenv("CDPFLEET_API_KEY")
// 1. Launch the browser
body := `{
"proxy": "cdpfleet-resi-country-us-session-cart1-lifetime-30",
"headless": "new"
}`
req, _ := http.NewRequest("POST", "https://starter.cdpfleet.com/chromium/session", strings.NewReader(body))
req.Header.Set("x-api-key", key)
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
msg, _ := io.ReadAll(res.Body)
log.Fatalf("launch failed: %s %s", res.Status, msg)
}
var session struct {
WsURL string `json:"wsUrl"`
}
if err := json.NewDecoder(res.Body).Decode(&session); err != nil {
log.Fatal(err)
}
// 2. Connect and drive it (Install fetches only the Playwright driver, once)
opts := &playwright.RunOptions{SkipInstallBrowsers: true}
if err := playwright.Install(opts); err != nil {
log.Fatal(err)
}
pw, err := playwright.Run(opts)
if err != nil {
log.Fatal(err)
}
defer pw.Stop()
browser, err := pw.Chromium.Connect(session.WsURL, playwright.BrowserTypeConnectOptions{
Headers: map[string]string{"x-api-key": key},
})
if err != nil {
log.Fatal(err)
}
page, err := browser.NewPage()
if err != nil {
log.Fatal(err)
}
if _, err := page.Goto("https://example.com"); err != nil {
log.Fatal(err)
}
title, _ := page.Title()
fmt.Println(title)
browser.Close() // ends the session and stops billing
}
UDP, WebRTC and your own VPN
- A proxy carries TCP only: behind one, no setup exposed a second public IP over WebRTC — Chromium, Chrome and Firefox show an anonymous mDNS name, Camoufox the exit, and
block_webrtcremoves the API (case 54). - VPN Tunnel (
"vpn_tunnel": true, a Tier 1 or 2 residential token, or your own proxy on dedicated plans) gives the browser UDP through the same exit: WebRTC shows exactly the exit IP and HTTP/3 works (case 69). Keep the token sticky, and don't combine it withproxy_rules: UDP can then leave through a rule's proxy. - Your own VPN (dedicated plans):
vpnwith a WireGuard, AmneziaWG, OpenVPN or sing-box config — or the name of one saved in the dashboard — sends everything, UDP and DNS included, through your VPN server. Private and internal addresses are refused as VPN servers.
The free Community proxy
- Leave
proxyout and the session uses the Community proxy, free on every plan; acdpfleet-plantoken picks a country or a sticky exit. - Free, so not guaranteed: it's shared and slower, and launches with it can be refused with
503 community_capacity_fullwhen it's busy. Use it to try things out; bring your own or the residential proxy for work that matters.
What a proxy costs in speed
- Every hop adds time: connection setup, first byte and full load through three proxies, measured by the browser itself (case 14).
- WebSockets work through every proxy type, but each message pays the full proxy path, and the proxy's location decides how much (case 13).
- Private addresses are refused: a proxy, a rule's proxy or a VPN server on loopback, private or internal ranges gets
400 invalid_proxy(orinvalid_vpn_config) at launch.