Skip to main content
POST
Run one or more browser-agent tasks in parallel. One request, one clean response — or a live SSE stream, or an async handle to poll.
The tasks field is always an array, even for a single task — so one endpoint covers both “run one task” and “run 1000 tasks in parallel”. No separate batch vs. single-task API.

Overview

  • Method: POST
  • Path: /browser-agent
  • Cost: billed per successful task. Failed tasks are free.
  • Max tasks per request: 1000
  • Max in-flight tasks per user: varies by plan (5 on the free tier, up to 150 on Enterprise). Check yours with GET /browser-agent-status
concurrency vs your in-flight cap: concurrency controls parallel workers within one request. Your in-flight cap counts every submitted task (pending, claimed, and running) across your account — so a 200-task submission counts as 200 against the cap immediately, even at concurrency: 5. If a submission would exceed your remaining cap, it’s rejected with 429 QUEUE_FULL; chunk large workloads to your cap using GET /browser-agent-status.
  • Per-task default timeout: 240000 ms

Headers

Request Body

Task object

Response Modes

Sync (default)

Default behavior. The request holds the HTTP connection until every task in the run completes, then returns the full BatchResult. Best for: small runs (≤ 50 tasks), interactive scripts, jobs where you want one clean response.
Response (200):

Async (async: true)

Returns immediately with a batchId. Poll GET /browser-agent/:id for progress. Best for: large runs, long-running tasks, fire-and-forget jobs.
Response (200):

Stream (stream: true)

Returns text/event-stream with per-step events. Each frame has a taskIndex tying it back to a task in the run. Best for: live UI progress, debugging, watching what the agent is doing in real time. SSE frames: Keepalive :keepalive comments are sent every 10s to stop intermediaries from closing the connection.
Example frames:
If the server is at SSE capacity, new stream requests get 503. Fall back to async: true + polling.

Response Schemas

BatchResult

TaskResult

Task statuses

Status Codes

Error response shape

Non-2xx responses return a structured JSON body (and the matching SSE error event when streaming):
code lets clients dispatch typed handlers without parsing strings. actionUrl, when present, deep-links the user back to pre.dev with the right modal pre-opened — paste it into window.open and they’ll land on the credit-purchase or subscription flow ready to go. The Node and Python SDKs map every code value to a typed exception class with the actionUrl populated — see the Node SDK and Python SDK error-handling sections.

Code Examples

Multiple tasks with concurrency

Structured extraction with output schema

Python — sync + async + streaming

Node.js — sync + streaming

TypeScript — fully typed client

Error Handling

All non-2xx responses follow the structured shape above. The two billing codes carry an actionUrl that auto-opens the right modal on pre.dev — surface it in your UI and the user is one click from resolving the gate.

402 SUBSCRIPTION_REQUIRED

This API key’s user is on the trial plan and has used their lifetime free task. Send them to actionUrl (subscribe modal) or have them upgrade at pre.dev/projects/key.

402 INSUFFICIENT_CREDITS

Subscription is fine, but the credit balance is below the per-task estimate (0.1 credits/task by default; failed tasks aren’t billed but you need the headroom to start). Send them to actionUrl (credits modal) — or top up at pre.dev/projects/key.

429 RATE_LIMITED / QUEUE_FULL

Per-minute request cap hit, or you’re at your plan’s in-flight task cap. Wait for tasks to drain, lower concurrency, or check GET /browser-agent-status.

503 SSE capacity exceeded

The serving pod is already at its SSE connection cap. Retry the request with "async": true and poll — the underlying work isn’t affected.

400 / invalid task shape

Every task needs a url. If you get 400, check you’re not sending tasks: { ... } (a single object) — it must always be an array, even for one task.

Next: Task Status

Fetch results for an async or historical task submission, with the full per-step event timeline.

Authorizations

Authorization
string
header
default:YOUR_API_KEY
required

API key for authentication. Get your API key from https://pre.dev/projects/key (Solo) or https://pre.dev/enterprise/dashboard?page=api (Enterprise). Use format: Bearer YOUR_API_KEY

Body

application/json
tasks
object[]
required
Required array length: 1 - 1000 elements
concurrency
integer

How many tasks to run in parallel. Default: 5.

Required range: 1 <= x <= 20
async
boolean
default:false

If true, returns { id, status: 'processing' } immediately — poll GET /:id for progress.

stream
boolean
default:false

If true, returns an SSE stream with task_event, task_result, done, and error frames.

Response

Batch result (sync mode) or batch stub (async mode).

Run summary. The schema is named BatchResult for backwards compatibility with older clients.

id
string

Run id (24-char Mongo ObjectId).

total
integer

Total tasks in the run.

completed
integer

Number of tasks that have finished (any status).

results
object[]

Per-task results aligned by taskIndex. In-progress runs return PENDING stubs for tasks that haven't started; the stub has only url, instruction, input, and status: "PENDING".

totalCreditsUsed
number

Sum of credits billed across all tasks in the run.

status
enum<string>
Available options:
processing,
completed,
failed
createdAt
string<date-time>
completedAt
string<date-time>

Populated once status !== "processing".

liveEvents
object[][]

Only present when includeEvents=true on GET /:id. Per-task in-flight event streams for tasks that haven't yet finished. Aligned by index with results; completed tasks get an empty array.

error
string

Set only when status === "failed".