> ## 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.

# Quickstart

> Create a session, connect with Patchright, read a page title, stop the session.

<Steps>
  <Step title="Get an API key">
    Sign in to the [dashboard](https://app.driver.dev), open **Settings → API keys**, create a key. Put it in your environment as `DRIVER_API_KEY`. Don't commit it.

    ```bash theme={"dark"}
    export DRIVER_API_KEY="dr_…"
    ```
  </Step>

  <Step title="Install Patchright">
    Patchright is Playwright with the CDP fingerprints removed. Use it instead of stock Playwright or sites that watch for automation will block the session. Details in [Why Patchright](/docs/frameworks/patchright#why-patchright).

    <CodeGroup>
      ```bash npm theme={"dark"}
      npm install patchright
      ```

      ```bash pip theme={"dark"}
      pip install patchright requests
      ```
    </CodeGroup>

    No `playwright install`, no browser download. Driver provides the browser.

    The TypeScript examples use top-level `await` and the global `fetch`: Node 18+, in an ES module (`"type": "module"` in `package.json`, or a `.mts` file), run with `npx tsx file.ts`. Python examples need 3.8+.
  </Step>

  <Step title="Create a session, connect, stop">
    <CodeGroup>
      ```typescript TypeScript theme={"dark"}
      import { chromium } from "patchright";

      const API = "https://api.driver.dev";
      const headers = {
        Authorization: `Bearer ${process.env.DRIVER_API_KEY}`,
        "Content-Type": "application/json",
      };

      // 1. Create a session. Every field is optional; this one asks for a US browser for ten minutes.
      const res = await fetch(`${API}/v1/browser/session`, {
        method: "POST",
        headers,
        body: JSON.stringify({ country: "US", duration: 600 }),
      });
      if (!res.ok) throw new Error(`Driver ${res.status}: ${(await res.json().catch(() => ({}))).error ?? res.statusText}`);
      const session = await res.json();

      try {
        // 2. Connect over CDP and reuse the browser's own context and tab.
        const browser = await chromium.connectOverCDP(session.cdpUrl);
        try {
          const context = browser.contexts()[0] ?? (await browser.newContext());
          const page = context.pages()[0] ?? (await context.newPage());

          // 3. Do the work.
          await page.goto("https://example.com");
          console.log(await page.title());
        } finally {
          await browser.close(); // ends your connection; the session is still running
        }
      } finally {
        // 4. Stop the session. This is what ends billing.
        await fetch(`${API}/v1/browser/session?sessionId=${session.sessionId}`, {
          method: "DELETE",
          headers,
        });
      }
      ```

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

      API = "https://api.driver.dev"
      headers = {"Authorization": f"Bearer {os.environ['DRIVER_API_KEY']}"}

      # 1. Create a session. Every field is optional; this one asks for a US browser for ten minutes.
      session = requests.post(f"{API}/v1/browser/session", headers=headers, json={"country": "US", "duration": 600})
      session.raise_for_status()
      session = session.json()

      try:
          # 2. Connect over CDP and reuse the browser's own context and tab.
          with sync_playwright() as p:
              browser = p.chromium.connect_over_cdp(session["cdpUrl"])
              context = browser.contexts[0] if browser.contexts else browser.new_context()
              page = context.pages[0] if context.pages else context.new_page()

              # 3. Do the work.
              page.goto("https://example.com")
              print(page.title())
              browser.close()
      finally:
          # 4. Stop the session. This is what ends billing.
          requests.delete(
              f"{API}/v1/browser/session",
              headers=headers,
              params={"sessionId": session["sessionId"]},
          )
      ```

      ```bash cURL theme={"dark"}
      # Create
      curl -s -X POST https://api.driver.dev/v1/browser/session \
        -H "Authorization: Bearer $DRIVER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"country":"US","duration":600}'
      # → {"sessionId":"…","status":"active","cdpUrl":"wss://…", …}

      # Stop
      curl -s -X DELETE "https://api.driver.dev/v1/browser/session?sessionId=$SESSION_ID" \
        -H "Authorization: Bearer $DRIVER_API_KEY"
      ```
    </CodeGroup>
  </Step>

  <Step title="Watch it run">
    Every session has a page under **Telemetry → Sessions** in the dashboard: live view while it runs, then the record (node, country, bandwidth, cost). See [Live view](/docs/sessions/live-view). From the API, `GET /v1/browser/session?sessionId=…` shows `status` `completed`, `stoppedAt`, and `bandwidthBytes` once it's done.
  </Step>
</Steps>

## What happened

The create call returned once Chrome was reachable over CDP, a few seconds in. It waits up to 20 s (three minutes on some machines), then answers `504`. `cdpUrl` is a WebSocket endpoint to that one browser. `connectOverCDP` attaches to it, which is why there's no `chromium.launch()` and nothing to download.

The browser came up with one context and one tab. Reusing them looks like a person who opened Chrome and started browsing. Creating extra contexts is one of the things sites notice.

Stopping the session releases the machine and closes the bill. Sessions also stop on their own at `duration`, an hour by default.

## Next

* [For agents](/docs/start/for-agents) if a coding agent is doing the integration.
* [Create a session](/docs/sessions/create#options) for every field: browser type, location, proxies, profiles, window size.
* [Best practices](/docs/start/best-practices) before you scale up.
* [Browser pools](/docs/sessions/pools) when a few seconds of start-up per browser is too slow.
