> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect over CDP

The box browser is a real Chromium, and you can drive it with the tools you already use. `cdpUrl()` returns an authenticated Chrome DevTools Protocol WebSocket URL that Playwright, Puppeteer, or Stagehand can connect to directly. There is no browser to install and nothing to manage.

<CodeGroup>
  ```typescript box.ts theme={"system"}
  const cdpUrl = await box.browser.cdpUrl()
  // wss://…?token=…
  ```

  ```python box.py theme={"system"}
  cdp_url = box.browser.cdp_url()
  # wss://…?token=…
  ```
</CodeGroup>

Like [live view](/docs/box/overall/browser/live-view) URLs, the CDP URL carries its auth token in the URL. Anyone who has it gets full control of the browser, so treat it as a secret.

## Playwright

`playwright-core` is enough here, since you connect to the box's Chromium instead of launching one locally:

<CodeGroup>
  ```typescript scrape.ts theme={"system"}
  import { chromium } from "playwright-core"

  const browser = await chromium.connectOverCDP(cdpUrl)

  const context = browser.contexts()[0] ?? (await browser.newContext())
  const page = context.pages()[0] ?? (await context.newPage())

  await page.goto("https://upstash.com")
  console.log(await page.title())
  ```

  ```python scrape.py theme={"system"}
  from playwright.sync_api import sync_playwright

  with sync_playwright() as p:
      browser = p.chromium.connect_over_cdp(cdp_url)
      context = browser.contexts[0] if browser.contexts else browser.new_context()
      page = context.pages[0] if context.pages else context.new_page()
      page.goto("https://upstash.com")
      print(page.title())
  ```
</CodeGroup>

## Puppeteer

```typescript scrape.ts theme={"system"}
import puppeteer from "puppeteer-core"

const browser = await puppeteer.connect({ browserWSEndpoint: cdpUrl })

const page = (await browser.pages())[0] ?? (await browser.newPage())
await page.goto("https://upstash.com")
```

## Stagehand

[Stagehand](https://www.stagehand.dev) can use the box browser as its local browser:

```typescript agent.ts theme={"system"}
import { Stagehand } from "@browserbasehq/stagehand"

const stagehand = new Stagehand({
  env: "LOCAL",
  localBrowserLaunchOptions: { cdpUrl },
})

await stagehand.init()
await stagehand.act("click the first link")
```

## Mixing CDP and SDK control

CDP clients and the SDK drive the same browser and the same tabs. A page opened by Playwright shows up in `box.browser.listTabs()`, and a tab created by the SDK is visible to Playwright. You can script the predictable steps like login and pagination with Playwright, hand the tab to [`act` or `run`](/docs/box/overall/browser/ai-actions) for the steps that are easier to describe in natural language, and watch either through [Live View](/docs/box/overall/browser/live-view).

As a rule of thumb: use CDP when you want precise, repeatable scripting with no LLM in the loop. Use [AI Actions](/docs/box/overall/browser/ai-actions) when describing the task is easier than scripting it.
