Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auth/profiles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -514,5 +514,5 @@ _ = browser
- Profiles store cookies and local storage. Start the session with `save_changes: true` to write changes back when the browser is closed.
- To keep a profile immutable for a run, omit `save_changes` (default) when creating the browser.
- Multiple browsers in parallel can use the same profile, but only one browser should write (`save_changes: true`) to it at a time. Parallel browsers with `save_changes: true` may cause profile corruption and unpredictable behavior.
- `save_changes` applies to a profile attached to a single browser — either at creation (`kernel.browsers.create()`) or loaded afterward with `kernel.browsers.update()`. A profile set on a [browser pool's](/browsers/pools) config is loaded read-only and never persisted; `save_changes` sent on a pool's profile is silently ignored. To persist per-user state through a pool, attach the profile after acquiring the browser and release with `reuse: false` — see [Per-user profiles with pools](/browsers/pools#per-user-profiles-with-browser-pools).
- `save_changes` applies to a profile attached to a single browser — either at creation (`kernel.browsers.create()`) or loaded afterward with `kernel.browsers.update()`. A profile set on a [browser pool's](/browsers/pools) config is loaded read-only and never persisted; `save_changes` sent on a pool's profile is silently ignored. To persist per-user state through a pool, attach the profile with `save_changes: true` after acquiring the browser and release with `reuse: false` — see [Per-user profiles with pools](/browsers/pools#per-user-profiles-with-browser-pools).
- Profile data is encrypted end to end using a per-organization key.
65 changes: 63 additions & 2 deletions browsers/pools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ A few constraints to weigh before moving a workload onto a browser pool:

- **No GPU browsers.** GPU-accelerated browsers are on-demand only. Use `browsers.create()` for WebGL, video, or canvas-heavy work.
- **One fixed configuration per browser pool**, with `start_url` the only setting you can override per acquisition — see [Create a browser pool](#create-a-browser-pool).
- **Profiles load read-only**, and a browser pool holds one at a time — see [Profiles with browser pools](#profiles-with-browser-pools) for how to persist state per user.
- **A profile set on the browser pool loads read-only**, and a browser pool holds one at a time — see [Per-user profiles with browser pools](#per-user-profiles-with-browser-pools) for how to persist state per user.
- **Browser pool capacity counts against your [concurrency limit](/info/pricing#concurrency-limits)** whether or not its browsers are acquired, though idle pooled browsers aren't billed.
- **Plan-gated.** Browser pools are available on the Start-Up and Enterprise plans.

Expand Down Expand Up @@ -208,7 +208,68 @@ A profile attached to the pool is loaded **read-only**. Every browser in the poo

### Per-user profiles with browser pools

Because that profile is shared and read-only, it can't hold per-user login state for many users at once. To serve many users from one browser pool, create it with no profile — stealth, proxies, extensions, and viewport still live on the pool — then attach each user's profile to the browser *after* you acquire it, and release with `reuse: false` so the browser is destroyed. Destroying it both persists that user's profile changes and keeps their state from reaching the next acquirer.
Because that profile is shared and read-only, it can't hold per-user login state for many users at once. To serve many users from one browser pool, create it with no profile — stealth, proxies, extensions, and viewport still live on the pool — then attach each user's profile to the browser *after* you acquire it, and release with `reuse: false` so the browser is destroyed. Destroying it keeps that user's state from reaching the next acquirer.

The read-only rule covers the pool's own profile, not one attached after acquiring: that profile belongs to the browser, so `save_changes` applies as it does on any other browser. Pass `save_changes: true` when you attach it — it defaults to `false`, and without it the browser is destroyed on release without writing the user's session back.

<CodeGroup>
```typescript Typescript/Javascript
const browser = await kernel.browserPools.acquire("my-pool");

await kernel.browsers.update(browser.session_id, {
profile: { name: "user-8f21c3", save_changes: true }
});

// ... drive the browser as that user ...

await kernel.browserPools.release("my-pool", {
session_id: browser.session_id,
reuse: false,
});
```

```python Python
browser = kernel.browser_pools.acquire("my-pool")

kernel.browsers.update(
browser.session_id,
profile={"name": "user-8f21c3", "save_changes": True},
)

# ... drive the browser as that user ...

kernel.browser_pools.release(
"my-pool",
session_id=browser.session_id,
reuse=False,
)
```

```go Go
browser, err := client.BrowserPools.Acquire(ctx, "my-pool", kernel.BrowserPoolAcquireParams{})
if err != nil {
panic(err)
}

if _, err := client.Browsers.Update(ctx, browser.SessionID, kernel.BrowserUpdateParams{
Profile: shared.BrowserProfileParam{
Name: kernel.String("user-8f21c3"),
SaveChanges: kernel.Bool(true),
},
}); err != nil {
panic(err)
}

// ... drive the browser as that user ...

if err := client.BrowserPools.Release(ctx, "my-pool", kernel.BrowserPoolReleaseParams{
SessionID: browser.SessionID,
Reuse: kernel.Bool(false),
}); err != nil {
panic(err)
}
```
</CodeGroup>

A profile can only be loaded into a browser that was created without one, which is why the pool itself has to stay profile-free.

Expand Down
Loading