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
idstringUnique id, brun_ followed by 24 characters.
objectstringAlways browser_run.
statusstringrunning, waiting_for_human, succeeded, failed, timed_out or stopped.
goalstringThe goal you sent.
start_urlstringThe URL the run started on.
enginestringAlways chromium.
modestringfast or accurate.
modelstring | nullThe model you chose with model, or null for the included models.
model_usdnumberOnly when you chose a model: what its tokens have cost so far, including the 20% surcharge.
stepsintegerSteps recorded so far, including pauses for a person. Pauses are not billed.
max_stepsintegerThe run's step limit.
billable_time_sintegerSeconds beyond each step's and each pause's included minute, so far.
time_usdnumberThe cost of billable_time_s, at $0.005 per minute.
current_urlstringThe page the browser is on, or was on when the run ended.
answerstring | nullThe agent's answer once it finishes. Runs that reach max_steps also get an answer from the last page.
dataobject | array | nullStructured output once the run ends, when you passed output_schema or the goal asked for a structure. See Structured output.
data_errorstringOnly present when structured output failed, with the reason.
errorstring | nullWhy 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 | nullWhat the run is waiting for, when status is waiting_for_human. Otherwise null.
profile_savedobject | nullWith save_profile, the profile saved when the run ended: id, name and cookies (the number of cookies saved).
usageobjectdecision_calls and llm_calls: how many model calls the agent made. Informational; included in the step price.
created_atstringWhen the run started (ISO 8601).
finished_atstring | nullWhen the run ended.
tracearrayOnly 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 | nullAPI only. What the run cost, once it is settled a few seconds after it ends. null before.
reserved_usdnumberAPI only. The amount held while the run is live; 0 once it is settled.
{
"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#
/v1/browser/runsStarts 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
goalstringRequiredWhat to do, in plain words, with every value the task needs. Up to 4,000 characters. See Write goals that work.
start_urlstringRequiredThe first page to open. An http or https URL.
modestringDefault fastfast ($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 50Step limit, 1 to 100. Values outside the range are clamped. The run is billed only for the steps it takes.
output_schemaobjectJSON Schema (draft 2020-12), or shorthand like {"title": "string", "items": [{"name": "string"}]}, for JSON in data. Up to 20 KB.
approvestringDefault riskyWhen 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 trueWhether 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.
secretsarrayNames of vault secrets the agent may type.
ephemeral_secretsobjectSecrets for this run only, deleted when it ends. A map from name (same rules as vault names) to:
filesarray | stringNames or ids of uploaded files the agent may attach, or "*" for all of them.
profilestringName or id of a saved profile to start from.
save_profilestringSave the session's cookies and local storage under this name when the run ends. Up to 64 characters.
allowed_domainsarrayHostnames the browser may visit, like "example.com" or "*.example.com". Up to 50.
webhookstringAn https URL that receives run.waiting and run.finished events.
modelstringA 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_usdnumberWith model: the most its tokens may cost, surcharge included. Defaults to an estimate from max_steps and the model's prices.
streambooleanDefault falsetrue returns the run's event stream (text/event-stream) instead of the 202 JSON.
screenbooleanDefault falseWith 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/"
}'{
"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#
/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 0Seconds to hold the request while the run is running, up to 50.
tracestring1 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#
/v1/browser/runs/{id}/eventsReturns 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
screenstring1 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.
| Event | data |
|---|---|
run | The run object with charge_usd and reserved_usd; the first has trace |
activity | step, kind (starting, reading, waiting, deciding, thinking, checking, acting, answering, extracting) and text |
step | One trace entry |
frame | data (base64 JPEG) and at |
end | id |
curl -N "https://lovelyrouter.com/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d/events?screen=1" \
-H "Authorization: Bearer $LOVELY_API_KEY"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#
/v1/browser/runsReturns your 50 most recent browser agent runs, newest first, in a short form. Retrieve a run for its full state.
| Field | Meaning |
|---|---|
id | The run id |
billing_status | running until the run is settled, then succeeded, failed, timed_out or stopped |
goal | The first 200 characters of the goal |
steps | Billed steps, 0 until the run is settled |
created_at | When the run started |
curl https://lovelyrouter.com/v1/browser/runs \
-H "Authorization: Bearer $LOVELY_API_KEY"{
"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#
/v1/browser/runs/{id}/replyAnswers 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
answerstringFor a question: text the agent reads. Up to 2,000 characters.
secretstringFor 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.
approvebooleanFor an approval: true to let the agent click the button, false to refuse.
denybooleanFor an approval: true is the same as "approve": false.
reasonstringWith a refusal: why, for the agent to read. Up to 500 characters.
donebooleanFor a takeover: true when the person has finished. The agent continues from the current page.
failedbooleanFor a takeover: true when the person couldn't finish.
notestringWith 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 https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"secret": "481923"}'{"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#
/v1/browser/runs/{id}/takeoverAsks 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
instructionsstringShown as the pause's prompt. Up to 500 characters. Default: The user asked to take over the browser.
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#
/v1/browser/runs/{id}/stopStops 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 -X POST https://lovelyrouter.com/v1/browser/runs/brun_3f9a1c7e0b2d4e6f8a9b0c1d/stop \
-H "Authorization: Bearer $LOVELY_API_KEY"Get a screenshot#
/v1/browser/runs/{id}/screenshotReturns 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 https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-o screen.jpgStore a secret#
/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
namestringRequired1 to 64 characters: lowercase a-z, 0-9, _, ., -, starting with a letter or digit.
Body parameters
valuestringRequiredThe secret, up to 4 KB (larger values return 413). For totp: the base32 seed (16+ characters of A-Z, 2-7).
kindstringDefault passwordpassword (password fields only), username or text (text-like fields, not password fields), or totp (typed as the current 6-digit code).
originsarrayRequired1 to 20 origins like https://github.com or https://*.example.com. https only, except http://localhost and http://127.0.0.1.
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"]}'{ "name": "github_password", "kind": "password", "origins": ["https://github.com"], "stored": true }curl https://lovelyrouter.com/v1/browser/vault \
-H "Authorization: Bearer $LOVELY_API_KEY"{
"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#
/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 -X DELETE https://lovelyrouter.com/v1/browser/vault/github_password \
-H "Authorization: Bearer $LOVELY_API_KEY"{ "deleted": "github_password" }Upload a file#
/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
namestringRequiredIn the path, URL-encoded. 1 to 128 characters: letters, digits, spaces and . _ - ( ) + ,, starting with a letter, digit or _.
descriptionstringQuery 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 -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{
"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#
/v1/browser/filesThe same as Upload a file, as multipart/form-data. Returns the same file object.
Form fields
filefileRequiredThe file. Its type comes from the part's content type.
namestringThe name to store it under. Defaults to the uploaded file's name.
descriptionstringWhat the file is. Up to 300 characters.
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#
/v1/browser/filesLists your uploaded files, sorted by name. last_used_at is when a run last attached the file.
curl https://lovelyrouter.com/v1/browser/files \
-H "Authorization: Bearer $LOVELY_API_KEY"{
"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#
/v1/browser/files/{name}Deletes a file by name or id. Returns 404 with No such file if there is none.
curl -X DELETE https://lovelyrouter.com/v1/browser/files/Maria_Garcia_CV_2026.pdf \
-H "Authorization: Bearer $LOVELY_API_KEY"{ "deleted": "Maria_Garcia_CV_2026.pdf" }List profiles#
/v1/browser/profilesLists 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 https://lovelyrouter.com/v1/browser/profiles \
-H "Authorization: Bearer $LOVELY_API_KEY"{
"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#
/v1/browser/profiles/{id}Deletes a profile by id or name. Returns 404 with No such profile if there is none.
curl -X DELETE https://lovelyrouter.com/v1/browser/profiles/shop-login \
-H "Authorization: Bearer $LOVELY_API_KEY"{ "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.
| Status | type | When |
|---|---|---|
400 | invalid_request_error | A body or parameter is invalid, e.g. "goal" is required (max 4,000 characters), Unknown vault secret(s): …, Unknown file(s): …, Unknown profile "…". |
401 | authentication_error | The API key is missing, invalid, revoked or expired. |
402 | insufficient_quota | Your balance is below the run's hold, or the hold would pass the key's spend limit. |
404 | not_found | No such run, secret, file, profile or screenshot in your account. |
409 | invalid_request_error | The run isn't waiting for this reply, has finished, or you have reached 100 files. |
413 | invalid_request_error | A file is larger than 10 MB, or a vault secret larger than 4 KB. |
429 | rate_limit_exceeded | 10 runs (browser agent and data tool runs together) are already running. |
502 | tool_unavailable | The run could not start. You were not charged; try again. |
503 | tool_unavailable | The browser agent is temporarily unavailable. |
{
"error": {
"type": "invalid_request_error",
"message": "\"start_url\" must be an http(s) URL"
}
}