Skip to content

Guide

How to run several browser profiles at once with Playwright.

On this page

Playwright’s own browser contexts share one browser process, one fingerprint and the same hardware values. Donut Browser profiles are separate browsers, each with its own device, cookies and proxy. Here you launch several profiles with one call to the local API, attach Playwright to each one, run a task in all of them at the same time, and stop them. You need Pro, Team or Enterprise, Node.js or Python with Playwright, and the local API turned on.

1. Turn on the API and export the token

Open Integrations (Cmd/Ctrl+I) → Local API and turn on Enable local API server. Copy the Authentication token and export it as DONUT_API_KEY. The API listens on 127.0.0.1:10108, and only programs on your computer can reach it. See Authentication.

2. Pick the profiles

GET /v1/profiles returns every profile. It has no filter parameters, so select the profiles in your script, for example by tag:

curl -sS http://127.0.0.1:10108/v1/profiles \
  -H "Authorization: Bearer $DONUT_API_KEY" |
  jq -r '.profiles[] | select(.tags | index("shop")) | select(.is_running | not) | .id'

Leave out profiles that already run. A batch launch does not take over a running browser; it reports PROFILE_RUNNING for it. POST /v1/profiles/{id}/run on one profile does attach to a running browser.

3. Launch the batch

POST /v1/profiles/batch/run starts the profiles and returns one debugging port per profile.

FieldDefaultMeaning
profile_idsrequiredThe profiles to launch
urlnoneA page to open in each profile (http, https or about:blank)
headlessfalseLaunch without windows
concurrency3How many profiles may be starting at the same time, 1 to 20. It does not limit how many run.
delay_ms800Pause between the starts of two launches, 0 to 60000

Values outside the ranges are moved to the nearest limit. The defaults come from Settings → Bulk launches: Pause between launches and Profiles starting at once.

curl -sS -X POST http://127.0.0.1:10108/v1/profiles/batch/run \
  -H "Authorization: Bearer $DONUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"profile_ids": ["<id 1>", "<id 2>", "<id 3>"], "concurrency": 3, "delay_ms": 800}'
# {"results":[{"profile_id":"<id 1>","ok":true,"remote_debugging_port":53127,"error":null}, …]}

The results come in the order of profile_ids. Each browser binds its DevTools port to 127.0.0.1.

4. Attach Playwright and run the task

Connect to each port with connectOverCDP. The profile’s first context holds its cookies, so use it instead of a new context. The scripts below launch the batch, run one task in every profile at once, and stop the batch even when a task fails.

// npm install playwright
import { chromium } from "playwright";

const API = "http://127.0.0.1:10108/v1";
const headers = {
  Authorization: `Bearer ${process.env.DONUT_API_KEY}`,
  "Content-Type": "application/json",
};
const ids = ["<id 1>", "<id 2>", "<id 3>"];

const { results } = await fetch(`${API}/profiles/batch/run`, {
  method: "POST",
  headers,
  body: JSON.stringify({ profile_ids: ids, concurrency: 3 }),
}).then((r) => r.json());

try {
  const launched = results.filter((r) => r.ok);
  for (const r of results.filter((r) => !r.ok)) console.error(r.profile_id, r.error);

  await Promise.all(
    launched.map(async ({ profile_id, remote_debugging_port }) => {
      const browser = await chromium.connectOverCDP(`http://127.0.0.1:${remote_debugging_port}`);
      const context = browser.contexts()[0];
      const page = context.pages()[0] ?? (await context.newPage());
      await page.goto("https://example.com");
      console.log(profile_id, await page.title());
      await browser.close(); // disconnects; the browser keeps running
    }),
  );
} finally {
  await fetch(`${API}/profiles/batch/stop`, {
    method: "POST",
    headers,
    body: JSON.stringify({ profile_ids: ids }),
  });
}

browser.close() on a browser you connected to only disconnects Playwright. The profile keeps running until you stop it.

5. Stop the profiles

POST /v1/profiles/batch/stop with {"profile_ids": [...]} stops the profiles one after another and returns one result per profile, with ok and error. POST /v1/profiles/{id}/kill stops one profile. In the app, select the profiles and click Stop selected.

To run the same launch from the app, select the profiles and click Run selected. Both buttons need Pro, Team or Enterprise.

Limits

  • The launch routes need Pro, Team or Enterprise. On other plans they answer 402.
  • The Wayfern engine limits how many browsers your plan may have open at once. Past it, a launch fails with “Too many browsers are open for your plan. Close a browser and try again.” (WAYFERN_INSTANCE_LIMIT_REACHED).
  • Wayfern accepts up to 100 automation (CDP) commands a minute. A command over the limit fails with “Automation rate limit exceeded (N requests/minute). Retry shortly.” Playwright sends several CDP commands for one action, so many profiles that click fast reach the limit sooner. Space out the steps, or run fewer profiles at once.
  • When your plan sets an hourly limit on launch requests, the routes answer 429 with a Retry-After header and the body automation request rate limit exceeded. A batch call counts as one request.

Troubleshooting

Result or errorWhat to do
PROFILE_RUNNING in a resultThe profile was already open. Stop it first, or use POST /v1/profiles/{id}/run, which attaches.
“profile is locked by another team member”A teammate has the profile open. See Team.
“cross-OS profiles cannot be launched”The profile was made for another operating system and cannot run on this computer.
“launch failed: …”Read the rest of the message. A proxy that does not answer or the browser limit of your plan are common causes.
connectOverCDP times outThe port is from an old launch, or the profile stopped. Launch again and use the new port.
402The route needs Pro, Team or Enterprise.

Launching and driving profiles from code is part of Pro.

Last updated .