# When should a Browserbase agent read a page without opening a browser?

Open a Browserbase session when the agent has to click, log in, fill in a form or leave a replay to debug. For a plain read of one page, especially from a chosen country, send it to UpRock instead, because Browserbase bills each session for at least a minute of browser time and a megabyte of proxy traffic, while UpRock charges $0.50 per 1,000 pages. The same fallback catches the moment every Browserbase browser is busy.

Updated 21 Oct 2026. Web page: https://uprock.ai/compare/browserbase

## On this page

- [What Browserbase does well](#what-browserbase-does-well)
- [What 1,000 page reads cost](#what-1000-page-reads-cost)
- [When to send a read somewhere else](#when-to-send-a-read-somewhere-else)
- [Browserbase and UpRock side by side](#browserbase-and-uprock-side-by-side)
- [The fallback, in TypeScript and Python](#the-fallback-in-typescript-and-python)
- [FAQ](#faq)
- [Other use cases to explore](#explore)
- [Related articles](#related)
- [References](#references)
- [Get started](#get-started)

## What Browserbase does well

Browserbase runs headless browsers in the cloud for agents. You connect with Playwright, Puppeteer or Selenium, or use Stagehand, its open-source (MIT) framework that drives a browser from natural-language steps. Every session is recorded for replay, you can watch and take over a live session, contexts keep logins across sessions, downloads are saved for you, CAPTCHAs are solved on paid plans, and managed residential proxies cover more than 200 countries. There's also a hosted MCP server, a Fetch API and a Search API.

When an agent has to act on a page (log in, click through a flow, fill in a form), a browser session is the right tool, and Browserbase is a good one. Keep it.

## What 1,000 page reads cost

Many agent steps are just reads: open a page, take the text, move on. A browser session is a lot of machinery for that, and Browserbase's billing reflects it: browser time is billed by the minute and proxy traffic by the megabyte, with a one-minute and one-megabyte minimum per session.

Here's what 1,000 single-page reads cost on the Developer plan ($20 a month) once the included allowance is used up, from Browserbase's published prices on 21 Oct 2026:

| How you read 1,000 pages | Browser time | Proxy traffic (needed for a country) | Total |
|---|---|---|---|
| A new session per page, no country | 1,000 × the 1-minute minimum = 16.7 hours × $0.12 | none | **$2.00** |
| A new session per page, from a chosen country | the same 16.7 hours: $2.00 | at least 1 MB per session, 1,000 MB × $12 per GB | **at least $14** |
| One long session reused for every page (10 s each) | 2.8 hours × $0.12 = $0.33 | the pages' real size, $12 per GB | **$0.33 plus traffic** |
| Fetch API, proxies + markdown | none | included | **$7.00** |
| UpRock | | country included | **$0.50** |

On the Startup plan ($99 a month) the overage rates drop to $0.10 per browser hour and $10 per GB, and Fetch calls without extras cost $0.50 per 1,000. Reusing one session is the cheapest Browserbase option, but it means one country, one identity and one point of failure for the whole batch.

## When to send a read somewhere else

| What happens on Browserbase | What it means | Send it to UpRock? |
|---|---|---|
| Creating a session returns 429 | Every browser your plan allows is busy, or you hit the session creation rate | Yes, right away |
| The page loads with status 401, 403, 429 or 5xx | The site refused or failed this session | Yes |
| `TimeoutError` while loading | The page didn't load in time | Yes |
| `ERR_TUNNEL_CONNECTION_FAILED` | Browserbase's proxies decline this site's category (banking, government, ticketing, webmail, gambling) | No. Respect it |
| The step needs a click, a login or a form | A read won't do | No. That's Browserbase's job |

## Browserbase and UpRock side by side

| | Browserbase | UpRock |
|---|---|---|
| What you get | A full browser session you control | One page per call, as markdown and HTML |
| Price | [$20 or $99 a month](https://www.browserbase.com/pricing) with 100 or 500 browser hours, then $0.12 or $0.10 per hour; proxies $12 or $10 per GB; Fetch with proxies + markdown $7 per 1,000 | $0.50 per 1,000 pages by card or USDC, $0.40 with UPT |
| Billing minimum | [One minute and one MB per session](https://docs.browserbase.com/account/billing/plans) | One credit per page |
| Concurrency | 3, 25 or 100 browsers by plan; over the limit [returns 429](https://docs.browserbase.com/optimizations/concurrency/overview) | Not published yet |
| Where requests come from | Browserbase's cloud, with [managed residential proxies](https://docs.browserbase.com/platform/identity/proxies) on paid plans | Devices whose owners opted in |
| Countries | 200+, "best effort", city and US state available | 63, or a region |
| Clicks, logins, forms, CAPTCHAs | Yes | No |
| Session replay and live view | [Yes](https://docs.browserbase.com/platform/browser/observability/session-replay) | No |
| MCP server | [Hosted](https://docs.browserbase.com/integrations/mcp/setup), with browser actions | Yes, remote (streamable HTTP) |
| Video | Session recordings for debugging, not video search | Search public video by moment, $1 per 1,000 searches |
| To try it | A Free plan with 1 browser hour, no proxies | Prepaid credits |

Where Browserbase is better: anything interactive, from logins to multi-step flows, plus replays, live takeover, CAPTCHA solving and wider country coverage. Where UpRock fits: plain reads, at a fraction of the cost of a session, from a chosen country, and as the place a read goes when every browser is busy.

## The fallback, in TypeScript and Python

Every version open a Browserbase session with a residential proxy in the country you ask for, read the page with Playwright, and close the session. If the session can't be created (429 and other API errors), the page answers with a status worth retrying, or the load times out, the read goes to UpRock. SDK retries are switched off so a busy pool falls through at once. UpRock gets one `POST /v1/crawl` request with your key in an `Authorization: Bearer` header; the MCP and CLI tabs do the same through UpRock's MCP server (TypeScript) or CLI (Python).

### TypeScript (HTTP)

```ts
// npm i @browserbasehq/sdk playwright-core   (Node 18+)
// env: BROWSERBASE_API_KEY, UPROCK_API_KEY
import Browserbase from "@browserbasehq/sdk";
import { chromium } from "playwright-core";

// maxRetries: 0 so a full concurrency pool (429) falls through to UpRock at once.
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY, maxRetries: 0 });

// Target statuses worth a second try from a device in the same country.
const RETRY = new Set([401, 403, 429, 500, 502, 503, 504]);

async function viaBrowserbase(url: string, country: string) {
  // Built-in proxies and geolocation need a Developer plan or higher.
  const session = await bb.sessions.create({
    api_timeout: 60, // seconds before Browserbase ends the session
    proxies: [{ type: "browserbase", geolocation: { country } }],
  });
  const browser = await chromium.connectOverCDP(session.connectUrl);
  try {
    const page = browser.contexts()[0].pages()[0];
    const res = await page.goto(url, {
      waitUntil: "domcontentloaded",
      timeout: 30_000,
    });
    return { status: res?.status() ?? 0, text: await page.innerText("body") };
  } finally {
    await browser.close();
  }
}

async function viaUpRock(url: string, country: string) {
  // One call: UpRock loads the page on a real device in that country, tries a fresh device
  // every 7 s (up to 3 tries) and returns markdown. Failed calls aren't billed.
  const res = await fetch("https://edge.uprock.com/v1/crawl", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.UPROCK_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url, country }),
    signal: AbortSignal.timeout(90_000),
  });
  const page = await res.json();
  if (!res.ok) throw new Error(`UpRock ${res.status} ${page.code}: ${page.error}`);
  return page.markdown ?? "";
}

export async function read(url: string, country = "US") {
  try {
    const page = await viaBrowserbase(url, country);
    if (!RETRY.has(page.status)) return { via: "browserbase", text: page.text };
    console.warn(`The site answered ${page.status} through Browserbase; trying UpRock`);
  } catch (err) {
    const message = (err as Error).message;
    // Browserbase's proxies decline some site categories (banking, government, ticketing,
    // webmail, gambling). That's a policy answer, not an outage, so don't route around it.
    if (message.includes("ERR_TUNNEL_CONNECTION_FAILED")) throw err;
    // 429 (no free browser), timeouts and other navigation errors fall through to UpRock.
    console.warn(`Browserbase failed (${message}); trying UpRock`);
  }
  return { via: "uprock", text: await viaUpRock(url, country) };
}

// node read.ts https://developer.mozilla.org/en-US/docs/Web/HTTP DE
const [url = "https://developer.mozilla.org/en-US/docs/Web/HTTP", country = "US"] =
  process.argv.slice(2);
const page = await read(url, country);
console.log(page.via, page.text.slice(0, 200));
```

### Python (HTTP)

```python
# pip install browserbase playwright requests   env: BROWSERBASE_API_KEY, UPROCK_API_KEY
import os
import sys

import requests
from browserbase import Browserbase
from playwright.sync_api import sync_playwright

# max_retries=0 so a full concurrency pool (429) falls through to UpRock at once.
bb = Browserbase(api_key=os.environ.get("BROWSERBASE_API_KEY"), max_retries=0)

# Target statuses worth a second try from a device in the same country.
RETRY = {401, 403, 429, 500, 502, 503, 504}


def via_browserbase(url: str, country: str) -> dict:
    # Built-in proxies and geolocation need a Developer plan or higher.
    session = bb.sessions.create(
        api_timeout=60,
        proxies=[{"type": "browserbase", "geolocation": {"country": country}}],
    )
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(session.connect_url)
        try:
            page = browser.contexts[0].pages[0]
            res = page.goto(url, wait_until="domcontentloaded", timeout=30_000)
            return {"status": res.status if res else 0, "text": page.inner_text("body")}
        finally:
            browser.close()


def via_uprock(url: str, country: str) -> str:
    # One call: UpRock loads the page on a real device in that country, tries a fresh device
    # every 7 s (up to 3 tries) and returns markdown. Failed calls aren't billed.
    res = requests.post(
        "https://edge.uprock.com/v1/crawl",
        headers={"Authorization": f"Bearer {os.environ['UPROCK_API_KEY']}"},
        json={"url": url, "country": country},
        timeout=90,
    )
    page = res.json()
    if not res.ok:
        raise RuntimeError(f"UpRock {res.status_code} {page.get('code')}: {page.get('error')}")
    return page.get("markdown", "")


def read(url: str, country: str = "US") -> dict:
    try:
        page = via_browserbase(url, country)
        if page["status"] not in RETRY:
            return {"via": "browserbase", "text": page["text"]}
        print(
            f"The site answered {page['status']} through Browserbase; trying UpRock",
            file=sys.stderr,
        )
    except Exception as err:
        # Browserbase's proxies decline some site categories (banking, government, ticketing,
        # webmail, gambling). That's a policy answer, not an outage, so don't route around it.
        if "ERR_TUNNEL_CONNECTION_FAILED" in str(err):
            raise
        # 429 (no free browser), timeouts and other navigation errors fall through to UpRock.
        print(f"Browserbase failed ({err}); trying UpRock", file=sys.stderr)
    return {"via": "uprock", "text": via_uprock(url, country)}


if __name__ == "__main__":
    # python read.py https://developer.mozilla.org/en-US/docs/Web/HTTP DE
    url = sys.argv[1] if len(sys.argv) > 1 else "https://developer.mozilla.org/en-US/docs/Web/HTTP"
    country = sys.argv[2] if len(sys.argv) > 2 else "US"
    page = read(url, country)
    print(page["via"], page["text"][:200])
```

### TypeScript (MCP)

```ts
// npm i @browserbasehq/sdk playwright-core @modelcontextprotocol/sdk   (Node 18+)
// env: BROWSERBASE_API_KEY, UPROCK_API_KEY
import Browserbase from "@browserbasehq/sdk";
import { chromium } from "playwright-core";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

// maxRetries: 0 so a full concurrency pool (429) falls through to UpRock at once.
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY, maxRetries: 0 });
const uprock = new Client({ name: "browserbase-fallback", version: "1.0.0" });
await uprock.connect(
  new StreamableHTTPClientTransport(
    new URL("https://mcp.uprock.ai/mcp"),
    { requestInit: { headers: { Authorization: `Bearer ${process.env.UPROCK_API_KEY}` } } },
  ),
);

// Target statuses worth a second try from a device in the same country.
const RETRY = new Set([401, 403, 429, 500, 502, 503, 504]);

const textOf = (content: unknown) =>
  (content as { type: string; text?: string }[]).find((c) => c.type === "text")?.text ??
  "";

async function viaBrowserbase(url: string, country: string) {
  // Built-in proxies and geolocation need a Developer plan or higher.
  const session = await bb.sessions.create({
    api_timeout: 60, // seconds before Browserbase ends the session
    proxies: [{ type: "browserbase", geolocation: { country } }],
  });
  const browser = await chromium.connectOverCDP(session.connectUrl);
  try {
    const page = browser.contexts()[0].pages()[0];
    const res = await page.goto(url, {
      waitUntil: "domcontentloaded",
      timeout: 30_000,
    });
    return { status: res?.status() ?? 0, text: await page.innerText("body") };
  } finally {
    await browser.close();
  }
}

async function viaUpRock(url: string, country: string) {
  const res = await uprock.callTool(
    { name: "crawl_fetch", arguments: { url, country } },
    undefined,
    { timeout: 300_000 },
  );
  if (res.isError) throw new Error(`UpRock: ${textOf(res.content)}`);
  const uri = JSON.parse(textOf(res.content)).content?.markdown?.resource;
  if (!uri) throw new Error(`UpRock: no markdown for ${url}`);
  return textOf(
    (await uprock.callTool({ name: "resource_fetch", arguments: { uri } })).content,
  );
}

export async function read(url: string, country = "US") {
  try {
    const page = await viaBrowserbase(url, country);
    if (!RETRY.has(page.status)) return { via: "browserbase", text: page.text };
    console.warn(`The site answered ${page.status} through Browserbase; trying UpRock`);
  } catch (err) {
    const message = (err as Error).message;
    // Browserbase's proxies decline some site categories (banking, government, ticketing,
    // webmail, gambling). That's a policy answer, not an outage, so don't route around it.
    if (message.includes("ERR_TUNNEL_CONNECTION_FAILED")) throw err;
    // 429 (no free browser), timeouts and other navigation errors fall through to UpRock.
    console.warn(`Browserbase failed (${message}); trying UpRock`);
  }
  return { via: "uprock", text: await viaUpRock(url, country) };
}

// node read.ts https://developer.mozilla.org/en-US/docs/Web/HTTP DE
const [url = "https://developer.mozilla.org/en-US/docs/Web/HTTP", country = "US"] =
  process.argv.slice(2);
try {
  const page = await read(url, country);
  console.log(page.via, page.text.slice(0, 200));
} finally {
  await uprock.close();
}
```

### Python (CLI)

```python
# pip install browserbase playwright; npm i -g uprock   env: BROWSERBASE_API_KEY, UPROCK_API_KEY
import json
import os
import subprocess
import sys

from browserbase import Browserbase
from playwright.sync_api import sync_playwright

# max_retries=0 so a full concurrency pool (429) falls through to UpRock at once.
bb = Browserbase(api_key=os.environ.get("BROWSERBASE_API_KEY"), max_retries=0)

# Target statuses worth a second try from a device in the same country.
RETRY = {401, 403, 429, 500, 502, 503, 504}


def via_browserbase(url: str, country: str) -> dict:
    # Built-in proxies and geolocation need a Developer plan or higher.
    session = bb.sessions.create(
        api_timeout=60,
        proxies=[{"type": "browserbase", "geolocation": {"country": country}}],
    )
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(session.connect_url)
        try:
            page = browser.contexts[0].pages[0]
            res = page.goto(url, wait_until="domcontentloaded", timeout=30_000)
            return {"status": res.status if res else 0, "text": page.inner_text("body")}
        finally:
            browser.close()


def via_uprock(url: str, country: str) -> str:
    run = subprocess.run(
        ["uprock", "ai", "crawl", url, "--country", country, "--content"],
        capture_output=True,
        text=True,
        timeout=330,
    )
    if run.returncode != 0:
        raise RuntimeError(f"UpRock: {run.stderr.strip()}")
    return json.loads(run.stdout).get("inlined_markdown", "")


def read(url: str, country: str = "US") -> dict:
    try:
        page = via_browserbase(url, country)
        if page["status"] not in RETRY:
            return {"via": "browserbase", "text": page["text"]}
        print(
            f"The site answered {page['status']} through Browserbase; trying UpRock",
            file=sys.stderr,
        )
    except Exception as err:
        # Browserbase's proxies decline some site categories (banking, government, ticketing,
        # webmail, gambling). That's a policy answer, not an outage, so don't route around it.
        if "ERR_TUNNEL_CONNECTION_FAILED" in str(err):
            raise
        # 429 (no free browser), timeouts and other navigation errors fall through to UpRock.
        print(f"Browserbase failed ({err}); trying UpRock", file=sys.stderr)
    return {"via": "uprock", "text": via_uprock(url, country)}


if __name__ == "__main__":
    # python read.py https://developer.mozilla.org/en-US/docs/Web/HTTP DE
    url = sys.argv[1] if len(sys.argv) > 1 else "https://developer.mozilla.org/en-US/docs/Web/HTTP"
    country = sys.argv[2] if len(sys.argv) > 2 else "US"
    page = read(url, country)
    print(page["via"], page["text"][:200])
```

Tested 21 Oct 2026 with an invalid Browserbase key, which fails session creation and sends the read to UpRock. Without a paid plan, the session path follows Browserbase's quickstart and SDK types (Node 2.22, Python 1.21). On 6 Nov 2026 we ran the HTTP and MCP versions again with an UpRock API key in an `Authorization` header: with an invalid Browserbase key, each fell back to UpRock and got the page.

## FAQ

### Why does a single page cost a full minute on Browserbase?

Browserbase's billing docs say browser time is billed by the minute and proxy bandwidth by the megabyte, with a one-minute and one-megabyte minimum per session. A read that takes ten seconds in its own session is billed as a minute.

### What happens when all my Browserbase browsers are in use?

Creating another session returns HTTP 429. The Node and Python SDKs retry 429s twice by default; the code on this page turns that off so the read goes to UpRock at once instead of waiting.

### Isn't Browserbase's Fetch API the same thing as UpRock?

It's the closest match. Fetch with proxies and markdown output (Browserbase calls markdown "Fetch Extract") costs $7 per 1,000 calls on the paid plans. Browserbase's docs say Fetch doesn't render JavaScript and caps pages at 5 MB, though its product page says it does render. UpRock loads every page in a real browser.

### Should I send ERR_TUNNEL_CONNECTION_FAILED pages to UpRock?

No. Browserbase's proxies decline some site categories, such as banking, government, ticketing, webmail and gambling. That's a policy decision about the site, and the fallback on this page respects it.

### Can UpRock click or log in?

No. UpRock loads a page and returns it; it doesn't keep a session, click or sign in. For that, Browserbase (or Stagehand on top of it) is the right tool.

## Other use cases to explore

- [Cap what a single page can cost](https://uprock.ai/compare/scrapingbee#what-a-page-costs-in-credits): one provider's credit table, from 1 to 75 credits a page, and how to stop before the expensive tiers.
- [See a page as it looks in another country](https://uprock.ai/use-cases/page-from-another-country): one store page from five countries, with five prices, two languages and a cookie banner in one country only.
- [Estimate what 1,000 pages will cost](https://uprock.ai/compare/jina-reader#tokens-or-pages-what-1000-reads-cost): token counts for six real pages, and the length where a flat per-page price is cheaper.
- [Search public video by moment](https://uprock.ai/video-semantic-search): find the second something is shown or said, across platforms.

## Related articles

- [How do I add a backup crawler to Firecrawl?](https://uprock.ai/compare/firecrawl.md): When to send a page from Firecrawl to UpRock, published prices side by side, and a tested fallback in TypeScript and Python.
- [How do I add a backup for Tavily Extract?](https://uprock.ai/compare/tavily.md): Retry the URLs in Tavily Extract's failed results on a real device, and read pages as they appear in a chosen country. Prices side by side and tested code.
- [How do I add a fallback for Exa contents errors?](https://uprock.ai/compare/exa.md): Read the per-URL statuses in Exa's /contents response and retry the right ones live on a real device. Error tags explained, prices side by side, tested code.
- [How do I add a backup to Jina Reader, and when is it cheaper?](https://uprock.ai/compare/jina-reader.md): Spot a failed read in Reader's JSON (it still answers 200), retry it on a real device, and see what six real pages cost in Reader tokens next to a flat per-page price.
- [How do I add a hosted fallback to Crawl4AI?](https://uprock.ai/compare/crawl4ai.md): Plug UpRock into Crawl4AI's fallback_fetch_function so blocked pages are fetched on a real device in the country you choose, then run through Crawl4AI's own markdown pipeline.
- [How do I add a backup to Bright Data's Web Unlocker?](https://uprock.ai/compare/bright-data.md): Which Web Unlocker errors to retry, which to send to a second network and which to respect, with Bright Data's published prices next to ours and tested fallback code.
- [How do I add a fallback to an Apify or Crawlee crawler?](https://uprock.ai/compare/apify.md): Hand the requests Crawlee gives up on to a second network with failedRequestHandler, see what a fetched page costs on Apify's own Actors, and run the tested code.
- [How do I cap ScrapingBee's credit cost and send the rest to a backup?](https://uprock.ai/compare/scrapingbee.md): ScrapingBee charges 1 to 75 credits a page depending on the options. Cap it with Auto-Mode and max_cost, watch the country fallback, and send the rest to UpRock with tested code.
- [Spider costs less per page than UpRock. When is a backup still worth it?](https://uprock.ai/compare/spider.md): Spider is cheaper per page, so keep it as your primary. How Spider reports a page it couldn't get (from our own tests), and tested code that sends only those pages to UpRock.
- [How do I add a fallback to Parallel's Extract API?](https://uprock.ai/compare/parallel.md): Make Parallel Extract report a failed live fetch instead of quietly serving an old cached copy, then retry those URLs on UpRock. Prices side by side and tested code.
- [How do I add a backup to ZenRows?](https://uprock.ai/compare/zenrows.md): Which ZenRows error codes to send to a second network and which to respect, what a page from one country costs once premium proxies are required, and tested fallback code.
- [How do I retry a ScraperAPI batch's failed jobs on another network?](https://uprock.ai/compare/scraperapi.md): Send the jobs ScraperAPI's async batch gives up on to UpRock, see which countries each ScraperAPI plan can target, and compare credit costs. Tested code for both.
- [How do I add a backup to Oxylabs' new Web API?](https://uprock.ai/compare/oxylabs.md): Which Oxylabs Web API failures to send to a second network and which to respect, what a generic page really costs after its October 2026 pricing change, and tested fallback code.
- [How do I add a backup to Context.dev's scrape API?](https://uprock.ai/compare/context-dev.md): Context.dev reports a failed scrape inside an HTTP 200. Which of its error codes to retry on a second network, what a page costs by plan, and tested fallback code.
- [How do I add a backup to Scrapfly instead of paying for residential?](https://uprock.ai/compare/scrapfly.md): A Scrapfly page costs 1 to 30 credits depending on proxy pool and browser. What that is per 1,000 pages on each plan, which errors to send on, and tested code.
- [How do I keep Crawlbase's site tiers from surprising my bill?](https://uprock.ai/compare/crawlbase.md): Crawlbase bills each site at 1 to 20 times its base rate. What that does to the cost per 1,000 pages, and tested code that moves pricey domains to UpRock.
- [Which unlocker API fits: Bright Data, Oxylabs, Zyte or UpRock?](https://uprock.ai/compare/web-unlocker-apis.md): Bright Data's Web Unlocker, Oxylabs' Web API and Zyte API priced per 1,000 hard pages, with what each bills and when a second network helps.
- [Compare: all articles](https://uprock.ai/compare.md)

## References

1. [Browserbase, Pricing](https://www.browserbase.com/pricing), checked 21 Oct 2026
2. [Browserbase docs, Plans and billing minimums](https://docs.browserbase.com/account/billing/plans), checked 21 Oct 2026
3. [Browserbase docs, Proxies (geolocation, excluded site categories)](https://docs.browserbase.com/platform/identity/proxies), checked 21 Oct 2026
4. [Browserbase docs, Fetch API (Fetch vs Fetch Extract)](https://docs.browserbase.com/platform/fetch/overview), checked 21 Oct 2026
5. [Browserbase docs, Concurrency and rate limits](https://docs.browserbase.com/optimizations/concurrency/overview), checked 21 Oct 2026
6. [Browserbase docs, Create a session (API reference)](https://docs.browserbase.com/reference/api/create-a-session), checked 21 Oct 2026
7. [Browserbase docs, Session replay](https://docs.browserbase.com/platform/browser/observability/session-replay), checked 21 Oct 2026
8. [Browserbase docs, MCP server setup](https://docs.browserbase.com/integrations/mcp/setup), checked 21 Oct 2026
9. [Stagehand on GitHub (MIT)](https://github.com/browserbase/stagehand), checked 21 Oct 2026
10. [UpRock, everything in one file (API, CLI, MCP, pricing)](https://uprock.ai/llms-full.txt), checked 21 Oct 2026

## Get started

Get an API key: https://uprock.ai/app/api-keys

Paste this into your agent:

```text
Set up UpRock for me: read https://uprock.ai/agents.md, connect the UpRock MCP server, and run one test crawl. Tell me what it costs before you spend anything.
```

Setup for every client (MCP, skill, CLI): https://uprock.ai/agents.md
