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

# Playwright

> Connect Driver sessions to Playwright

[Playwright](https://playwright.dev) is a modern browser automation framework with auto-waiting, powerful selectors, and multi-browser support.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install playwright @browsercash/sdk
  ```

  ```bash pip theme={null}
  pip install playwright requests
  ```
</CodeGroup>

## Quick Start

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { chromium } from "playwright";
  import BrowsercashSDK from "@browsercash/sdk";

  async function run() {
    const client = new BrowsercashSDK({
      apiKey: process.env.DRIVER_API_KEY!,
      baseURL: "https://api.driver.dev",
    });

    // Create a Driver session
    const session = await client.browser.session.create({ type: "hosted" });

    // Connect Playwright via CDP
    const browser = await chromium.connectOverCDP(session.cdpUrl);

    // Get existing context or create new one
    const context = browser.contexts()[0] || (await browser.newContext());
    const page = context.pages()[0] || (await context.newPage());

    // Automate
    await page.goto("https://example.com");
    console.log(await page.title());

    // Cleanup
    await browser.close();
    await client.browser.session.stop({ sessionId: session.sessionId });
  }

  run().catch(console.error);
  ```

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

  # Create a Driver session
  resp = requests.post(
      "https://api.driver.dev/v1/browser/session",
      headers={
          "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}",
          "Content-Type": "application/json",
      },
      json={"type": "hosted"}
  )
  session = resp.json()

  # Connect Playwright via CDP
  with sync_playwright() as p:
      browser = p.chromium.connect_over_cdp(session["cdpUrl"])

      # Get existing context or create new one
      context = browser.contexts[0] if browser.contexts else browser.new_context()
      page = context.pages[0] if context.pages else context.new_page()

      # Automate
      page.goto("https://example.com")
      print(page.title())

      browser.close()

  # Cleanup
  requests.delete(
      "https://api.driver.dev/v1/browser/session",
      headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"},
      params={"sessionId": session["sessionId"]}
  )
  ```
</CodeGroup>

## With Session Options

<CodeGroup>
  ```typescript TypeScript theme={null}
  const session = await client.browser.session.create({
    country: "US",
    type: "hosted",
    windowSize: "1920x1080",
  });

  const browser = await chromium.connectOverCDP(session.cdpUrl);
  ```

  ```python Python theme={null}
  resp = requests.post(
      "https://api.driver.dev/v1/browser/session",
      headers={
          "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}",
          "Content-Type": "application/json",
      },
      json={
          "country": "US",
          "type": "hosted",
          "windowSize": "1920x1080"
      }
  )
  session = resp.json()

  browser = p.chromium.connect_over_cdp(session["cdpUrl"])
  ```
</CodeGroup>

## Using Existing Contexts

Driver sessions may have a pre-existing browser context. Always check before creating a new one:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const browser = await chromium.connectOverCDP(session.cdpUrl);

  // Use existing context if available
  const context = browser.contexts()[0] || (await browser.newContext());
  const page = context.pages()[0] || (await context.newPage());
  ```

  ```python Python theme={null}
  browser = p.chromium.connect_over_cdp(session["cdpUrl"])

  # Use existing context if available
  context = browser.contexts[0] if browser.contexts else browser.new_context()
  page = context.pages[0] if context.pages else context.new_page()
  ```
</CodeGroup>

## With Persistent Profiles

<CodeGroup>
  ```typescript TypeScript theme={null}
  // First run — login and save profile
  const session1 = await client.browser.session.create({
    type: "hosted",
    profile: { name: "my-account", persist: true },
  });

  const browser1 = await chromium.connectOverCDP(session1.cdpUrl);
  const page1 = await browser1.newPage();

  // Login flow...
  await page1.goto("https://example.com/login");
  await page1.fill("#email", "user@example.com");
  await page1.fill("#password", "password");
  await page1.click('button[type="submit"]');

  await browser1.close();
  await client.browser.session.stop({ sessionId: session1.sessionId });

  // Later — session starts already logged in
  const session2 = await client.browser.session.create({
    type: "hosted",
    profile: { name: "my-account", persist: true },
  });
  ```

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

  headers = {
      "Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}",
      "Content-Type": "application/json",
  }

  # First run — login and save profile
  resp1 = requests.post(
      "https://api.driver.dev/v1/browser/session",
      headers=headers,
      json={"type": "hosted", "profile": {"name": "my-account", "persist": True}}
  )
  session1 = resp1.json()

  with sync_playwright() as p:
      browser1 = p.chromium.connect_over_cdp(session1["cdpUrl"])
      page1 = browser1.new_page()

      # Login flow...
      page1.goto("https://example.com/login")
      page1.fill("#email", "user@example.com")
      page1.fill("#password", "password")
      page1.click('button[type="submit"]')

      browser1.close()

  # Stop session1
  requests.delete(
      "https://api.driver.dev/v1/browser/session",
      headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"},
      params={"sessionId": session1["sessionId"]}
  )

  # Later — session starts already logged in
  resp2 = requests.post(
      "https://api.driver.dev/v1/browser/session",
      headers=headers,
      json={"type": "hosted", "profile": {"name": "my-account", "persist": True}}
  )
  session2 = resp2.json()
  ```
</CodeGroup>

## Error Handling

<CodeGroup>
  ```typescript TypeScript theme={null}
  try {
    const browser = await chromium.connectOverCDP(session.cdpUrl);
    // ... automation
  } catch (error) {
    if (error.message.includes("Target closed")) {
      // Session may have timed out
      console.log("Session closed, creating new one...");
    }
    throw error;
  } finally {
    await client.browser.session.stop({ sessionId: session.sessionId });
  }
  ```

  ```python Python theme={null}
  try:
      browser = p.chromium.connect_over_cdp(session["cdpUrl"])
      # ... automation
  except Exception as e:
      if "Target closed" in str(e):
          # Session may have timed out
          print("Session closed, creating new one...")
      raise
  finally:
      requests.delete(
          "https://api.driver.dev/v1/browser/session",
          headers={"Authorization": f"Bearer {os.getenv('DRIVER_API_KEY')}"},
          params={"sessionId": session["sessionId"]}
      )
  ```
</CodeGroup>

## Tips

* Use `page.waitForLoadState('networkidle')` for pages with dynamic content
* Driver sessions support all Playwright features including screenshots, PDFs, and tracing
* For long-running tasks, monitor session status and handle reconnection
* Use `browser.close()` to end your Playwright connection. Unlike Puppeteer's `browser.disconnect()`, this properly closes the CDP connection while the Driver session can still be stopped separately via the API
