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

# Browser Automation

> Drive Chrome on a Mobilerun device with Puppeteer or Playwright over the Chrome DevTools Protocol.

Mobilerun exposes a per-device WebSocket that reverse-proxies Chrome-for-Android's DevTools (CDP) endpoint. Off-the-shelf browser automation libraries — [Puppeteer](https://pptr.dev) and [Playwright](https://playwright.dev) — connect to it with a single URL, just like they would to a local Chrome instance.

Use this when you want to script Chrome on a device directly (navigate, evaluate JavaScript, intercept network requests, scrape content) instead of going through the agent.

## Endpoint

```
wss://api.mobilerun.ai/v1/devices/{deviceId}/browser/cdp
```

The endpoint is a **browser-level** CDP socket. Pages are multiplexed over the CDP `Target` domain — you do not need a separate URL per tab.

### Authentication

Pass your Mobilerun API key as a request header during the WebSocket handshake:

```
Authorization: Bearer dr_sk_YOUR_API_KEY
```

<Warning>
  Always send the key as a header. Never put credentials in the URL — query-string tokens leak into logs, traces, and proxy access records.
</Warning>

### Requirements

* The device must be in the `ready` state. See [Devices](/devices) for state details.
* Chrome must be installed and have remote debugging available on the device. If Chrome's CDP socket is not reachable, the connection fails at handshake with a clear error.
* You need a Mobilerun [API key](/api-keys).

## Connect with Puppeteer

```js theme={null}
import puppeteer from 'puppeteer-core';

const browser = await puppeteer.connect({
  browserWSEndpoint: `wss://api.mobilerun.ai/v1/devices/${deviceId}/browser/cdp`,
  headers: {
    Authorization: `Bearer ${process.env.MOBILERUN_API_KEY}`,
  },
});

const page = await browser.newPage();
await page.goto('https://example.com');
const title = await page.title();
console.log(title);

await browser.disconnect();
```

## Connect with Playwright

```js theme={null}
import { chromium } from 'playwright';

const browser = await chromium.connectOverCDP(
  `wss://api.mobilerun.ai/v1/devices/${deviceId}/browser/cdp`,
  {
    headers: {
      Authorization: `Bearer ${process.env.MOBILERUN_API_KEY}`,
    },
  }
);

const context = browser.contexts()[0] ?? await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');
console.log(await page.title());

await browser.close();
```

## How it works

The endpoint transparently bridges the WebSocket to Chrome's `chrome_devtools_remote` socket on the device. There is no local port forwarding to set up and no separate ADB tunnel to manage — once the device is connected to Mobilerun, the CDP endpoint is available automatically on every adb-backed device type ([Cloud Phone](/device-types#cloud-phone) and [Physical Phone](/device-types#physical-phone)).

Standard CDP semantics apply: connect once at the browser level, then attach to individual tabs through `Target.getTargets` / `Target.attachToTarget` (Puppeteer and Playwright handle this for you).

## Limitations

* **Chrome only.** WebView debugging (`webview_devtools_remote_<pid>`) is not exposed.
* **Browser-level only.** There is no per-page `/json/list` proxy — use the CDP `Target` domain to enumerate and attach to pages.
* **Chrome must be debuggable.** A device without an active Chrome CDP socket fails at connect time.
