For AI agents: a documentation index is available at /llms.txt
Skip to main content

Agent Run API

BETA

The Agent Run API is currently in beta. Parameters and response shapes may change in future releases.

The Agent Run API is only available for Cloud plans. Contact us for more information here.

Run a managed AI agent that drives a real browser to complete a task described in plain English. The agent plans and executes its own browser steps (navigate, click, type, read) and returns a structured, LLM-ready result. You can optionally constrain the run with a JSON output schema, a starting URL, a navigation allowlist, an authentication profile, or webhook notifications.

The browser and the model are fully managed. You do not supply an AI model key or run a browser yourself.

Endpoints

  • Start a run: POST /agent/run
  • Get run status and result: GET /agent/run/{id}
  • List all runs: GET /agent/run
  • Cancel or delete a run: DELETE /agent/run/{id}
  • Auth: token query parameter (?token=)
  • Content-Type: application/json
  • Response: application/json
Prerequisites

Quickstart

Submit a task inside the query. The response returns a run ID immediately while the agent runs asynchronously in the background.

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Go to https://example.com and tell me the exact text of the top-level heading."
}'

Response200 OK

{
"id": "run_abc123def456",
"status": "pending"
}

Use the id to poll for status and the result via GET /agent/run/{id}. The query must be at least 10 characters.

Polling for results

Poll GET /agent/run/{id} on an interval until status reaches a terminal value (succeeded, failed, timed_out, or stopped). A single request right after submitting usually returns pending. Each poll returns the steps the agent has taken so far (short, human-readable rationales), so you can watch its progress.

# Repeat this request on an interval until "status" is terminal
# (succeeded, failed, timed_out, or stopped).
curl --request GET \
--url 'https://production-sfo.browserless.io/agent/run/run_abc123def456?token=YOUR_API_TOKEN_HERE'

Response

{
"id": "run_abc123def456",
"status": "succeeded",
"data": {
"answer": "The exact text of the top-level heading is: Example Domain"
},
"error": null,
"steps": [
"Opening the example homepage",
"Reading the main heading",
"Closing the completed browser session"
],
"turns": [
{
"turnId": "turn_0a1b2c3d4e5f",
"query": "Go to https://example.com and tell me the exact text of the top-level heading.",
"status": "succeeded",
"data": {
"answer": "The exact text of the top-level heading is: Example Domain"
},
"steps": ["Opening the example homepage", "Reading the main heading"],
"units": 4
}
],
"created_at": "2025-06-30T10:00:00.000Z",
"completed_at": "2025-06-30T10:00:18.000Z",
"expires_at": "2025-07-07T10:00:18.000Z"
}

By default data is { "answer": "<the agent's answer>" }. When you provide a responseSchema, data is shaped to match it instead. Results are retained for 7 days after completion (see expires_at).

Structured output

Pass a JSON Schema in responseSchema to ask the agent to return its answer in a specific shape. This is best-effort: the agent is instructed to conform to the schema, but conformance is not strictly enforced, so validate the result on your side.

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Go to https://news.ycombinator.com and list the titles of the top 3 stories.",
"responseSchema": {
"type": "object",
"properties": {
"stories": { "type": "array", "items": { "type": "string" } }
},
"required": ["stories"]
}
}'

Guiding the agent

Steer where the agent starts and how far it can wander. startUrl sets the first navigation target, allowedDomains restricts navigation to an allowlist, and maxSteps bounds how many browser actions the agent may take (default 30, hard cap 60).

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Find the current price of the Pro plan.",
"startUrl": "https://www.browserless.io/pricing",
"allowedDomains": ["browserless.io"],
"maxSteps": 15
}'

Browsing hints

proxy, proxyCountry, and stealth configure the browser session the agent drives. The run applies them when it provisions the session (proxy network, exit country, stealth mode) and repeats them in the agent's instructions. They're fixed for the life of the run; the agent can't change them mid-task. solveCaptchas works differently: it tells the agent to use its CAPTCHA-solving tool when it hits one, it is not a session setting.

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Check whether this product is in stock and report the price.",
"startUrl": "https://www.example-shop.com/product/123",
"proxy": "residential",
"proxyCountry": "US",
"stealth": true,
"solveCaptchas": true
}'

Using a profile

Run against authenticated pages by passing a saved profile name in the profile field. The agent's browser loads the profile's cookies, localStorage, and IndexedDB before it starts, so it browses as the logged-in user.

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Open my dashboard and tell me how many open tickets I have.",
"startUrl": "https://app.example.com/dashboard",
"profile": "acme-prod"
}'
tip

Create and manage profiles via the Authenticated Profiles workflow. The profile name is scoped to your API token.

Webhook notifications

Receive a callback when a run finishes instead of polling. There are two events that fire: succeeded (the run finished with a result) and failed (the run failed, timed out, or was stopped). Subscribing to both covers every terminal outcome. Provide an HTTPS URL and, optionally, the subset of events you want.

curl --request POST \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
"query": "Summarize the latest blog post on browserless.io.",
"webhook": {
"url": "https://your-server.com/webhook",
"events": ["succeeded", "failed"]
}
}'

When the run finishes, Browserless POSTs the following payload to your webhook URL. type is succeeded or failed (a timed-out or stopped run is delivered as failed), and success mirrors it as a boolean:

{
"success": true,
"type": "succeeded",
"id": "run_abc123def456",
"data": { "answer": "..." },
"error": null
}

The webhook URL must be https:// and must not resolve to a private or internal network address. If the operator has configured a signing key, deliveries include an X-Browserless-Signature: sha256=<hmac> header you can use to verify authenticity.

Cancelling a run

Send a DELETE with the run ID. A pending or running run is cancelled (its in-flight work is aborted and the run moves to stopped); a run that has already finished is deleted.

Cancel an active run (200):

{
"status": "stopped"
}

Delete a finished run (200):

{
"status": "deleted"
}

If the run reaches a terminal state at the same moment you cancel, the API returns a 409 Conflict:

{
"id": "run_abc123def456",
"status": "succeeded",
"message": "Run is already succeeded"
}
curl --request DELETE \
--url 'https://production-sfo.browserless.io/agent/run/run_abc123def456?token=YOUR_API_TOKEN_HERE'

Listing all runs

List your account's agent runs, newest first. Results are paginated — pass the nextCursor from a response to fetch the next page.

Query parameters

ParameterTypeDefaultDescription
tokenstringYour API token (required).
limitnumber20Results per page (1–100).
cursorstringOpaque pagination cursor from nextCursor in a previous response. A malformed cursor is ignored and the first page is returned.
statusstringFilter by status: "pending", "running", "succeeded", "failed", "timed_out", "stopped", or "capped".
curl --request GET \
--url 'https://production-sfo.browserless.io/agent/run?token=YOUR_API_TOKEN_HERE&limit=20'

Response

{
"runs": [
{
"id": "run_abc123def456",
"status": "succeeded",
"created_at": "2025-06-30T10:00:00.000Z",
"completed_at": "2025-06-30T10:00:18.000Z"
},
{
"id": "run_def456abc789",
"status": "running",
"created_at": "2025-06-30T10:05:00.000Z",
"completed_at": null
}
],
"nextCursor": "eyJpZCI6InJ1bl9hYmMxMjNkZWY0NTYifQ"
}

Request body

POST /agent/run

FieldTypeRequiredDefaultDescription
querystringYesThe natural-language task for the agent. Trimmed; must be at least 10 characters.
timeoutnumberNo900000Per-turn timeout in milliseconds, clamped to [10000, 900000] (15-minute hard cap).
maxStepsnumberNo30Soft cap on browser actions the agent may take, clamped to [1, 60].
responseSchemaobjectNoJSON Schema for the agent's final answer. Best-effort — validate the result on your side.
startUrlstringNoFirst navigation target. Must be an http(s) URL and must not point to a private/internal network address.
allowedDomainsstring[]NoNavigation allowlist passed to the agent.
proxystringNoRoute the agent's browser session through a "residential" or "datacenter" proxy. Applied at session creation.
proxyCountrystringNoTwo-letter ISO country code for the proxy exit (e.g. "US"). Used with proxy.
stealthbooleanNoRun the agent's browser session in stealth / anti-bot-detection mode.
solveCaptchasbooleanNoInstruct the agent to solve CAPTCHAs it encounters (a prompt instruction, not a session setting).
profilestringNoName of a saved authentication profile to load before browsing.
webhookobjectNoWebhook configuration for terminal-state notifications. See below.

Unknown query parameters and unknown body fields are rejected with a 400. proxy, proxyCountry, and stealth configure the agent's browser session; solveCaptchas, startUrl, allowedDomains, and maxSteps steer the agent through its instructions (see Browsing hints).

webhook

FieldTypeRequiredDefaultDescription
urlstringYesHTTPS URL to receive the notification. Cannot point to a private/internal network address.
eventsstring[]No["succeeded", "failed"]Which events to deliver. succeeded fires when the run completes; failed fires when the run fails, times out, or is stopped. Must be a subset of ["succeeded", "failed"].

Response fields

POST /agent/run (start)

FieldTypeDescription
idstringThe unique run ID (prefixed run_). Use it to poll for status and the result.
statusstringInitial run status — "pending".

GET /agent/run/{id} (status and result)

FieldTypeDescription
idstringThe run ID.
statusstringRun status. See Run statuses.
dataobject | nullThe agent's final result from the latest turn: { "answer": string } by default, or your responseSchema shape when provided. null until the run produces a result.
errorstring | nullError message if the run failed. null on success.
stepsstring[]Short human-readable rationales streamed as the agent works.
turnsAgentTurn[]The turn transcript. In v1 a run has a single turn. See below.
created_atstringISO 8601 timestamp when the run was created.
completed_atstring | nullISO 8601 timestamp when the run finished. null while active.
expires_atstring | nullISO 8601 timestamp when the result expires (7 days after completion). null while active.

AgentTurn

FieldTypeDescription
turnIdstringThe turn ID (prefixed turn_).
querystringThe task submitted for this turn.
statusstringTurn status: "running", "succeeded", "failed", or "timed_out" ("capped" is reserved for a future release).
dataobject | nullThe turn's result (same shape as the top-level data).
stepsstring[]Step rationales for this turn.
unitsnumberBilling units charged for this turn, derived from the model tokens it consumed.

GET /agent/run (list all)

FieldTypeDescription
runsAgentRunListItem[]Array of runs, newest first.
nextCursorstring | nullCursor for fetching the next page. null when there are no more results.

AgentRunListItem

FieldTypeDescription
idstringThe run ID.
statusstringRun status.
created_atstringISO 8601 timestamp when the run was created.
completed_atstring | nullISO 8601 timestamp when the run finished. null if still active.

Run statuses

StatusDescription
pendingAccepted and queued; the agent has not started yet.
runningThe agent is actively driving the browser.
succeededThe run completed and produced a result in data.
failedThe run ended with an error (see error).
timed_outThe run exceeded its timeout.
stoppedThe run was cancelled via DELETE.
cappedReserved for a future release (strict cost cap). Not emitted in v1, though it is accepted as a status filter value when listing runs.

Error responses

StatusDescription
400 Bad RequestInvalid body or query parameters, a query shorter than 10 characters, an unknown query/body field, or a task rejected by the relevance/abuse gate (not a browser task, or a prohibited use). Rejected tasks do not create a run.
401 UnauthorizedMissing or invalid API token.
403 ForbiddenThe API key is not account-scoped. Agent runs are account-owned and require an account-scoped key.
404 Not FoundThe run ID does not exist or belongs to another token.
409 Conflict(DELETE) the run reached a terminal state concurrently with your cancel; or the reserved POST /agent/run/{id}/continue endpoint, which is not enabled in v1.
429 Too Many RequestsConcurrent agent-run limit reached for your plan.
503 Service UnavailableThe agent-run service is suspended, unavailable, or temporarily degraded.

FAQ & Troubleshooting

Why am I getting a 401 Unauthorized error?

Your API token is missing or invalid. Include it as a ?token= query parameter. Verify the token in your account dashboard.

Why am I getting a 403 Forbidden error?

Agent runs are account-owned and require an account-scoped API key. If your token is not mapped to an account, the API rejects the request with 403. Contact support if you believe your key should be account-scoped.

My run finished with status succeeded but data.answer is empty

The agent completed but did not produce answer text — often because the task was ambiguous or the target page could not be reached. Refine the query, set a startUrl, or inspect steps to see what the agent did.

My task was rejected with a 400 before a run was created

Every task passes a relevance and abuse gate first. Tasks that don't require a browser (e.g. "write me a poem") and tasks that request a prohibited use are rejected with a 400 and no run is created. Rephrase the task as a concrete browsing objective.

Next steps