[cdpfleet](https://cdpfleet.com/) / AI agents

For AI agents

# Give your AI *a real browser*.

Let Claude, ChatGPT, Gemini, Cursor or your own agent browse with a real browser on our fleet: the browser and exit country the site should see, a person who can step in for a captcha or a login, and a video of what the agent did.

[Connect your assistant](https://cdpfleet.com/docs/mcp/setup)[skill.md for coding agents](https://cdpfleet.com/skill.md)

-   **1 URL**to connect an assistant
-   **12**real desktop browsers
-   **OAuth**no key to paste

## Connect an assistant: one URL

Add `https://cdpfleet.com/mcp` as an MCP server or connector in Claude, ChatGPT, Gemini, Claude Code, Codex or Cursor. When the assistant opens a cdpfleet window, sign in and click **Allow** (OAuth) — nothing to install or copy, and you can disconnect it any time in the dashboard. Scripts and agents without OAuth send an API key as `Authorization: Bearer`. Step by step for each app: [MCP setup](https://cdpfleet.com/docs/mcp/setup).

The assistant then has tools to launch a browser (any of the fleet's browsers, headful by default, with a residential exit by country or your own proxy), navigate, read the page as an accessibility snapshot, click and type, take screenshots, and hand the browser to a person. Each MCP connection gets its own browser; one left idle for 30 minutes is closed.

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

## Coding agents: skill.md

An AI coding agent writing a scraper or a test doesn't need MCP — it needs the rules. [`/skill.md`](https://cdpfleet.com/skill.md) is one page for it: how to launch, connect and close, the browser list from the live fleet, proxies, limits, errors and the gotchas. Point the agent at it, or at [`/llms.txt`](https://cdpfleet.com/llms.txt) for the full index of docs, cases and comparisons.

```text
Read https://cdpfleet.com/skill.md, then write a Playwright script that opens news.ycombinator.com
through a cdpfleet browser and prints the top 5 story titles. My API key is in CDPFLEET_API_KEY.
```

## What a real browser gives an agent

### Headful, real builds

Real Chrome, Edge, Brave and Firefox-based browsers with a display, without the headless tells sites check for ([case 16](https://cdpfleet.com/docs/cases/headless-tells)). Headful takes 2 threads, headless 1.

[The browsers →](https://cdpfleet.com/browsers)

### A person can take over

A captcha, a login or a one-time code: the agent hands the browser to a person in live view and waits, then carries on in the same page.

[Live view →](https://cdpfleet.com/live-view)

### A video of every run

Headful sessions are recorded: replay what the agent saw and did, step by step — the first 15 minutes of each session free on paid plans.

[Recordings →](https://cdpfleet.com/recordings)

### Stays logged in

A persistent profile keeps cookies, localStorage and IndexedDB between sessions: log in once by hand, and the agent starts signed in next time.

[Profiles →](https://cdpfleet.com/profiles)

## Long runs and logins from code

-   **Stay logged in:** launch with `"profile": "agent-login"` and work in `browser.contexts()[0]` — that context is saved when the session ends ([case 40](https://cdpfleet.com/docs/cases/persistent-profiles)). On Camoufox the profile also keeps the device fingerprint ([case 49](https://cdpfleet.com/docs/cases/camoufox-same-device)).
-   **Hand over and take back:** a `start_url` session can be driven by a person in live view while your code joins later and finds the same page ([case 58](https://cdpfleet.com/docs/cases/launch-interactive-to-code)).
-   **Find out what went wrong:** fetch the recording of a failed run by its session id ([case 41](https://cdpfleet.com/docs/cases/replay-a-failed-run)).
-   **Files the agent downloads are kept:** launch with `"downloads": true` and they are listed about a second after the browser closes — the MCP `downloads` tool waits for them; a person clicking in live view gets the same ([case 82](https://cdpfleet.com/docs/cases/downloads-in-practice)).
-   **Hand a person a tab as a link:** when the agent needs someone to sign in or answer a one-time code, open a [live tab](https://cdpfleet.com/docs/functions/livetab) and send its `live_url`; when it closes, read the tab's cookies and carry on in code ([the live tab](https://cdpfleet.com/docs/updates/live-tab)).
-   **Many tabs need memory:** an agent that opens tab after tab hits the session's memory before the 50-tab cap; `"memory": 2` doubles it ([case 80](https://cdpfleet.com/docs/cases/tabs-and-memory-by-plan)).
-   **Survive a restart:** on [dedicated threads](https://cdpfleet.com/dedicated-threads) `keep_alive` keeps the browser running when the agent disconnects; reconnect to the same `wsUrl` within `inactivity_timeout` (up to 24 h) and its pages are where they were.
-   **Feed the model less:** an accessibility snapshot is a fraction of the page's HTML ([case 32](https://cdpfleet.com/docs/cases/page-to-text-for-llms)).

Headful Chrome that keeps the agent's login between sessions

 

```node
// 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/chrome/session', {
  method: 'POST',
  headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
  body: JSON.stringify({
    proxy: 'http://user:pass@proxy.example.com:8080',
    headless: false,
    profile: 'agent-login',
  }),
});
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 context = browser.contexts()[0]; // the saved profile — a new context isn't saved
const page = context.pages()[0] ?? await context.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
# 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:pass@proxy.example.com:8080",
        "headless": False,
        "profile": "agent-login",
    },
    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})
    context = browser.contexts[0]  # the saved profile — a new context isn't saved
    page = context.pages[0] if context.pages else context.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()  # ends the session and stops billing
```

```java
// 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:pass@proxy.example.com:8080",
          "headless": false,
          "profile": "agent-login"
        }
        """;
    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)));
      BrowserContext context = browser.contexts().get(0); // the saved profile — a new context isn't saved
      Page page = context.pages().isEmpty() ? context.newPage() : context.pages().get(0);
      page.navigate("https://example.com");
      System.out.println(page.title());
      browser.close(); // ends the session and stops billing
    }
  }
}
```

```csharp
// 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:pass@proxy.example.com:8080",
      "headless": false,
      "profile": "agent-login"
    }
    """;
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 context = browser.Contexts[0]; // the saved profile — a new context isn't saved
var page = context.Pages.Count > 0 ? context.Pages[0] : await context.NewPageAsync();
await page.GotoAsync("https://example.com");
Console.WriteLine(await page.TitleAsync());
await browser.CloseAsync(); // ends the session and stops billing
```

```go
// go get github.com/playwright-community/playwright-go@v0.6000.0
//
// 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:pass@proxy.example.com:8080",
	  "headless": false,
	  "profile": "agent-login"
	}`
	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)
	}
	context := browser.Contexts()[0] // the saved profile — a new context isn't saved
	var page playwright.Page
	if pages := context.Pages(); len(pages) > 0 {
		page = pages[0]
	} else if page, err = context.NewPage(); 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
}
```

## Try it free, and when not to

-   **Try it on the [Community plan](https://cdpfleet.com/community):** 2 threads, 60 browser-minutes each a day, the MCP server included. It has no recordings, profiles or `keep_alive`, and request data may be used to improve the service — don't sign in to real accounts on it.
-   **Not for every page:** if a plain HTTP request returns the data, an agent doesn't need a browser at all ([fetch first](https://cdpfleet.com/docs/cases/fetch-first)).
-   **Or one call instead of a browser:** for a page as Markdown, a screenshot or a PDF, a [Function](https://cdpfleet.com/docs/functions) returns it in one request — no browser to drive; for several outputs of the same pages, one session is cheaper ([case 85](https://cdpfleet.com/docs/cases/function-vs-your-own-browser)).
-   **Not a crawler farm:** an MCP connection drives one browser at a time. For thousands of pages, have the agent write code that runs many sessions in parallel ([worker pool](https://cdpfleet.com/docs/cases/worker-pool)).

## Related

Cases, recipes, posts and comparisons about this — updated as new ones are published.

### Reference docs

The details, options and errors.

-   [MCP setup](https://cdpfleet.com/docs/mcp/setup)Claude, ChatGPT, Gemini, Claude Code, Codex, Cursor.
-   [MCP server](https://cdpfleet.com/docs/mcp)Tools, hosted and local.
-   [skill.md](https://cdpfleet.com/skill.md)One page for AI coding agents.
-   [Live view](https://cdpfleet.com/docs/live-view)Handing a session to a person.
-   [SDKs](https://cdpfleet.com/docs/sdk)Node.js, Python and Go clients.
-   [AI agent examples](https://cdpfleet.com/docs/cases#for-agents)Examples and benchmarks for this use, filtered.

### Cases 7

Real problems solved end to end, run on production in five languages.

-   [Case 88: A monitor end to end: create it, trigger a change, verify the signed webhook, read the diff and history](https://cdpfleet.com/docs/cases/monitor-end-to-end)Watch one element of a page that changes on every load, trigger checks with `POST …/run`, receive the `monitor.change` webhook and verify its HMAC signature, check…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/88-monitor-end-to-end)
-   [Case 82: Kept downloads in practice: a person in Launch interactive and an AI agent over MCP](https://cdpfleet.com/docs/cases/downloads-in-practice)Files nobody's code was waiting for: a person pastes file links into a Launch interactive session, an AI agent clicks a download and opens a file link through the…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/82-downloads-in-practice)
-   [Case 79: MCP in action: an MCP client drives a cloud browser through cdpfleet.com/mcp](https://cdpfleet.com/docs/cases/mcp-in-action)A script connects to the hosted MCP server with its API key and does what an AI assistant does: list the tools, launch a browser, open a page, read it as an…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/79-mcp-in-action)
-   [Case 78: keep\_alive: an agent run that survives its client going away](https://cdpfleet.com/docs/cases/keep-alive-reconnect)An agent works through a catalogue in a headless Chrome session with `keep_alive`, then its client disconnects. A new client reconnects to the same session and…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/78-keep-alive-reconnect)
-   [Case 58: Do it by hand in live view, then let your code finish in the same browser](https://cdpfleet.com/docs/cases/launch-interactive-to-code)Launch a browser with `start_url` and no client, drive it through a control-mode live link — address bar, Tab, typing — then connect your code: it finds the person's…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/58-launch-interactive-to-code)
-   [Case 41: A scrape broke: find out why from its session recording](https://cdpfleet.com/docs/cases/replay-a-failed-run)A scraper dies with a timeout. Its session was recorded anyway: fetch the video by session id and see the page it failed on, with no tracing set up in advance.[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/41-replay-a-failed-run)
-   [Case 32: A page for an LLM: HTML, innerText or the accessibility snapshot?](https://cdpfleet.com/docs/cases/page-to-text-for-llms)Three ways to hand a rendered page to a model — raw HTML, innerText and Playwright's ARIA snapshot — measured on three real pages: how big each is and what it keeps.[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/32-page-to-text-for-llms)

### Updates 2

Product news and browser releases.

-   [The live tab: a remote browser tab you share as a link](https://cdpfleet.com/docs/updates/live-tab)Open a real browser tab in another country and use it from any web browser — free on the site, longer from the dashboard or one API call, and a link you can hand to…
-   [Kept downloads: files from a session, fetched after it ends](https://cdpfleet.com/docs/updates/kept-downloads)Launch with "downloads": true and every file the browser downloads is kept for 2 days — including files a person clicked in live view or an AI agent fetched through…

## Read next

[Pages to text for LLMs →](https://cdpfleet.com/llm-data)[Live view →](https://cdpfleet.com/live-view)[Recordings →](https://cdpfleet.com/recordings)[Profiles →](https://cdpfleet.com/profiles)[Community plan →](https://cdpfleet.com/community)

---
Source: https://cdpfleet.com/ai-agents · Markdown: https://cdpfleet.com/ai-agents.md · All docs for AI tools: https://cdpfleet.com/llms.txt
