Privacy-focused Chromium
Brave
The Brave browser, with stable and beta channels.
- Launch
POST https://starter.cdpfleet.com/brave/session- Connect
chromium.connect(wsUrl, { headers: { 'x-api-key': KEY } })- Playwright client
- 1.60.x
- Versions now
- stable1.99.57 beta1.101.47
Example
// 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/brave/session', {
method: 'POST',
headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
body: JSON.stringify({
proxy: 'http://user:[email protected]: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
# 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/brave/session",
headers={"x-api-key": KEY},
json={
"proxy": "http://user:[email protected]: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
// 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": "http://user:[email protected]:8080",
"headless": "new"
}
""";
HttpResponse<String> res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://starter.cdpfleet.com/brave/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": "http://user:[email protected]:8080",
"headless": "new"
}
""";
var res = await http.PostAsync("https://starter.cdpfleet.com/brave/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": "http://user:[email protected]:8080",
"headless": "new"
}`
req, _ := http.NewRequest("POST", "https://starter.cdpfleet.com/brave/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
}
Want different options or a specific version? Open this browser in the code builder.
Launch options
Send these as the JSON body of the launch request. Everything is optional; unknown fields are ignored. Context options such as viewport, user agent or cookies are set client-side after connecting (browser.newContext({...})).
| Option | Type | Default | What it does |
|---|---|---|---|
proxy | string | object | array | the free Community proxy | A proxy URL ( Use it for: 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 Optional: without a proxy (and without Example: "http://user:[email protected]:8080" |
proxy_rules | array | none | A list of Use it for: 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. Needs Example: [{"name":"dc-assets","hosts":["*.cloudfront.net","static.example.com"],"proxy":["http://u:[email protected]:4444","http://u:[email protected]:4444"]}] |
inactivity_timeout | number | string | 120s | The timer resets on any protocol traffic in either direction. A number of seconds ( Use it for: 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. Up to 10 minutes on shared threads, 5 on Community and 24 h on dedicated; a longer value is cut to your plan's maximum. Idle minutes are billed like any others. A slow page load through a proxy can produce no traffic for a while, so keep it at 60 s or more. Never longer than the session's overall timeout. Example: "2m" |
overall_timeout | number | string | your plan's maximum session length (24 h) | A number of seconds ( Use it for: A hard time budget per job, so a stuck loop can't hold a thread for hours: batch scrapers and agents with a deadline. Example: "30m" |
memory | integer | 1 | A multiplier of your plan's per-session memory (Community 2 GB, shared 4 GB, dedicated 6 GB; headful sessions get 512 MB more for the display). The session's weight is multiplied too: headless with Use it for: Memory-heavy work that would otherwise end with Only Example: 2 |
downloads | boolean | false | With Use it for: 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 ( Paid plans ( Example: true |
vpn_tunnel | boolean | false | The browser gets a network of its own whose only way out is the session's residential proxy, so UDP works as on a home connection: WebRTC shows the residential exit IP as its public address, and HTTP/3 (QUIC) works. Without it, a proxied session sends no UDP at all — WebRTC shows only an anonymous mDNS name and sites fall back to HTTP/2. The TLS fingerprint and the exit IP are the same either way. It is not a VPN: the residential proxy still opens the connections to the site, so the TCP/IP fingerprint the site sees is the exit device's, as without the tunnel. UDP traffic is billed at the token's tier and listed as host Use it for: 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. Needs the default Example: true |
vpn | string | object | none | The name of a VPN saved on the dashboard's Proxies → My VPNs page ( Use it for: 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. Dedicated plans only ( Example: "home-wg" |
headless | boolean | "new" | "shell" | false (headful, on a virtual display) |
Use it for: 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. Example: "new" |
channel | "stable" | "beta" | "dev" | "developer" | stable | Omit it (or send Use it for: 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. Opera calls its dev channel Example: "beta" |
cdp | boolean | false | The launch response then carries Use it for: 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. Same session, proxy and limits. You can connect to Example: true |
screencast | boolean | false | Not a launch option: the generated program calls Use it for: 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. View-only and only for the client that drives the page. With Example: true |
live_view | boolean | false (headful sessions are always watchable) | Headful sessions can always be watched or taken over ( Use it for: 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. Headless Chromium-based browsers only (Camoufox, Firefox and WebKit are watchable when headful, their default). Doesn't give your code CDP access — add Example: true |
screen_size | "WxH" | { width, height } | the server's display (1280×720 window) | Sets the screen the browser runs on, from 640×480 to 3840×2160, e.g. Use it for: Responsive and visual tests at a given resolution, and personas that need a common desktop or laptop screen instead of the server default. Headful sessions only — a headless launch with Example: "1366x768" |
keep_alive | boolean | false | A disconnect no longer ends the session: reconnect to the same Use it for: Agents and step-by-step workflows that disconnect between steps, and code that may lose its connection, without losing the logged-in browser. Not on Rebrowser. Dedicated threads only ( Example: true |
start_url | string (http/https URL) | none | The server opens the page itself and the session doesn't wait for your code to connect: open a live link in Use it for: 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. http or https, at most 2,048 characters ( Example: "https://example.com" |
profile | string | object | none | A name ( Use it for: 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). Every browser except Whale and Rebrowser. Keeps cookies (session cookies too), localStorage and IndexedDB; not sessionStorage. 20 profiles per shared thread and 30 per dedicated thread (at least 100), 50 MB each; list and delete with Example: "shop-login" |
record | boolean | true (when recording applies) | Sessions are recorded by default within your recording limits: headful sessions from their display, headless sessions only when launched with Use it for: Keeping a video of each run for debugging, QA evidence or audits. Set Recording applies to headful sessions and to headless ones launched with Example: false |