Lovely RouterDocs
Get a key
API reference

Browser endpoints

Every /v1/browser endpoint for runs, vault secrets, files and profiles, with parameters, limits, responses and errors.

The browser agent API lives under https://lovelyrouter.com/v1/browser. Authenticate every request with your API key as Authorization: Bearer $LOVELY_API_KEY (Authentication). Request and response bodies are JSON, except file uploads and screenshots. Runs are billed from your prepaid balance; see Modes, models & pricing.

For a walkthrough, start with the browser agent overview.

The run object#

A run is one task for the agent. Every run endpoint returns this object. Fields marked API only are missing from webhook payloads.

Attributes

idstring

Unique id, brun_ followed by 24 characters.

objectstring

Always browser_run.

statusstring

running, waiting_for_human, succeeded, failed, timed_out or stopped.

goalstring

The goal you sent.

start_urlstring

The URL the run started on.

enginestring

Always chromium.

modestring

fast or accurate.

modelstring | null

The model you chose with model, or null for the included models.

model_usdnumber

Only when you chose a model: what its tokens have cost so far, including the 20% surcharge.

stepsinteger

Steps recorded so far, including pauses for a person. Pauses are not billed.

max_stepsinteger

The run's step limit.

billable_time_sinteger

Seconds beyond each step's and each pause's included minute, so far.

time_usdnumber

The cost of billable_time_s, at $0.005 per minute.

current_urlstring

The page the browser is on, or was on when the run ended.

answerstring | null

The agent's answer once it finishes. Runs that reach max_steps also get an answer from the last page.

dataobject | array | null

Structured output once the run ends, when you passed output_schema or the goal asked for a structure. See Structured output.

data_errorstring

Only present when structured output failed, with the reason.

errorstring | null

Why the run did not succeed, e.g. reached the step limit (50), blocked by the website, no human response within 30 minutes, stopped by the user.

needsobject | null

What the run is waiting for, when status is waiting_for_human. Otherwise null.

profile_savedobject | null

With save_profile, the profile saved when the run ended: id, name and cookies (the number of cookies saved).

usageobject

decision_calls and llm_calls: how many model calls the agent made. Informational; included in the step price.

created_atstring

When the run started (ISO 8601).

finished_atstring | null

When the run ended.

tracearray

Only with ?trace=1. One entry per step: step, url, action (such as click:e12, type:e3, choose:e5, secret:e4:shop_password, upload:e9, back, done, or ask, approval, handoff for pauses), description, ms (duration) and error (null, or why the step failed).

charge_usdnumber | null

API only. What the run cost, once it is settled a few seconds after it ends. null before.

reserved_usdnumber

API only. The amount held while the run is live; 0 once it is settled.

The run object
{
  "id": "brun_3f9a1c7e0b2d4e6f8a9b0c1d",
  "object": "browser_run",
  "status": "succeeded",
  "goal": "Find the price of the book \"A Light in the Attic\" and whether it is in stock.",
  "start_url": "https://books.toscrape.com/",
  "engine": "chromium",
  "mode": "fast",
  "model": null,
  "steps": 2,
  "max_steps": 50,
  "billable_time_s": 0,
  "time_usd": 0,
  "current_url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
  "answer": "\"A Light in the Attic\" costs £51.77 and is in stock (22 available).",
  "data": null,
  "error": null,
  "needs": null,
  "profile_saved": null,
  "usage": { "decision_calls": 3, "llm_calls": 1 },
  "created_at": "2026-10-11T09:30:00.000Z",
  "finished_at": "2026-10-11T09:30:07.000Z",
  "trace": [
    {
      "step": 1,
      "url": "https://books.toscrape.com/",
      "action": "click:e54",
      "description": "click link \"A Light in the ...\" → https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
      "ms": 2140,
      "error": null
    },
    {
      "step": 2,
      "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
      "action": "done",
      "description": "Read the result and wrote the answer",
      "ms": 2875,
      "error": null
    }
  ],
  "charge_usd": 0.012,
  "reserved_usd": 0
}

Create a run#

POST/v1/browser/runs

Starts a run and returns 202 with the run object at once. The agent works in the background; follow it with its event stream, Retrieve a run or a webhook. Starting a run reserves its maximum cost from your balance (how the hold works).

The response also has poll, a ready-made status path with ?wait=30, events, the run's event stream, and webhook_secret when you passed webhook. webhook_secret is returned only here (or in the first event of a streamed start).

With "stream": true, the response is instead 200 with the run's event stream, from the start until the run ends. See Live events & screen.

Body parameters

goalstringRequired

What to do, in plain words, with every value the task needs. Up to 4,000 characters. See Write goals that work.

start_urlstringRequired

The first page to open. An http or https URL.

modestringDefault fast

fast ($0.006 per step) or accurate ($0.012 per step: also looks at screenshots when unsure and can run read-only page code). See Modes.

max_stepsintegerDefault 50

Step limit, 1 to 100. Values outside the range are clamped. The run is billed only for the steps it takes.

output_schemaobject

JSON Schema (draft 2020-12), or shorthand like {"title": "string", "items": [{"name": "string"}]}, for JSON in data. Up to 20 KB.

approvestringDefault risky

When to pause for approval before an action that submits something (a submit button, a buy/book/pay/send/delete button, or Enter in a non-search form): risky (only purchases, orders, sending, deleting, applications), submit (every such action) or none. Any other value returns 400.

humanbooleanDefault true

Whether the agent may ask questions and hand the browser over. Set false for unattended runs.

human_timeout_minutesnumberDefault 30` (`5` for a run started by a cloud agent)

How long each pause waits for a reply, 1 to 1,440 minutes. Values outside the range are clamped. Then the run ends as timed_out.

secretsarray

Names of vault secrets the agent may type.

ephemeral_secretsobject

Secrets for this run only, deleted when it ends. A map from name (same rules as vault names) to:

filesarray | string

Names or ids of uploaded files the agent may attach, or "*" for all of them.

profilestring

Name or id of a saved profile to start from.

save_profilestring

Save the session's cookies and local storage under this name when the run ends. Up to 64 characters.

allowed_domainsarray

Hostnames the browser may visit, like "example.com" or "*.example.com". Up to 50.

webhookstring

An https URL that receives run.waiting and run.finished events.

modelstring

A model id from GET /v1/models, e.g. claude-sonnet-5.5, for the agent's thinking, writing and extraction. Its tokens are billed at the normal price plus 20%.

max_model_usdnumber

With model: the most its tokens may cost, surcharge included. Defaults to an estimate from max_steps and the model's prices.

streambooleanDefault false

true returns the run's event stream (text/event-stream) instead of the 202 JSON.

screenbooleanDefault false

With stream: add frame events with the browser's screen.

curl https://lovelyrouter.com/v1/browser/runs \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "goal": "Find the price of the book \"A Light in the Attic\" and whether it is in stock.",
    "start_url": "https://books.toscrape.com/"
  }'
Response
{
  "id": "brun_3f9a1c7e0b2d4e6f8a9b0c1d",
  "object": "browser_run",
  "status": "running",
  "goal": "Find the price of the book \"A Light in the Attic\" and whether it is in stock.",
  "start_url": "https://books.toscrape.com/",
  "engine": "chromium",
  "mode": "fast",
  "model": null,
  "steps": 0,
  "max_steps": 50,
  "billable_time_s": 0,
  "time_usd": 0,
  "current_url": "https://books.toscrape.com/",
  "answer": null,
  "data": null,
  "error": null,
  "needs": null,
  "profile_saved": null,
  "usage": { "decision_calls": 0, "llm_calls": 0 },
  "created_at": "2026-10-11T09:30:00.000Z",
  "finished_at": null,
  "poll": "/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d?wait=30",
  "events": "/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d/events",
  "charge_usd": null,
  "reserved_usd": 0.445
}

Retrieve a run#

GET/v1/browser/runs/{id}

Returns the run object. With wait, the request holds until the run's status (or the kind of pause) changes, or until wait seconds pass, then returns the current state. While a run is waiting_for_human, it returns at once.

Query parameters

waitintegerDefault 0

Seconds to hold the request while the run is running, up to 50.

tracestring

1 adds trace, the list of steps so far.

curl "https://lovelyrouter.com/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d?wait=30&trace=1" \
  -H "Authorization: Bearer $LOVELY_API_KEY"

The response is the run object, with trace when you pass trace=1.

Stream a run#

GET/v1/browser/runs/{id}/events

Returns the run's live events as Server-Sent Events (text/event-stream) until the run ends. The first event is the run object with trace; then activity (what the agent is doing now), step (each finished step), run (after every step and when a pause begins or ends) and, with screen, frame (the browser's screen). The last events are the finished run, with charge_usd, and end. A finished run sends its run event and end at once. Lines starting with : are keep-alive comments. See Live events & screen.

Query parameters

screenstring

1 adds frame events: data (a base64 JPEG of the browser) and at. One on connect, then whenever the page changes, at most about 4 a second.

Eventdata
runThe run object with charge_usd and reserved_usd; the first has trace
activitystep, kind (starting, reading, waiting, deciding, thinking, checking, acting, answering, extracting) and text
stepOne trace entry
framedata (base64 JPEG) and at
endid
cURL
curl -N "https://lovelyrouter.com/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d/events?screen=1" \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
event: run
data: {"id":"brun_3f9a1c7e0b2d4e6f8a9b0c1d","object":"browser_run","status":"running","steps":1,"trace":[…],…}

event: frame
data: {"data":"/9j/4AAQSkZJRgABAQ…","at":"2026-10-11T09:30:03.120Z"}

event: activity
data: {"step":2,"kind":"answering","text":"Reading the result and writing the answer"}

event: step
data: {"step":2,"url":"https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html","action":"done","description":"Read the result and wrote the answer","ms":2875,"error":null}

event: run
data: {"id":"brun_3f9a1c7e0b2d4e6f8a9b0c1d","status":"succeeded","answer":"\"A Light in the Attic\" costs £51.77 and is in stock (22 available).","charge_usd":0.012,…}

event: end
data: {"id":"brun_3f9a1c7e0b2d4e6f8a9b0c1d"}

List runs#

GET/v1/browser/runs

Returns your 50 most recent browser agent runs, newest first, in a short form. Retrieve a run for its full state.

FieldMeaning
idThe run id
billing_statusrunning until the run is settled, then succeeded, failed, timed_out or stopped
goalThe first 200 characters of the goal
stepsBilled steps, 0 until the run is settled
created_atWhen the run started
cURL
curl https://lovelyrouter.com/v1/browser/runs \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "brun_3f9a1c7e0b2d4e6f8a9b0c1d",
      "billing_status": "succeeded",
      "goal": "Find the price of the book \"A Light in the Attic\" and whether it is in stock.",
      "steps": 2,
      "created_at": "2026-10-11T09:30:00.000Z"
    }
  ]
}

Reply to a run#

POST/v1/browser/runs/{id}/reply

Answers a run that is waiting_for_human. Send exactly one kind of reply, matching needs.kind. Returns 200 with the run object, its status back to running. See Human in the loop.

Body parameters

answerstring

For a question: text the agent reads. Up to 2,000 characters.

secretstring

For a question: a value the agent types on the current site without seeing it. Up to 4,096 characters. It can go into any text-like field there, password and PIN fields included.

approveboolean

For an approval: true to let the agent click the button, false to refuse.

denyboolean

For an approval: true is the same as "approve": false.

reasonstring

With a refusal: why, for the agent to read. Up to 500 characters.

doneboolean

For a takeover: true when the person has finished. The agent continues from the current page.

failedboolean

For a takeover: true when the person couldn't finish.

notestring

With done or failed: what the person did, for the agent to read. Up to 500 characters.

Errors: 400 when the body matches no reply shape, 409 This run is not waiting for a reply, and 409 This run is waiting for a reply of kind "question" (or approval, takeover) when the reply doesn't match the pause.

cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"secret": "481923"}'
Reply bodies
{"answer": "Use the billing address."}
{"approve": true}
{"approve": false, "reason": "Wrong delivery date"}
{"done": true, "note": "Solved the CAPTCHA"}
{"failed": true, "note": "The site asked for a phone call"}

Take over a run#

POST/v1/browser/runs/{id}/takeover

Asks the run to hand you the browser after the step in progress. Returns 202 with the run object. The next status shows waiting_for_human with needs.kind takeover and a live_view_url. Hand back with a {"done": true} reply.

If the run is waiting for a question or an approval, the takeover starts after you reply to it. A finished run returns 409 with This run has finished.

Body parameters

instructionsstring

Shown as the pause's prompt. Up to 500 characters. Default: The user asked to take over the browser.

cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/takeover \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"instructions": "I will pick the delivery slot myself."}'

Stop a run#

POST/v1/browser/runs/{id}/stop

Stops a run. It ends as stopped at the next step boundary, or right away if it is waiting for a person. Returns 202 with the run object as it was; the next status shows stopped. A stopped run pays for the steps it took and returns no data.

cURL
curl -X POST https://lovelyrouter.com/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d/stop \
  -H "Authorization: Bearer $LOVELY_API_KEY"

Get a screenshot#

GET/v1/browser/runs/{id}/screenshot

Returns a JPEG (image/jpeg) of the browser. While the run has its browser open, that is the screen as it is now; otherwise the latest one kept: the page when a pause for a person began, or the final page of a finished run. needs.screenshot holds this path. Returns 404 with No screenshot yet when there is none. To watch the screen continuously, stream the run with screen=1.

cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -o screen.jpg

Store a secret#

PUT/v1/browser/vault/{name}

Stores a write-only secret, or replaces the one with this name. The agent can type it only on the listed origins, and the model never sees it. See Logins, secrets & profiles.

Path parameters

namestringRequired

1 to 64 characters: lowercase a-z, 0-9, _, ., -, starting with a letter or digit.

Body parameters

valuestringRequired

The secret, up to 4 KB (larger values return 413). For totp: the base32 seed (16+ characters of A-Z, 2-7).

kindstringDefault password

password (password fields only), username or text (text-like fields, not password fields), or totp (typed as the current 6-digit code).

originsarrayRequired

1 to 20 origins like https://github.com or https://*.example.com. https only, except http://localhost and http://127.0.0.1.

cURL
curl -X PUT https://lovelyrouter.com/v1/browser/vault/github_password \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"value": "correct-horse-battery-staple", "kind": "password", "origins": ["https://github.com"]}'
Response
{ "name": "github_password", "kind": "password", "origins": ["https://github.com"], "stored": true }

List secrets#

GET/v1/browser/vault

Lists your secrets by name, without values.

cURL
curl https://lovelyrouter.com/v1/browser/vault \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "name": "github_password",
      "kind": "password",
      "origins": ["https://github.com"],
      "created_at": "2026-10-11T09:00:00.000Z",
      "updated_at": "2026-10-11T09:00:00.000Z",
      "last_used_at": "2026-10-11T09:12:31.000Z"
    }
  ]
}

Delete a secret#

DELETE/v1/browser/vault/{name}

Deletes a secret. Runs that list it can no longer type it. Returns 404 with No such secret if there is none.

cURL
curl -X DELETE https://lovelyrouter.com/v1/browser/vault/github_password \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{ "deleted": "github_password" }

Upload a file#

PUT/v1/browser/files/{name}

Uploads a file the agent may attach to forms, or replaces the file with this name (keeping its id). Send the raw bytes as the body and the file type as Content-Type. See File uploads.

Path and query parameters

namestringRequired

In the path, URL-encoded. 1 to 128 characters: letters, digits, spaces and . _ - ( ) + ,, starting with a letter, digit or _.

descriptionstring

Query parameter. What the file is, to help the agent choose. Up to 300 characters.

Files are limited to 10 MB (413) and 100 per account (409 file limit reached (100); delete some first).

cURL
curl -X PUT "https://lovelyrouter.com/v1/browser/files/Maria_Garcia_CV_2026.pdf?description=Resume" \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/pdf" \
  --data-binary @Maria_Garcia_CV_2026.pdf
Response
{
  "id": "file_7a3c9e1b5d2f8a4c6e0b",
  "name": "Maria_Garcia_CV_2026.pdf",
  "mime": "application/pdf",
  "bytes": 84213,
  "sha256": "5f2b8c…",
  "description": "Resume",
  "created_at": "2026-10-11T09:20:44.000Z",
  "last_used_at": null
}

Upload a file with a form#

POST/v1/browser/files

The same as Upload a file, as multipart/form-data. Returns the same file object.

Form fields

filefileRequired

The file. Its type comes from the part's content type.

namestring

The name to store it under. Defaults to the uploaded file's name.

descriptionstring

What the file is. Up to 300 characters.

cURL
curl https://lovelyrouter.com/v1/browser/files \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -F "file=@Maria_Garcia_CV_2026.pdf;type=application/pdf" \
  -F "description=Resume"

List files#

GET/v1/browser/files

Lists your uploaded files, sorted by name. last_used_at is when a run last attached the file.

cURL
curl https://lovelyrouter.com/v1/browser/files \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "file_7a3c9e1b5d2f8a4c6e0b",
      "name": "Maria_Garcia_CV_2026.pdf",
      "mime": "application/pdf",
      "bytes": 84213,
      "sha256": "5f2b8c…",
      "description": "Resume",
      "created_at": "2026-10-11T09:20:44.000Z",
      "last_used_at": "2026-10-11T09:47:10.000Z"
    }
  ]
}

Delete a file#

DELETE/v1/browser/files/{name}

Deletes a file by name or id. Returns 404 with No such file if there is none.

cURL
curl -X DELETE https://lovelyrouter.com/v1/browser/files/Maria_Garcia_CV_2026.pdf \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{ "deleted": "Maria_Garcia_CV_2026.pdf" }

List profiles#

GET/v1/browser/profiles

Lists your saved sessions, most recently updated first. A run creates or updates a profile with save_profile and starts from one with profile. origins are the sites the profile holds cookies or local storage for; bytes is its size.

cURL
curl https://lovelyrouter.com/v1/browser/profiles \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "prof_4c1e9a7b2d0f3e5a8b6c",
      "name": "shop-login",
      "origins": ["https://shop.example.com"],
      "bytes": 18342,
      "created_at": "2026-10-10T16:02:11.000Z",
      "updated_at": "2026-10-11T09:15:40.000Z",
      "last_used_at": "2026-10-11T09:14:58.000Z"
    }
  ]
}

Delete a profile#

DELETE/v1/browser/profiles/{id}

Deletes a profile by id or name. Returns 404 with No such profile if there is none.

cURL
curl -X DELETE https://lovelyrouter.com/v1/browser/profiles/shop-login \
  -H "Authorization: Bearer $LOVELY_API_KEY"
Response
{ "deleted": "shop-login" }

Errors#

Errors return a JSON body with a type and a human-readable message. Messages name the field at fault. See also Errors.

StatustypeWhen
400invalid_request_errorA body or parameter is invalid, e.g. "goal" is required (max 4,000 characters), Unknown vault secret(s): …, Unknown file(s): …, Unknown profile "…".
401authentication_errorThe API key is missing, invalid, revoked or expired.
402insufficient_quotaYour balance is below the run's hold, or the hold would pass the key's spend limit.
404not_foundNo such run, secret, file, profile or screenshot in your account.
409invalid_request_errorThe run isn't waiting for this reply, has finished, or you have reached 100 files.
413invalid_request_errorA file is larger than 10 MB, or a vault secret larger than 4 KB.
429rate_limit_exceeded10 runs (browser agent and data tool runs together) are already running.
502tool_unavailableThe run could not start. You were not charged; try again.
503tool_unavailableThe browser agent is temporarily unavailable.
Error
{
  "error": {
    "type": "invalid_request_error",
    "message": "\"start_url\" must be an http(s) URL"
  }
}