> ## 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.

# Migrate from Browser Use

> Swap Browser Use Cloud for pre.dev Browser Agents — same task shape, 100/100 vs 97/100 pass rate, 2.9× cheaper AND 2.8× faster.

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](/browser-agents/overview#benchmark), pre.dev passes **100/100** vs Browser Use Cloud's **97/100** — and is **2.9× cheaper and 2.8× faster** ($0.0129 vs $0.0372 per task, 12.7s vs 35.7s avg).

## 1. Swap env vars + install the SDK

<Tabs>
  <Tab title="Node / TypeScript">
    ```bash theme={null}
    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=...
    ```
  </Tab>

  <Tab title="Python">
    ```bash theme={null}
    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=...
    ```
  </Tab>
</Tabs>

Grab your pre.dev key at [pre.dev/projects/browser-agents](https://pre.dev/projects/browser-agents).

***

## 2. Run one task

<Tabs>
  <Tab title="TypeScript">
    **Before — Browser Use**

    ```ts theme={null}
    import { 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.dev**

    ```ts theme={null}
    import { 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' }
    ```
  </Tab>

  <Tab title="Python">
    **Before — Browser Use**

    ```python theme={null}
    import 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.dev**

    ```python theme={null}
    import 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'}
    ```
  </Tab>
</Tabs>

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.

<Tabs>
  <Tab title="TypeScript">
    **Before — Browser Use (manual fan-out)**

    ```ts theme={null}
    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)**

    ```ts theme={null}
    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);
    ```
  </Tab>

  <Tab title="Python">
    **Before — Browser Use (manual fan-out)**

    ```python theme={null}
    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)**

    ```python theme={null}
    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"]]
    ```
  </Tab>
</Tabs>

Up to 1,000 tasks per request. See [Run a Task](/browser-agents/api/run-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)`.

<Tabs>
  <Tab title="TypeScript">
    **Before — Browser Use**

    ```ts theme={null}
    const 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.dev**

    ```ts theme={null}
    const { 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));
    }
    ```
  </Tab>

  <Tab title="Python">
    **Before — Browser Use**

    ```python theme={null}
    import 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.dev**

    ```python theme={null}
    import 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)
    ```
  </Tab>
</Tabs>

See [Task Status](/browser-agents/api/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](https://claude.com/claude-code) at the root of your repo. Works for any stack — Node, Bun, Deno, Python, monorepos.

```markdown theme={null}
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

* [Quickstart](/browser-agents/quickstart) — run your first task end-to-end.
* [Run a Task API reference](/browser-agents/api/run-task) — the full `POST /browser-agent` contract.
* [Benchmark](/browser-agents/overview#benchmark) — how the numbers were measured.
