Always-on workloads
Monitoring, long crawls and queues that keep browsers busy most of the day.
cdpfleet / Dedicated threads
Plans · dedicated threads
A dedicated thread is capacity reserved for you: no daily minutes, no rolling hour, no leases. $5 per thread per month — $0.007 per browser-hour when it runs 24/7.
With dedicated threads your thread count is the only limit: run that many browsers for as long as you like. Nothing counts down during the day, nothing caps the hour, and a launch is never refused because the shared fleet is busy.
One browser running around the clock is 720 browser-hours a month for $5 — $0.007 an hour. That is the lowest per-hour price on the comparison with other cloud browsers.
| Dedicated threads | 5 | 10 | 25 | 50 | 100 | 500 | 1,000 | 2,500 | 5,000 | 10,000 | More than 10,000 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Dedicated$5 / thread / month | $25 | $50 | $125 | $250 | $500 | $2,500 | $5,000 | $12,500 | $25,000 | $50,000 | CustomContact sales |
overall_timeout for a hard cap.inactivity_timeout (e.g. "10m") to keep a quiet browser open while your code thinks or waits.keep_alive: a disconnect no longer ends the session. Reconnect to the same wsUrl within the idle timeout and your contexts, pages and logins are still there — for long agent runs, deploys of your own code, or flaky networks.// 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/chrome/session', {
method: 'POST',
headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
body: JSON.stringify({
proxy: 'http://user:[email protected]:8080',
headless: 'new',
keep_alive: true,
inactivity_timeout: '10m',
}),
});
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/chrome/session",
headers={"x-api-key": KEY},
json={
"proxy": "http://user:[email protected]:8080",
"headless": "new",
"keep_alive": True,
"inactivity_timeout": "10m",
},
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",
"keep_alive": true,
"inactivity_timeout": "10m"
}
""";
HttpResponse<String> res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://starter.cdpfleet.com/chrome/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",
"keep_alive": true,
"inactivity_timeout": "10m"
}
""";
var res = await http.PostAsync("https://starter.cdpfleet.com/chrome/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",
"keep_alive": true,
"inactivity_timeout": "10m"
}`
req, _ := http.NewRequest("POST", "https://starter.cdpfleet.com/chrome/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
}
Monitoring, long crawls and queues that keep browsers busy most of the day.
An agent that waits on a model or a person keeps its browser open with a long idle timeout and keep_alive.
Sign in once and keep the session for hours — or save it as a persistent profile for the next launch.
A flat monthly price per browser slot, metered to the second for your own reports.
If your browsers run a few hours a day, shared threads cost less: Shared 60 is $1, Shared 120 is $1.50, Shared 240 is $2.50, Shared 480 is $4 per thread per month. Need more than 10,000 threads? Contact sales.
| Community | Shared | Dedicated | |
|---|---|---|---|
| Price | Free | $1–$4 per thread / month | $5 per thread / month |
| Concurrent browsers | 2 threads | Up to your thread count | Up to your thread count |
| Daily browser-minutes | 60 per thread | Each thread adds its minutes to one account pool | Unlimited |
| Rolling-hour limit | 25% of the daily pool in any 60 minutes | 25% of the daily pool in any 60 minutes | None |
| Session length | Up to 15 minutes | Up to 24 h | Up to 24 h |
| Session leases | Lowest priority, 5-minute leases | 15-minute leases, renewed automatically while shared capacity allows | None — capacity is reserved |
| Idle timeout | 120 s by default, up to 5 min | 120 s by default, up to 10 min | 120 s by default, up to 24 h |
| Launch rate | At least 2 per second and 10 per minute | Threads ÷ 5 per second, threads × 6 per minute (at least 2 and 10) | Twice the shared rate: threads ÷ 5 × 2 per second, threads × 12 per minute |
| Proxies | Included (shared), or your own | Your own, the residential add-on, or the free Community proxy | Your own, the residential add-on, or the free Community proxy |
| Per session | 5 open pages, 3 GB memory | 10 open pages, 4 GB memory | 20 open pages, 8 GB memory |
| Recordings, profiles, keep-alive | Not included | Recordings, and profiles: 20 per thread (at least 100) | Recordings, keep-alive, and profiles: 30 per thread (at least 100) |
| Sizes | One size; invite-only for now | 5–1,000 threads | 5–10,000 threads, custom above |