> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfsky.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Concurrency

> Run browser jobs in parallel, manage shared account limits, and clean up failed workers.

Set a maximum number of browser jobs, queue excess work, and stop each session when its job finishes. Your account limit applies across all processes using the account.

<span id="gate-on-what-is-actually-free" />

## Check available capacity

```bash theme={null}
curl "$SURFSKY_API_BASE_URL/users/browser-limits" \
  -H "X-Cloud-Api-Token: $SURFSKY_API_TOKEN"
```

Example response for an account with a browser limit:

```json theme={null}
{
  "success": true,
  "msg": "Browser limits retrieved",
  "data": {
    "has_browser_limits": true,
    "parallel_browsers": 50,
    "running": 12,
    "available": 38
  }
}
```

`available` is a snapshot. It does not reserve slots; another worker can start a browser before your next request. Set an application concurrency limit below the account cap if other jobs share the account, and handle `parallel_browsers_limit_reached` at startup.

When `has_browser_limits` is `false`, `parallel_browsers` is `null` and `available` is omitted. Browser slots are one of several account limits; [limits](/limits) covers request rates and proxy traffic quotas and the `429` codes each one returns.

<span id="pick-the-cheaper-session" />

## Use an SDK pool

The [Python](/sdk/python#process-items-concurrently) and [TypeScript](/sdk/typescript#process-items-concurrently) SDKs provide `map()` and browser pools with a concurrency setting and managed cleanup.

A pool can reuse a browser for several items. Cookies, storage, and open pages can carry over. Use separate sessions for jobs that need independent state, and retire a browser after a job that leaves it unusable.

A persistent profile represents one running browser at a time. Starting an already running profile can return that session. Use different profiles to run different accounts concurrently.

<span id="always-stop-in-a-finally" />

<span id="sweep-for-leaks" />

## Stop sessions after failures

Put the stop call in `finally`, outside the framework connection block, so it also runs if connecting fails. The [framework quickstarts](/quickstart#choose-an-integration) show the full lifecycle.

The idle timeout closes a browser after a period without activity. Choose a timeout long enough for legitimate pauses, but avoid using it as the only cleanup mechanism. See [session lifetime](/sessions#session-lifetime).

After a worker crashes, compare `GET /profiles/active` with the session IDs your application owns. Session age alone does not prove a leak: another worker may still be using that browser. Stop only sessions you can identify as abandoned.

<Warning>
  `POST /profiles/stop` stops every browser on the account, including work owned by other processes. Use individual stop calls for routine cleanup.
</Warning>

<span id="ramping-up" />

## Increase load gradually

Start with a small worker count. Measure completed jobs, target-site failures, proxy traffic, and browser startup errors before raising it.

Browser slots and API rate limits are separate. Starting many browsers at once can hit a request limit even when slots remain. Queue startups and use bounded backoff with jitter for temporary capacity errors. See [errors and retries](/errors#retrying).

A timed-out start has an uncertain outcome: the server may have created a browser before the connection was lost. Check active sessions before blindly repeating one-time start requests.

<span id="what-to-watch" />

## What to monitor

| Signal                                        | What to do with it                                                        |
| --------------------------------------------- | ------------------------------------------------------------------------- |
| `data.available` from `/users/browser-limits` | Check account capacity; do not treat it as a reservation.                 |
| Active sessions and your worker records       | Find sessions whose owning job has ended.                                 |
| Minute and hour rate-limit headers            | Slow API requests before exhausting either window.                        |
| `cluster_is_full` or startup failures         | Back off and stop retrying after a bounded number of attempts.            |
| Proxy quota and traffic stats                 | Estimate how much work your remaining traffic can support.                |
| Target status and expected page content       | Measure completed work, including responses that contain challenge pages. |
