Documentation Index
Fetch the complete documentation index at: https://docs.pre.dev/llms.txt
Use this file to discover all available pages before exploring further.
If you’re running Browser Use Cloud today, switching to pre.dev Browser Agents is an SDK swap + an env-var rename. Same task shape (URL + instruction + schema), same structured JSON out.
On our public benchmark, pre.dev passes 100/100 vs Browser Use Cloud’s 97/100 — and is 2.9× cheaper and 2.8× faster (0.0129vs0.0372 per task, 12.7s vs 35.7s avg).
1. Swap env vars + install the SDK
npm uninstall browser-use-sdk
npm install predev-api
# Replace in .env, .env.example, CI, docker-compose, etc.
# BROWSER_USE_API_KEY=... → PREDEV_API_KEY=...
pip uninstall browser-use-sdk
pip install predev-api
# Replace in .env, .env.example, CI, docker-compose, etc.
# BROWSER_USE_API_KEY=... → PREDEV_API_KEY=...
Grab your pre.dev key at pre.dev/projects/browser-agents.
2. Run one task
Before — Browser Useimport { BrowserUse } from 'browser-use-sdk/v3';
const client = new BrowserUse({ apiKey: process.env.BROWSER_USE_API_KEY! });
const response = await client.run(
'Extract the H1 from https://example.com. Return JSON { heading: string }.',
{ llm: 'bu-mini' },
);
const data = JSON.parse(response.output);
After — pre.devimport { PredevAPI } from 'predev-api';
const client = new PredevAPI({ apiKey: process.env.PREDEV_API_KEY! });
const result = await client.browserAgent([
{
url: 'https://example.com',
instruction: 'Extract the H1.',
output: {
type: 'object',
properties: { heading: { type: 'string' } },
required: ['heading'],
},
},
]);
const data = result.results[0].data; // { heading: 'Example Domain' }
Before — Browser Useimport os, json
from browser_use_sdk import BrowserUse
client = BrowserUse(api_key=os.environ["BROWSER_USE_API_KEY"])
response = client.run(
"Extract the H1 from https://example.com. Return JSON { heading: string }.",
llm="bu-mini",
)
data = json.loads(response.output)
After — pre.devimport os
from predev_api import PredevAPI
client = PredevAPI(api_key=os.environ["PREDEV_API_KEY"])
result = client.browser_agent([
{
"url": "https://example.com",
"instruction": "Extract the H1.",
"output": {
"type": "object",
"properties": {"heading": {"type": "string"}},
"required": ["heading"],
},
}
])
data = result["results"][0]["data"] # {'heading': 'Example Domain'}
Key differences:
- URL is a first-class field, not embedded in the instruction. The agent navigates there before running.
- Output schema is JSON Schema, not a serialized Pydantic/Zod model. The runner validates the response and retries on schema-failure.
- Tasks is always an array — one method covers 1 task or 1,000.
3. Run many tasks in parallel
Browser Use makes you fire N concurrent run() calls yourself. pre.dev takes an array and fans out server-side — one request, one response.
Before — Browser Use (manual fan-out)const urls = ['https://news.ycombinator.com', 'https://lobste.rs', 'https://reddit.com/r/programming'];
const responses = await Promise.all(
urls.map((url) =>
client.run(`Extract the top 5 story titles from ${url}. Return JSON { titles: string[] }.`, { llm: 'bu-mini' }),
),
);
const data = responses.map((r) => JSON.parse(r.output));
After — pre.dev (one call, server-side concurrency)const result = await client.browserAgent(
urls.map((url) => ({
url,
instruction: 'Extract the top 5 story titles.',
output: {
type: 'object',
properties: { titles: { type: 'array', items: { type: 'string' } } },
required: ['titles'],
},
})),
{ concurrency: 3 },
);
const data = result.results.map((r) => r.data);
Before — Browser Use (manual fan-out)import asyncio, json
urls = ['https://news.ycombinator.com', 'https://lobste.rs', 'https://reddit.com/r/programming']
async def one(url):
r = await client.run(f"Extract the top 5 story titles from {url}. Return JSON {{titles: string[]}}.", llm="bu-mini")
return json.loads(r.output)
data = await asyncio.gather(*(one(u) for u in urls))
After — pre.dev (one call, server-side concurrency)result = client.browser_agent(
[
{
"url": url,
"instruction": "Extract the top 5 story titles.",
"output": {
"type": "object",
"properties": {"titles": {"type": "array", "items": {"type": "string"}}},
"required": ["titles"],
},
}
for url in urls
],
concurrency=3,
)
data = [r["data"] for r in result["results"]]
Up to 1,000 tasks per request. See Run a Task for the full schema.
4. Async + poll pattern
If you were polling Browser Use by session id, pre.dev has a direct equivalent: pass async: true on submit, then call getBrowserAgent(id).
Before — Browser Useconst handle = client.run(task, { llm: 'bu-mini' });
const sessionId = handle.sessionId;
while (true) {
const status = await client.sessions.get(sessionId);
if (status.state === 'finished') break;
await new Promise((r) => setTimeout(r, 2000));
}
After — pre.devconst { id } = await client.browserAgent(
[{ url, instruction, output }],
{ async: true },
);
while (true) {
const batch = await client.getBrowserAgent(id);
if (batch.status === 'completed' || batch.status === 'failed') break;
await new Promise((r) => setTimeout(r, 2000));
}
Before — Browser Useimport time
handle = client.run(task, llm="bu-mini")
session_id = handle.session_id
while True:
status = client.sessions.get(session_id)
if status.state == "finished":
break
time.sleep(2)
After — pre.devimport time
batch = client.browser_agent(
[{"url": url, "instruction": instruction, "output": output}],
run_async=True,
)
while True:
state = client.get_browser_agent(batch["id"])
if state["status"] in ("completed", "failed"):
break
time.sleep(2)
See Task Status for the full polling contract (pass includeEvents=True for the per-step timeline).
One-shot Claude Code prompt
Drop this into Claude Code at the root of your repo. Works for any stack — Node, Bun, Deno, Python, monorepos.
Migrate this codebase from Browser Use Cloud to pre.dev Browser Agents.
## Why
pre.dev Browser Agents is a drop-in replacement for Browser Use Cloud's
task API: same URL + instruction + JSON-schema contract, structured JSON
output, 2.9× cheaper AND 2.8× faster at 100/100 pass on the public
benchmark (vs Browser Use's 97/100). Docs:
https://docs.pre.dev/browser-agents/migrate-from-browser-use
## Do this
1. **Find call sites.** Search the entire repo (not just `src/`) for:
- Imports: `browser-use-sdk`, `from browser_use`, `BrowserUse`,
`browser_use_sdk`.
- Env refs: `BROWSER_USE_API_KEY` in code, `.env`, `.env.example`,
`.env.*`, CI configs (`.github/workflows/*`, `render.yaml`,
`vercel.json`, `docker-compose*.yml`, `Dockerfile*`, `fly.toml`),
README, internal docs.
- Any `cloud.browser-use.com` or `api.browser-use.com` URLs.
2. **Swap the SDK.**
- Node/TS/Bun: replace `browser-use-sdk` dependency with
`predev-api` in `package.json`, `bun.lockb`, `yarn.lock`, or
`pnpm-lock.yaml` (let the user run the install).
- Python: replace `browser-use-sdk` with `predev-api` in
`requirements.txt`, `pyproject.toml`, `poetry.lock`, or
`Pipfile` (let the user run the install).
3. **Rewrite calls** per these rules. Full mapping at
https://docs.pre.dev/browser-agents/migrate-from-browser-use — read
it once before editing.
| Browser Use | pre.dev |
|---|---|
| `import { BrowserUse } from 'browser-use-sdk/v3'` | `import { PredevAPI } from 'predev-api'` |
| `new BrowserUse({ apiKey })` | `new PredevAPI({ apiKey })` |
| `client.run(taskStr, { llm })` | `client.browserAgent([{ url, instruction, output }])` |
| `response.output` (JSON string) | `result.results[0].data` (already parsed) |
| Python `from browser_use_sdk import BrowserUse` | `from predev_api import PredevAPI` |
| Python `BrowserUse(api_key=...)` | `PredevAPI(api_key=...)` |
| Python `client.run(task, llm=...)` | `client.browser_agent([{...}])` |
| Python `json.loads(response.output)` | `result["results"][0]["data"]` |
| Embedded URL in task string | first-class `url` field |
| Zod/Pydantic schema | JSON Schema in `output` field |
| Parallel via `Promise.all(client.run(...))` | single call with `client.browserAgent([...])` + `{ concurrency }` |
| `handle.sessionId` + session polling | `client.browserAgent([...], { async: true })` + `client.getBrowserAgent(id)` |
| Python async equivalent | `client.browser_agent([...], run_async=True)` + `client.get_browser_agent(id)` |
4. **Env vars.** Rename `BROWSER_USE_API_KEY` → `PREDEV_API_KEY`
everywhere (code, env files, CI, docker). Base URL is
`https://api.pre.dev` if it's referenced explicitly.
5. **Unsupported features — leave TODOs, don't delete.** For any of:
- `client.sessions.*` (persistent sessions)
- `client.agent_profiles.*` / `AgentProfile`
- `client.schedules.*`
- raw Playwright handoff
Leave a comment above the call:
`// TODO: pre.dev equivalent not yet available — see https://docs.pre.dev/browser-agents/migrate-from-browser-use#whats-different--not-yet-supported`
and keep the original code path so tests don't explode. We'll
hand-migrate these.
6. **Verify.** After edits, run the project's typecheck and test
commands (infer them from `package.json` scripts,
`pyproject.toml`, or a `Makefile`). Fix anything that breaks.
Do not skip this step.
7. **Report.** At the end, print:
- Files changed (grouped: imports, env, call sites).
- Count of migrated calls.
- Count of TODO-comments left for unsupported features.
- Any test/typecheck failures you couldn't fix, with exact
file:line and why.
## Constraints
- **Don't delete** any original code until the replacement is verified
working. If unsure of a mapping, leave the old call with a TODO
and surface it in the final report.
- **Don't guess** the pre.dev SDK name — it's `predev-api` on both
npm and pip. If that's not what the project's package manager
resolves, stop and ask.
- **Respect the user's existing code style** — don't reformat files
you didn't otherwise change.
Next