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

Persistent profiles

# Log in once. *Stay logged in.*

Launch with `"profile": "name"` and the browser restores that profile's cookies, localStorage and IndexedDB, then saves them again when the session ends — so the next launch starts signed in.

[Profile docs](https://cdpfleet.com/docs/profiles)[Code builder](https://cdpfleet.com/docs/builder)

-   **20–30**profiles per thread
-   **50 MB**per profile
-   **10**browsers

## How it works

-   The first launch with a new name creates the profile; every later launch restores it.
-   Work in **`browser.contexts()[0]`** — that context is the profile. A context you create with `newContext()` is a throwaway one and isn't saved.
-   When the session ends and the browser has exited cleanly, the profile is saved: cookies (session cookies included), localStorage and IndexedDB.
-   `"profile": {"id": "shop-login", "mode": "readonly"}` restores without saving, so many sessions can share one login in parallel; a second writer gets `409 profile_in_use`.

Chrome that stays logged in 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: 'new',
    profile: 'shop-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": "new",
        "profile": "shop-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": "new",
          "profile": "shop-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": "new",
      "profile": "shop-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": "new",
	  "profile": "shop-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
}
```

## Browsers and limits

-   Profiles work on Google Chrome, Microsoft Edge, Brave, Opera, Yandex Browser, Chromium, Patchright, Camoufox, Firefox, WebKit.
-   A Camoufox profile also keeps its device: the fingerprint generated on the first launch is saved and reused, so a site sees the same computer every time.
-   20 profiles per shared thread and 30 per dedicated thread (at least 100 per account), 50 MB per saved profile, included in every paid plan (not on Community). List and delete them with `GET` / `DELETE /v1/me/profiles`.
-   Each profile is encrypted with its own key and stored privately in Germany.

## Without profiles

Playwright's `storageState` still works for carrying cookies and storage between sessions in your own code, and `keep_alive` on [dedicated threads](https://cdpfleet.com/dedicated-threads) keeps the same browser across reconnects.

## Related

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

### Reference docs

The details, options and errors.

-   [Persistent profiles](https://cdpfleet.com/docs/profiles)Modes, limits, errors and the API.
-   [Errors & limits](https://cdpfleet.com/docs/errors)Profile errors and what to do.

### Cases 14

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

-   [Case 77: Kasada and AWS WAF on real sites: twelve browsers, four sites, one exit each](https://cdpfleet.com/docs/cases/kasada-and-aws-waf-by-browser)Two real sites behind Kasada and two behind AWS WAF, loaded once by each of twelve browsers and by a returning Camoufox profile, through one sticky residential exit…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/77-kasada-and-aws-waf-by-browser)
-   [Case 75: HUMAN on real sites: twelve browsers, three sites, one exit each](https://cdpfleet.com/docs/cases/human-real-sites-by-browser)Three real sites behind HUMAN (formerly PerimeterX), chosen from our sweep as easy, medium and hard, loaded once by each of twelve browsers and by a returning…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/75-human-real-sites-by-browser)
-   [Case 73: Akamai on real sites: twelve browsers, three sites, one exit each](https://cdpfleet.com/docs/cases/akamai-real-sites-by-browser)Three real sites behind Akamai Bot Manager, chosen from our sweep as easy, medium and hard, loaded once by each of twelve browsers and by a returning Camoufox…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/73-akamai-real-sites-by-browser)
-   [Case 71: DataDome on real sites: twelve browsers, three sites, one exit each](https://cdpfleet.com/docs/cases/datadome-real-sites-by-browser)Three real sites behind DataDome, chosen from our sweep as easy, medium and hard, loaded once by each of twelve browsers and by a returning Camoufox profile, all…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/71-datadome-real-sites-by-browser)
-   [Case 70: Cloudflare on real sites: twelve browsers, three sites, one exit each](https://cdpfleet.com/docs/cases/cloudflare-real-sites-by-browser)Three real sites behind Cloudflare, chosen from our sweep as easy, medium and hard, loaded once by each of twelve browsers and by a returning Camoufox profile, all…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/70-cloudflare-real-sites-by-browser)
-   [Case 66: Which fingerprints stay the same across launches: canvas, WebGL, audio and fonts on seven browsers](https://cdpfleet.com/docs/cases/fingerprint-stability-across-launches)Three launches per browser, each reading a canvas hash, the WebGL renderer and a rendered WebGL image, an audio hash and a font list on the same page. Chrome, Edge,…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/66-fingerprint-stability-across-launches)
-   [Case 67: Akamai and HUMAN: what gets through? Four browsers, three networks, the vendors' own sites](https://cdpfleet.com/docs/cases/akamai-and-human-what-gets-through)akamai.com (Akamai Bot Manager) and humansecurity.com (HUMAN, formerly PerimeterX), each protected by its own vendor, loaded once per cell by headless Chromium,…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/67-akamai-and-human-what-gets-through)
-   [Case 65: DataDome: what gets through? Four browsers, three networks, DataDome's own site](https://cdpfleet.com/docs/cases/datadome-what-gets-through)DataDome's own home and pricing pages, which DataDome protects, loaded once per cell by headless Chromium, headful Chrome, headful Edge and Camoufox through your own…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/65-datadome-what-gets-through)

[All cases →](https://cdpfleet.com/docs/cases) 14 match this topic

### Recipes 5

Complete programs for one setup each.

-   [Chrome that stays logged in between sessions](https://cdpfleet.com/docs/examples#chrome-profile)`profile: "shop-login"` restores the profile's cookies, localStorage and IndexedDB at launch and saves them when the session ends — log in once and reuse it on every…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/examples/recipes/chrome-profile)
-   [Camoufox that keeps the same device and login](https://cdpfleet.com/docs/examples#camoufox-profile)A Camoufox profile keeps its device: the fingerprint generated on the first launch (here a Windows one) is saved with the logins and reused, so the site sees the…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/examples/recipes/camoufox-profile)
-   [Firefox that stays logged in between sessions](https://cdpfleet.com/docs/examples#firefox-profile)`profile` works on Firefox too: cookies (session ones included) and localStorage are restored at launch and saved when the session ends. Measured on prod with this…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/examples/recipes/firefox-profile)
-   [WebKit that stays logged in between sessions](https://cdpfleet.com/docs/examples#webkit-profile)`profile` works on WebKit: cookies and localStorage set in one session came back in the next (measured on prod with this launch, on example.com). One WebKit rule to…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/examples/recipes/webkit-profile)
-   [Many scrapers sharing one saved login](https://cdpfleet.com/docs/examples#profile-readonly)`"mode": "readonly"` restores a profile without saving it back, so any number of sessions can share one login in parallel while a single normal session keeps it…[Code on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/examples/recipes/profile-readonly)

## Read next

[Dedicated threads →](https://cdpfleet.com/dedicated-threads)[Recordings →](https://cdpfleet.com/recordings)[AI agents →](https://cdpfleet.com/ai-agents)[Browsers →](https://cdpfleet.com/browsers)

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