Browser
Browser runs
A browser run is a cloud browser agent that does one task, in plain language, on the sites you allow, and answers in text or in JSON matching your answer_schema. A create answers at once with the queued run; read it until its status is final, or run and wait in one call. See Browser runs for what a run can and can't do.
Each run is billed once, when it ends: its Drex Max and LLM tokens plus its browser seconds, at the published prices, never more than limits.max_cost_usd. A run that never starts, times out, or fails on Drex's side isn't charged; one you cancel while it runs is charged its browser seconds so far. Reading, listing and artifacts are free.
| Method | Returns | Description |
|---|---|---|
browserRuns.create(params, options?) | APIPromise<BrowserRunRef> | Starts a run and answers with it queued. |
browserRuns.get(runId, options?) | APIPromise<BrowserRun> | The run: its status and, once it ended, its answer, outcome, steps and usage. |
browserRuns.list(params?, options?) | APIPromise<BrowserRunList> | One page of the account's runs, newest first, without steps. |
browserRuns.iter(params?, options?) | AsyncGenerator<BrowserRun> | Every run, newest first, 50 a page. params takes status. |
browserRuns.cancel(runId, options?) | APIPromise<BrowserRunRef> | Cancels a queued or running run and stops its browser. Cancelling it again answers the same; a run that already ended answers 409 (ConflictError, code run_finished). |
browserRuns.artifacts(runId, options?) | APIPromise<BrowserRunArtifactList> | The artifacts, such as screenshots, the run recorded, with their links. |
browserRuns.run(params, options?) | Promise<BrowserRun> | Creates a run, then polls browserRuns.get until its status is final. See Run and wait. |
browserRuns.create and browserRuns.run take a CreateBrowserRunParams:
| Parameter | Type | Required | Description |
|---|---|---|---|
task | string | Yes | What the agent should do, in plain language: 1 to 4000 characters. |
start_url | string | Yes | An http or https page on a public host, on the default port, whose origin is in allowed_origins. |
allowed_origins | string[] | Yes | The origins (https://shop.example.com, no path) the run may load, 1 to 20. Everything else is blocked. |
answer_schema | Record<string, unknown> | null | No | A JSON Schema (at most 16 KB, without pattern or patternProperties) the answer must match. With one, answer holds the structured answer; a completed answer that doesn't match fails the run as answer_invalid, charged. |
risky_actions | "deny" | No | The agent never submits payments, sends messages or makes other irreversible changes. Default: "deny", the only value. |
limits | BrowserRunLimits | No | How far the run may go. |
model | "default" | No | Default: "default", the only value. |
artifacts | BrowserRunArtifactOptions | No | What the run records besides its answer. |
metadata | Record<string, string> | No | Your own strings, up to 20 of up to 500 characters, returned with the run. |
idempotency_key | string | No | Sent as the Idempotency-Key header, up to 200 characters: the same key and body return the same run, and a different body answers 409 (ConflictError, code idempotency_conflict). Minted per call when omitted, so the client's retries never start a second run. |
BrowserRunLimits field | Type | Description |
|---|---|---|
max_cost_usd | number | The most the run may be charged, in US dollars: 0.01 to 1. Default: 0.04. |
max_seconds | number | How long the run may browse, in seconds: 1 to 1800. Default: 600. |
max_llm_calls | number | How many planning and reading calls the agent may make: 1 to 100. Default: 20. |
BrowserRunArtifactOptions field | Type | Description |
|---|---|---|
screenshots | boolean | Record a screenshot of each step. Default: true. |
har | boolean | Record the run's network traffic as a HAR file. Default: false. |
browserRuns.list takes a BrowserRunListParams:
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | No | 1 to 50. Default: 20. Out of range throws a NaceError. |
status | BrowserRunStatus | No | Only runs with this status. |
cursor | string | null | No | The previous page's next_cursor. Keep going until it's null. |
- Ids: a run id is
brun_and 32 hex characters. Another account's run answers404(NotFoundError, codenot_found), like one that doesn't exist. An empty id, or one with/,?or#, throws aNaceErrorbefore sending. While browser runs aren't available on a deployment, every browser route answers a plain404. - Refusals: a start URL or origin on a banking, payment, identity, large social or government site, or on a host that isn't public, answers
422(UnprocessableEntityError, codeorigin_blocked). An account with zero retention can't create runs (422, codezero_retention_unsupported). More than 20 queued or running runs, or more than $20 of runs in 24 hours, answers429(RateLimitError, coderate_limitedordaily_spend_limit). Without spendable credit, or when a free account, or one with less than $5 of credit and no pay as you go, already has two unfinished runs and document jobs, it answers402(InsufficientCreditError, codeinsufficient_credit). - Retention: a run's task, URLs, answer and steps are erased 48 hours after it ends;
taskthen reads empty.
A BrowserRunRef has id, object ("browser_run"), status and created_at.
A BrowserRun has id, object, status, task, start_url, allowed_origins, risky_actions, limits, answer (the structured answer, or null), answer_text, outcome, final_url, steps (only from get; each a BrowserRunStep the agent reported), pending_approval (always null), usage, artifacts_url, metadata, created_at, started_at and finished_at.
A BrowserRunStatus is queued, then running, then one of succeeded, partial, failed, cancelled or timed_out.
| Constant | Value | Description |
|---|---|---|
BROWSER_RUN_STATUSES | ["queued", "running", "succeeded", "partial", "failed", "cancelled", "timed_out"] | Every run status. |
BROWSER_RUN_FINAL_STATUSES | ReadonlySet of "succeeded", "partial", "failed", "cancelled", "timed_out" | The statuses a run ends in. |
MAX_BROWSER_RUN_PAGE_SIZE | 50 | The largest browserRuns.list limit. |
DEFAULT_BROWSER_RUN_POLL_MS | 2000 | The default pollIntervalMs of browserRuns.run. |
DEFAULT_BROWSER_RUN_TIMEOUT_MS | 3000000 | The default timeoutMs of browserRuns.run. |
A BrowserRunOutcome has kind, reason and evidence, a list of the pages the agent cites. A BrowserRunEvidence has url and text. A BrowserRunOutcomeKind is completed or not_found on a run that succeeded; blocked_login, blocked_captcha, risky_action_denied or limit_reached on a partial one; site_error, internal_error or answer_invalid on a failed one; and cancelled or timed_out.
A BrowserRunUsage has drex_input_tokens, llm_input_tokens, llm_output_tokens, browser_seconds and cost_usd: what the run was charged, null until it ended and 0 when it wasn't charged.
A BrowserRunList has items and next_cursor. A BrowserRunArtifactList has items. A BrowserRunArtifact has kind (such as "screenshot"), step, url and expires_at, when the https link stops working.
const started = await client.browserRuns.create({
task: "Find the price of the blue ceramic mug.",
start_url: "https://shop.example.com/",
allowed_origins: ["https://shop.example.com"],
answer_schema: {
type: "object",
properties: { price: { type: "number" }, currency: { type: "string" } },
required: ["price", "currency"],
},
limits: { max_cost_usd: 0.1, max_seconds: 300 },
metadata: { order: "42" },
});
const latest = await client.browserRuns.get(started.id);
console.log(latest.status, latest.answer, latest.usage?.cost_usd);
const { items } = await client.browserRuns.artifacts(started.id);
for (const artifact of items) console.log(artifact.kind, artifact.url);
for await (const past of client.browserRuns.iter({ status: "succeeded" })) {
console.log(past.id, past.answer_text);
}Run and wait
client.browserRuns.run(params, options?) creates the run once, then reads it with browserRuns.get until its status is one of BROWSER_RUN_FINAL_STATUSES, and returns it. A run that ends failed, partial, cancelled or timed_out is returned, not thrown: read its outcome.
options is a RunBrowserRunOptions: a RequestOptions plus pollIntervalMs and timeoutMs. Each request of the wait takes the request options. pollIntervalMs is the milliseconds between reads of the run, default 2000. timeoutMs is how long to wait for the run to end, in milliseconds: the whole wait, not one request, default 3000000 (50 minutes: a run may wait queued for 15 and browse for 30).
When the time runs out it throws BrowserRunTimeoutError, whose run is the run as last read and timeoutMs the wait budget. The run keeps going: read it again later, or cancel it. A create that fails throws its own error. That run may still have started when the error is ambiguous, such as APIConnectionError after the server accepted the request: pass your own idempotency_key and call run again with the same key and body, which returns that run instead of starting a second billed one. Any other failure after the create, such as a read that fails after its retries or aborting signal, throws BrowserRunWaitError, whose runId is the run that started and cause the error that stopped the wait. That run keeps going and is billed when it ends: read it with browserRuns.get by that id, or cancel it, since calling run again starts another.
import { BrowserRunTimeoutError, BrowserRunWaitError } from "nace-sdk";
try {
const finished = await client.browserRuns.run(
{
task: "What are the store's opening hours on Saturday?",
start_url: "https://shop.example.com/contact",
allowed_origins: ["https://shop.example.com"],
},
{ pollIntervalMs: 3_000, timeoutMs: 600_000 },
);
console.log(finished.status, finished.outcome?.kind, finished.answer_text);
} catch (err) {
if (err instanceof BrowserRunTimeoutError) {
await client.browserRuns.cancel(err.run.id);
} else if (err instanceof BrowserRunWaitError) {
console.error(`run ${err.runId} is still going`, err.cause);
} else {
throw err;
}
}