Check your project’s capacity
Use your API key to inspect its project:rateLimit is a legacy concurrency field. It does not tell you the HTTP request limit. Treat account information as a snapshot rather than a reservation for a future request. Several workers may read the same available capacity before any of them starts a browser.
Keys in the same project share its capacity and balance. Creating more keys does not create more browser slots. Free accounts can also have limits across their projects.
How usage tiers work
For projects on current pricing, concurrency increases with the project’s net settled lifetime Stripe payments:
Payments count for the project that received them. Refunds and disputed payments do not contribute to qualifying spend. Signup credits and other credit grants are not qualifying purchases. Funding through other billing arrangements may follow different rules.
Your higher concurrency allowance is preserved. When a project participates in spend tiers, its limit takes the maximum of its applicable legacy-plan allowance, the current pricing floor, and its spend-tier allowance. An already-granted higher limit is not automatically lowered. For example, a legacy allowance of 250 remains 250 when the spend tier would grant 50.
Some active legacy or externally billed projects follow a different billing path. Lapsed or cancelling legacy plans can transition onto current pricing while keeping their higher grant. Use
concurrentSessionLimit and the project’s Billing page as the source of truth. If the tier ladder is absent or differs from your expectations, contact support with your project ID before making a purchase solely to increase concurrency.
Keep track of browser lifetime
A V4 session is a conversation. A run is one turn in that conversation. A browser is the live Chrome instance used for website interaction. A browser can survive the run that created it and be reused by a later turn. A conversation can also have multiple browsers. Additional tabs in the same browser are not additional browser sessions. After all runs in a conversation are inactive, V4 browsers become eligible for cleanup after approximately 20 minutes without recent run activity. Cleanup runs periodically, so this is not an exact shutdown deadline. V4 agent browsers also have a four-hour hard timeout. If your application is finished with a browser, explicitly stop that browser rather than relying on idle cleanup. Only stop browser IDs your application owns and no longer needs. For a standalone browser, the stop request is:browser.ready while the run/browser is active. A conversation remaining in history does not mean its live browser is still available.
Queue follow-ups within a conversation
Only one V4 run can be active in a session. Submitting a direct follow-up while it is busy returns 409. Choose the behavior you need:- Wait for the active run to finish, then submit the next turn.
- Submit through the session message queue to process a later turn.
- Intentionally interrupt the active run when you want to change its current work.
Budget HTTP requests separately
HTTP traffic passes through two separate limits: a shared per-IP edge limit and your project’s application limit. Raising one does not raise the other.Edge limits
On September 9, 2026, the standard edge (WAF) ceilings increased:
These are approximate rates evaluated over a five-minute window, not a strict
per-second cap or a guaranteed burst allowance. Applications behind the same
NAT or proxy share the IP’s budget. Account-specific edge rules and certain
special endpoints can have different limits.
Project limits
The edge increase did not change the standard per-project application buckets:
The selected V4 polling endpoints include:
GET /api/v4/runs/{run_id}/statusGET /api/v4/sessions/{session_id}GET /api/v4/browsers/{browser_session_id}
GET /api/v4/runs/{run_id}/events and GET /api/v4/runs/{run_id} use the general bucket. Their budget does not automatically grow with your concurrency grant. Account overrides and other protections can differ from these defaults; use the response headers and error details when diagnosing throttling.
For example, a project with 500 stored concurrent sessions and no request-rate
override has a 1,000 RPS status-polling budget and a separate 25 RPS general
budget. All API keys in that project share those budgets. Creating a new key
does not create more request capacity.
The project limiter counts requests in five-second windows. A 25 RPS
budget therefore permits 125 requests per window. X-RateLimit-Limit reports
that window allowance, not requests per second: a value of 125 does not mean
125 RPS. X-RateLimit-Remaining is the window’s remaining request count.
X-RateLimit-Reset is a relative number of seconds, not a Unix timestamp.
A project throttle includes limit_rps and retry_after_seconds in the JSON
body and normally sends Retry-After: 5. An edge throttle can instead send
Retry-After: 300 without limit_rps. Honor the delay actually returned; a
five-second retry loop is inappropriate for a five-minute edge throttle.
Capture the response headers and body when asking support to identify which
limit applied. A missing limit header is not a promise of unlimited traffic.
If you only need the final result, use the SDK’s status-based wait helper:
When you need events
Use the returned cursor to continue reading and process every page withhasMore before treating the event stream as caught up. After terminal status, drain the remaining event pages; do not exit solely because a separate status request completed.
Set a request budget across all event streams in the project. For example, polling events once per second for 50 runs would generate about 50 event requests per second, exceeding the standard general bucket before counting any creates or other requests. Polling each every five seconds would average about 10 event requests per second; pagination and other traffic still need headroom.
Spread poll times so that every worker does not send its request on the same clock tick. A per-project limiter in your application is more predictable than each worker independently retrying.
For SDK event waits, set wait_for_event(..., interval=3) in Python or
waitForEvent(..., { interval: 3_000 }) in TypeScript. This also works on SDK
3.11.3, whose event-wait default is one second. One hundred waits at a
three-second interval average about 33 event requests per second, exceeding
the standard 25 RPS general budget. For that workload, explicitly use a
five-second interval (20 event requests per second) or a shared request
scheduler, leaving headroom for creates, pagination, and other general calls.
The event-wait helper pauses between pages too. For an old run with a large
event history, start from a known cursor or implement paginated reads within
your shared request budget; a longer interval also makes backlog traversal slower.
Diagnose errors before retrying
The SDK retries some transient responses, including 429, but the default is only three retries. SDK 3.11.3 uses exponential delays of roughly 1, 2, and 4 seconds and does not itself apply
Retry-After or jitter. Add application-level scheduling for sustained workloads. Do not repeatedly submit a new billable run when the outcome of an earlier submission is uncertain.
SDK 3.11.3 errors expose the response status and body, but not response headers.
Use retry_after_seconds when the body includes it. To diagnose an edge throttle
or read its exact Retry-After, capture the raw HTTP response through your
application’s HTTP instrumentation or a REST client.
API-key monthly spending caps are soft limits. They use a cached spend snapshot; already-running or simultaneous work can finish above the cap. They do not replace project balance management or per-run cost controls. BYOK provider charges also remain separate from Browser Use charges.
A practical batch-processing pattern
- Resolve the key’s project, available balance, and current capacity.
- Keep jobs in a durable application queue and admit a bounded number of workers. Leave room for browsers used elsewhere in the project and additional browsers created by agents.
- Track job ID, session ID, run ID, and browser IDs separately.
- Use cheap status polling for result-only jobs; share one request budget across event streams and general API operations.
- Handle busy-session, queue, rate, capacity, and spending errors according to their cause.
- Record terminal outcomes, release unneeded browsers, and retry only when the job’s previous outcome is understood.