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

# Connect Playwright

> Connect a Playwright client to a running Tilion browser over CDP.

Use Playwright for your own applications or unprotected pages. For protected-page reads, use [fetch](https://docs.tilion.dev/guides/read-page.md) to get exit escalation and a `content_ok` result.

Install `playwright` and `requests` for Python, or `playwright-core` for Node.js. A local browser installation is not required for this connection.

With a [running session](https://docs.tilion.dev/guides/create-browser.md), set `TILION_API_KEY` and `TILION_SESSION_ID`.

```python Python theme={"dark"}
import os
import requests
from playwright.sync_api import sync_playwright

base = "https://back.tilion.dev"
sid = os.environ["TILION_SESSION_ID"]
headers = {"Authorization": f"Bearer {os.environ['TILION_API_KEY']}"}
response = requests.get(f"{base}/v1/session/{sid}/connect", headers=headers, timeout=30)
response.raise_for_status()
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(response.json()["connect_url"], timeout=60000)
    try:
        context = browser.contexts[0]
        page = context.pages[0] if context.pages else context.new_page()
        page.goto("https://example.com/", wait_until="domcontentloaded")
        print(page.title())
    finally:
        browser.close()
```

```javascript Node.js theme={"dark"}
import { chromium } from "playwright-core";

const sid = process.env.TILION_SESSION_ID;
const response = await fetch(`https://back.tilion.dev/v1/session/${sid}/connect`, {
  headers: { Authorization: `Bearer ${process.env.TILION_API_KEY}` },
  signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(await response.text());
const { connect_url } = await response.json();
const browser = await chromium.connectOverCDP(connect_url, { timeout: 60_000 });
try {
  const context = browser.contexts()[0];
  const page = context.pages()[0] ?? await context.newPage();
  await page.goto("https://example.com/", { waitUntil: "domcontentloaded" });
  console.log(await page.title());
} finally {
  await browser.close();
}
```

```bash curl theme={"dark"}
# Retrieve connection URLs. curl does not run Playwright.
curl --fail-with-body \
  "https://back.tilion.dev/v1/session/$TILION_SESSION_ID/connect" \
  -H "Authorization: Bearer $TILION_API_KEY"
```

The examples print `Example Domain`. The default context contains the session's cookies and storage. A new context has separate state.

> [!NOTE]
>
> `browser.close()` disconnects this client. The remote session remains running until you delete it or it expires. Send `DELETE /v1/session/{id}` when finished.

Connection tokens expire after 1,800 seconds. Request `/connect` again for fresh URLs. See [Playwright, CDP & tunnels](https://docs.tilion.dev/sessions/playwright-cdp.md) for the native Playwright endpoint and localhost tunnels.

To attach another process to the same browser, see [Connect multiple CDP clients](https://docs.tilion.dev/guides/multiple-cdp-clients.md). A session supports one main client and up to two observers when enabled for your account.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.