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

# Browser

> Configure Stagehand on Browserbase or locally

You get a browser from one of Stagehand's browser factories and hand it to `Stagehand.create()`. There are three ways to get one:

* **Browserbase (`browserbase.launch`):** Cloud-managed browser infrastructure optimized for production web automation at scale
* **Local (`localBrowser.launch`):** Run browsers directly on your machine for development and debugging
* **Attach over CDP (`localBrowser.connect`):** Attach to any Chromium browser you are already running, by URL

Stagehand closes only the browsers it launched, so `browser.close()` is yours to call.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const cloud = await browserbase.launch({ apiKey: process.env.BROWSERBASE_API_KEY });
    const local = await localBrowser.launch({ headless: true });
    const connected = await localBrowser.connect({ cdpUrl: "http://127.0.0.1:9222" });

    await Stagehand.create({ browser: cloud });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    cloud = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
    local = await local_browser.launch(headless=True)
    connected = await local_browser.connect(cdp_url="http://127.0.0.1:9222")

    await Stagehand.create(browser=cloud)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")

    cloud, _ := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{APIKey: apiKey})
    stagehand.Create(ctx, stagehand.CreateOptions{Browser: cloud})

    // Or start a browser on your machine, or attach to one you already run:
    stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{Headless: true})
    stagehand.ConnectLocalBrowser(ctx, stagehand.LocalBrowserConnectOptions{
    	CDPURL: "http://127.0.0.1:9222",
    })
    ```
  </Tab>
</Tabs>

## Browserbase environment

Browserbase provides managed cloud browser infrastructure optimized for web automation at scale. It offers advanced features like stealth mode, proxy support, and persistent contexts. It is also the only browser that supports [server-side caching](/v4/best-practices/caching) and the [Model Gateway](/v4/configuration/models#model-gateway).

<Card icon="cloud" title="Browserbase" href="https://docs.browserbase.com" description="Session settings, proxies, stealth mode, and session recordings.">
  Read the Browserbase documentation for the full set of session settings Stagehand passes through.
</Card>

### Multi-region support

Browserbase runs browsers in four regions, so you can cut latency by starting the browser near your users or your target site, and keep session data in a required jurisdiction. Set the region on `browserbase.launch()`; `us-west-2` is the default.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { browserbase, Stagehand } from "@browserbasehq/stagehand";

    const browser = await browserbase.launch({
      apiKey: process.env.BROWSERBASE_API_KEY,
      region: "eu-central-1", // Browser runs in Frankfurt
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    from stagehand import Stagehand, browserbase

    browser = await browserbase.launch(
        api_key=os.environ["BROWSERBASE_API_KEY"],
        region="eu-central-1",  # Browser runs in Frankfurt
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")
    region := stagehand.BrowserbaseRegionEUCentral1 // Browser runs in Frankfurt

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{
    	APIKey: apiKey,
    	Region: &region,
    })
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

Supported regions: `us-west-2` (default), `us-east-1`, `eu-central-1`, `ap-southeast-1`.

<Note>
  You normally do not need to configure a regional endpoint. Stagehand picks the API deployment that matches your session's region on its own, including for [server-side caching](/v4/best-practices/caching). An explicit Stagehand `apiUrl` overrides that regional selection.
</Note>

### API endpoint overrides

Browserbase session management and Stagehand managed services use separate APIs. Both default to the production service. The SDKs do not read either URL from an environment variable. Pass `baseUrl` to Browserbase and `apiUrl` to Stagehand when testing another deployment. Use the service origin without `/v1`.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const browser = await browserbase.launch({
      apiKey: process.env.BROWSERBASE_API_KEY,
      baseUrl: "https://api.dev.browserbase.com",
    });

    const stagehand = await Stagehand.create({
      browser,
      apiUrl: "https://api.stagehand.dev.browserbase.com",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    browser = await browserbase.launch(
        api_key=os.environ["BROWSERBASE_API_KEY"],
        base_url="https://api.dev.browserbase.com",
    )

    stagehand = await Stagehand.create(
        browser=browser,
        api_url="https://api.stagehand.dev.browserbase.com",
    )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")
    stagehandAPIURL := "https://api.stagehand.dev.browserbase.com"

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{
    	APIKey:  apiKey,
    	BaseURL: "https://api.dev.browserbase.com",
    })
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    	APIURL:  &stagehandAPIURL,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

The Browserbase URL controls extension, session, and connection requests that `browserbase.launch()` and `browserbase.connect()` make. The Stagehand URL controls Model Gateway and managed-cache requests that the browser runtime makes.

### Environment variables

Before getting started, set up the required environment variables:

```bash theme={null}
export BROWSERBASE_API_KEY=your_api_key_here
```

<Tip>
  Get your API key from the [Browserbase Dashboard](https://browserbase.com/overview). Stagehand does not read this variable for you: read it in your own code and pass it to `browserbase.launch()`.
</Tip>

### Using Stagehand with Browserbase

#### Basic setup

The simplest way to get started is with default settings. `browserbase.launch()` needs nothing but your Browserbase API key:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { browserbase, Stagehand } from "@browserbasehq/stagehand";

    const browser = await browserbase.launch({
      apiKey: process.env.BROWSERBASE_API_KEY,
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    from stagehand import Stagehand, browserbase

    browser = await browserbase.launch(
        api_key=os.environ["BROWSERBASE_API_KEY"],
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{APIKey: apiKey})
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

#### Advanced configuration

Configure browser settings, proxy support, and other session parameters directly on `browserbase.launch()`:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { browserbase, Stagehand } from "@browserbasehq/stagehand";

    const browser = await browserbase.launch({
      apiKey: process.env.BROWSERBASE_API_KEY,
      proxies: true,
      region: "us-west-2",
      browserSettings: {
        viewport: { width: 1920, height: 1080 },
        blockAds: true,
      },
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    from stagehand import BrowserbaseBrowserSettings, Stagehand, browserbase

    browser = await browserbase.launch(
        api_key=os.environ["BROWSERBASE_API_KEY"],
        proxies=True,
        region="us-west-2",
        browser_settings=BrowserbaseBrowserSettings.model_validate({
            "viewport": {"width": 1920, "height": 1080},
            "block_ads": True,
        }),
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")
    region := stagehand.BrowserbaseRegionUSWest2
    proxies := stagehand.BrowserbaseProxyEnabled(true)
    blockAds := true
    width, height := 1920.0, 1080.0

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{
    	APIKey:   apiKey,
    	Proxies:  &proxies,
    	Region:   &region,
    	BrowserSettings: &stagehand.BrowserbaseBrowserSettings{
    		Viewport: &stagehand.BrowserbaseViewport{Width: &width, Height: &height},
    		BlockAds: &blockAds,
    	},
    })
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Accordion title="Advanced Browserbase configuration example">
  <Tabs>
    <Tab title="TypeScript">
      ```typescript theme={null}
      const browser = await browserbase.launch({
        apiKey: process.env.BROWSERBASE_API_KEY,
        proxies: true,
        region: "us-west-2",
        timeout: 3600, // 1 hour session timeout
        keepAlive: true, // Available on Startup plan
        browserSettings: {
          verified: false, // this is a Scale Plan feature - reach out to support@browserbase.com to enable
          blockAds: true,
          solveCaptchas: true,
          recordSession: false,
          viewport: {
            width: 1920,
            height: 1080,
          },
        },
        userMetadata: {
          userId: "automation-user-123",
          environment: "production",
        },
      });
      ```
    </Tab>

    <Tab title="Python">
      ```python theme={null}
      browser = await browserbase.launch(
          api_key=os.environ["BROWSERBASE_API_KEY"],
          proxies=True,
          region="us-west-2",
          timeout=3600,  # 1 hour session timeout
          keep_alive=True,  # Available on Startup plan
          browser_settings=BrowserbaseBrowserSettings.model_validate({
              # verified is a Scale Plan feature: contact support@browserbase.com to enable
              "verified": False,
              "block_ads": True,
              "solve_captchas": True,
              "record_session": False,
              "viewport": {"width": 1920, "height": 1080},
          }),
          user_metadata={
              "user_id": "automation-user-123",
              "environment": "production",
          },
      )

      stagehand = await Stagehand.create(browser=browser)
      ```
    </Tab>

    <Tab title="Go">
      ```go theme={null}
      apiKey := os.Getenv("BROWSERBASE_API_KEY")
      region := stagehand.BrowserbaseRegionUSWest2
      proxies := stagehand.BrowserbaseProxyEnabled(true)
      timeout := 3600.0 // 1 hour session timeout
      keepAlive := true // Available on Startup plan

      // verified is a Scale Plan feature: contact support@browserbase.com to enable
      verified := false
      blockAds := true
      solveCaptchas := true
      recordSession := false
      width, height := 1920.0, 1080.0

      browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{
      	APIKey:    apiKey,
      	Proxies:   &proxies,
      	Region:    &region,
      	Timeout:   &timeout,
      	KeepAlive: &keepAlive,
      	BrowserSettings: &stagehand.BrowserbaseBrowserSettings{
      		Verified:      &verified,
      		BlockAds:      &blockAds,
      		SolveCaptchas: &solveCaptchas,
      		RecordSession: &recordSession,
      		Viewport:      &stagehand.BrowserbaseViewport{Width: &width, Height: &height},
      	},
      	UserMetadata: map[string]json.RawMessage{
      		"userId":      json.RawMessage(`"automation-user-123"`),
      		"environment": json.RawMessage(`"production"`),
      	},
      })
      if err != nil {
      	return err
      }

      client, err := stagehand.Create(ctx, stagehand.CreateOptions{
      	Browser: browser,
      })
      if err != nil {
      	return err
      }

      defer func() { err = errors.Join(err, client.Close(ctx)) }()
      ```
    </Tab>
  </Tabs>
</Accordion>

<Note>
  When `browserbase.launch()` creates a session without an `extensionId`, Stagehand uploads its extension and starts the session against it. Stagehand deletes the upload when you call `browser.close()` for a launched session without `keepAlive` enabled.
</Note>

### Alternative: Browserbase SDK

If you prefer to manage sessions directly, create the session with the Browserbase SDK and hand Stagehand the resulting session through `browserbase.connect()`:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Browserbase } from "@browserbasehq/sdk";
    import { browserbase, Stagehand } from "@browserbasehq/stagehand";

    const bb = new Browserbase({
      apiKey: process.env.BROWSERBASE_API_KEY!,
    });

    const session = await bb.sessions.create({
      projectId: process.env.BROWSERBASE_PROJECT_ID!,
      // Add configuration options here
    });

    // Attach by session ID so the browser keeps its Browserbase identity
    const browser = await browserbase.connect({
      apiKey: process.env.BROWSERBASE_API_KEY!,
      sessionId: session.id,
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    from browserbase import Browserbase

    from stagehand import Stagehand
    from stagehand import browserbase as stagehand_browserbase

    bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])

    session = bb.sessions.create(
        project_id=os.environ["BROWSERBASE_PROJECT_ID"],
        # Add configuration options here
    )

    # Attach by session ID so the browser keeps its Browserbase identity
    browser = await stagehand_browserbase.connect(
        api_key=os.environ["BROWSERBASE_API_KEY"],
        session_id=session.id,
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    // Create the session with your Browserbase client of choice, then attach by
    // session ID so the browser keeps its Browserbase identity.
    session, err := createBrowserbaseSession(ctx)
    if err != nil {
    	return err
    }

    browser, err := stagehand.ConnectBrowserbase(ctx, stagehand.BrowserbaseConnectOptions{
    	APIKey:    os.Getenv("BROWSERBASE_API_KEY"),
    	SessionID: session.ID,
    })
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

#### Connecting to an existing session

`localBrowser.connect()` attaches to any Chromium browser that is already running and exposing a DevTools endpoint, whether that is a Browserbase session you created earlier or a browser on your own machine:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { localBrowser, Stagehand } from "@browserbasehq/stagehand";

    const browser = await localBrowser.connect({
      cdpUrl: "wss://connect.browserbase.com/?apiKey=...&sessionId=...",
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from stagehand import Stagehand, local_browser

    browser = await local_browser.connect(
        cdp_url=connect_url,  # from your Browserbase session
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    browser, err := stagehand.ConnectLocalBrowser(ctx, stagehand.LocalBrowserConnectOptions{
    	CDPURL: "wss://connect.browserbase.com/?apiKey=...&sessionId=...",
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, browser.Close(ctx)) }()

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{Browser: browser})
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Note>
  Stagehand never closes a browser it did not launch. When you connect to an existing browser, `close()` tears down the Stagehand connection and leaves the browser running.
</Note>

## Local environment

Run browsers directly on your machine when you want full control over the browser process and its launch flags. This suits development, debugging, and custom browser setups.

### Environment comparison

| Feature                     | Browserbase                   | Local                          |
| --------------------------- | ----------------------------- | ------------------------------ |
| **Scalability**             | High (cloud-managed)          | Limited (local resources)      |
| **Stealth features**        | Advanced fingerprinting       | Basic stealth                  |
| **Proxy support**           | Built-in residential proxies  | Manual configuration           |
| **Session persistence**     | Cloud context storage         | File-based user data           |
| **Geographic distribution** | Multi-region deployment       | Single machine                 |
| **Debugging**               | Session recordings and logs   | Direct DevTools access         |
| **Setup complexity**        | API key only                  | Browser installation required  |
| **Server-side caching**     | Supported                     | Not available                  |
| **Model Gateway**           | Supported                     | Not available                  |
| **Cost**                    | Usage-based pricing           | Infrastructure and maintenance |
| **Best for**                | Production, scale, compliance | Development, debugging         |

### Basic local setup

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { localBrowser, Stagehand } from "@browserbasehq/stagehand";

    const browser = await localBrowser.launch();
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from stagehand import Stagehand, local_browser

    browser = await local_browser.launch()
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{})
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

### Advanced local configuration

Customize browser launch options for local development:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { localBrowser, Stagehand } from "@browserbasehq/stagehand";

    const browser = await localBrowser.launch({
      headless: false, // Show browser window
        devtools: true, // Open developer tools
        viewport: { width: 1280, height: 720 },
        executablePath: "/opt/google/chrome/chrome", // Custom Chrome path
        port: 9222, // Fixed CDP debugging port
        ignoreDefaultArgs: [
          "--disable-sync",
        ], // Remove specific default launch args
        args: [
          "--disable-web-security",
          "--allow-running-insecure-content",
        ],
        userDataDir: "./chrome-user-data", // Persist browser data
        preserveUserDataDir: true, // Keep data after closing
        chromiumSandbox: false, // Disable sandbox (adds --no-sandbox)
        ignoreHTTPSErrors: true, // Ignore certificate errors
        locale: "en-US", // Set browser language
        deviceScaleFactor: 1.0, // Display scaling
        hasTouch: false, // Emulate touch input
        proxy: {
          server: "http://proxy.example.com:8080",
          username: "user",
          password: "pass",
        },
        downloadsPath: "./downloads", // Download directory
        acceptDownloads: true, // Allow downloads
    });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from stagehand import Stagehand, local_browser

    browser = await local_browser.launch(
        headless=False,  # Show browser window
        devtools=True,  # Open developer tools
        viewport_width=1280,
        viewport_height=720,
        executable_path="/opt/google/chrome/chrome",  # Custom Chrome path
        port=9222,  # Fixed CDP debugging port
        ignore_default_args=["--disable-sync"],  # Remove specific default launch args
        args=[
            "--disable-web-security",
            "--allow-running-insecure-content",
        ],
        user_data_dir="./chrome-user-data",  # Persist browser data
        preserve_user_data_dir=True,  # Keep data after closing
        chromium_sandbox=False,  # Disable sandbox (adds --no-sandbox)
        ignore_https_errors=True,  # Ignore certificate errors
        locale="en-US",  # Set browser language
        device_scale_factor=1.0,  # Display scaling
        has_touch=False,  # Emulate touch input
        proxy_server="http://proxy.example.com:8080",
        proxy_username="user",
        proxy_password="pass",
        downloads_path="./downloads",  # Download directory
        accept_downloads=True,  # Allow downloads
    )
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    chromiumSandbox := false // Disable sandbox (adds --no-sandbox)
    acceptDownloads := true  // Allow downloads
    deviceScaleFactor := 1.0 // Display scaling

    browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{
    		Headless:       false,                            // Show browser window
    		Devtools:       true,                             // Open developer tools
    		Viewport:       &stagehand.LocalViewport{Width: 1280, Height: 720},
    		ExecutablePath: "/opt/google/chrome/chrome",      // Custom Chrome path
    		Port:           9222,                             // Fixed CDP debugging port
    		IgnoreDefaultArgs: &stagehand.IgnoreDefaultArgs{ // Remove specific default launch args
    			Args: []string{"--disable-sync"},
    		},
    		Args: []string{
    			"--disable-web-security",
    			"--allow-running-insecure-content",
    		},
    		UserDataDir:         "./chrome-user-data", // Persist browser data
    		PreserveUserDataDir: true,                 // Keep data after closing
    		ChromiumSandbox:     &chromiumSandbox,
    		IgnoreHTTPSErrors:   true,     // Ignore certificate errors
    		Locale:              "en-US",  // Set browser language
    		DeviceScaleFactor:   &deviceScaleFactor,
    		HasTouch:            false,    // Emulate touch input
    		Proxy: &stagehand.LocalProxyConfig{
    			Server:   "http://proxy.example.com:8080",
    			Username: "user",
    			Password: "pass",
    		},
    		DownloadsPath:    "./downloads", // Download directory
    		AcceptDownloads:  &acceptDownloads,
    })
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Note>
  Authenticated local proxies are not supported yet. Setting a proxy `username` or `password` raises in all three SDKs; a proxy `server` and `bypass` list work everywhere.
</Note>

#### Default local launch arguments

When Stagehand launches a local browser, it adds the following Chrome arguments before any values you pass in the launch `args`:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    [
      "--enable-unsafe-extension-debugging",
      "--remote-allow-origins=*",
      "--window-size=1280,800",
      "--enable-features=WebMCPTesting,DevToolsWebMCPSupport",
    ]
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    [
        "--enable-unsafe-extension-debugging",
        "--remote-allow-origins=*",
        "--window-size=1280,800",
        "--enable-features=WebMCPTesting,DevToolsWebMCPSupport",
    ]
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    []string{
    	"--enable-unsafe-extension-debugging",
    	"--remote-allow-origins=*",
    	"--window-size=1280,800",
    	"--enable-features=WebMCPTesting,DevToolsWebMCPSupport",
    }
    ```
  </Tab>
</Tabs>

<Note>
  `--enable-unsafe-extension-debugging` is required so the SDK can attach a CDP session to the service worker hosting Stagehand's runtime. Chrome blocks debugger access to extension targets without it. `--enable-features=WebMCPTesting,DevToolsWebMCPSupport` turns on the browser support behind [WebMCP](/v4/basics/webmcp). `--window-size` follows the `viewport` option and falls back to `1280,800` when you do not set one.
</Note>

A larger set of standard Chrome automation flags (disabling background networking, component updates, sync, translation, and similar) is prepended as well. To remove some of them, list the exact flags in the ignore-default-args option:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const browser = await localBrowser.launch({
      ignoreDefaultArgs: ["--disable-sync"],
      args: [
        "--disable-features=site-per-process,IsolateOrigins",
        "--renderer-process-limit=6",
      ],
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    browser = await local_browser.launch(
        ignore_default_args=["--disable-sync"],
        args=[
            "--disable-features=site-per-process,IsolateOrigins",
            "--renderer-process-limit=6",
        ],
    )

    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{
    	IgnoreDefaultArgs: &stagehand.IgnoreDefaultArgs{
    		Args: []string{"--disable-sync"},
    	},
    	Args: []string{
    		"--disable-features=site-per-process,IsolateOrigins",
    		"--renderer-process-limit=6",
    	},
    })
    if err != nil {
    	return err
    }

    stagehand.Create(ctx, stagehand.CreateOptions{Browser: browser})
    ```
  </Tab>
</Tabs>

Set the ignore-default-args option to `true` to remove all default arguments. Use a list to remove only exact matches while preserving the rest.

## Advanced configuration

### Keep alive

The keep-alive option controls whether the browser remains running after `close()` is called.

By default, Stagehand terminates the browser it launched and cleans up all resources when it shuts down. Turning keep-alive on keeps the browser running independently so you can reconnect to it later.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { browserbase, localBrowser, Stagehand } from "@browserbasehq/stagehand";

    const browser = await browserbase.launch({
      apiKey: process.env.BROWSERBASE_API_KEY,
      keepAlive: true,
    });
    const stagehand = await Stagehand.create({ browser });

    // The browser session continues running after close()
    await stagehand.close();
    await browser.close();

    // Later, reconnect to the same session over CDP
    const browser2 = await localBrowser.connect({ cdpUrl: existingConnectUrl });
    const stagehand2 = await Stagehand.create({ browser: browser2 });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    from stagehand import Stagehand, browserbase, local_browser

    browser = await browserbase.launch(
        api_key=os.environ["BROWSERBASE_API_KEY"],
        keep_alive=True,
    )
    stagehand = await Stagehand.create(browser=browser)

    # The browser session continues running after close()
    await stagehand.close()
    await browser.close()

    # Later, reconnect to the same session over CDP
    browser2 = await local_browser.connect(cdp_url=existing_connect_url)
    stagehand2 = await Stagehand.create(browser=browser2)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")
    keepAlive := true

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{
    	APIKey:    apiKey,
    	KeepAlive: &keepAlive,
    })
    if err != nil {
    	return err
    }
    client, err := stagehand.Create(ctx, stagehand.CreateOptions{Browser: browser})
    if err != nil {
    	return err
    }

    // The browser session continues running after Close()
    if err := client.Close(ctx); err != nil {
    	return err
    }
    if err := browser.Close(ctx); err != nil {
    	return err
    }

    // Later, reconnect to the same session over CDP
    browser2, err := stagehand.ConnectLocalBrowser(ctx, stagehand.LocalBrowserConnectOptions{
    	CDPURL: existingConnectURL,
    })
    if err != nil {
    	return err
    }

    client2, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser2,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client2.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

#### Behavior by environment

| Behavior              | keep-alive on                        | keep-alive off (default)                               |
| --------------------- | ------------------------------------ | ------------------------------------------------------ |
| **Browserbase**       | Session stays active after `close()` | Session is terminated via API                          |
| **Local**             | Chrome process continues running     | Chrome process is killed and temp profile is removed   |
| **Attached over CDP** | Always left running                  | Always left running (Stagehand never owns the browser) |

#### Local environment

When running locally with keep-alive on, Stagehand leaves the Chrome process running when your script exits. This is useful for debugging or for handing off a browser session to another process. Combine it with a fixed `port` so you can reattach with `localBrowser.connect()`.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const browser = await localBrowser.launch({
      keepAlive: true,
      headless: false,
      port: 9222,
    });
    const stagehand = await Stagehand.create({ browser });
    const [page] = await browser.context.pages();
    await page.goto("https://example.com");

    // Browser window stays open after the script exits
    await stagehand.close();
    await browser.close();
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    browser = await local_browser.launch(
        keep_alive=True,
        headless=False,
        port=9222,
    )
    stagehand = await Stagehand.create(browser=browser)
    page = (await browser.context.pages())[0]
    await page.goto("https://example.com")

    # Browser window stays open after the script exits
    await stagehand.close()
    await browser.close()
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{
    	KeepAlive: true,
    	Headless:  false,
    	Port:      9222,
    })
    if err != nil {
    	return err
    }
    client, err := stagehand.Create(ctx, stagehand.CreateOptions{Browser: browser})
    if err != nil {
    	return err
    }

    browserContext, err := browser.Context()
    if err != nil {
    	return err
    }
    pages, err := browserContext.Pages(ctx)
    if err != nil {
    	return err
    }
    if len(pages) == 0 {
    	return errors.New("Stagehand initialized without an active page")
    }
    page := pages[0]
    if _, err := page.Goto(ctx, "https://example.com", nil); err != nil {
    	return err
    }

    // Browser window stays open after the program exits
    return errors.Join(client.Close(ctx), browser.Close(ctx))
    ```
  </Tab>
</Tabs>

<Note>
  A temporary user data directory is deleted when a local browser shuts down. Set `userDataDir` (or turn on the preserve option) if you want the profile to survive.
</Note>

#### Browserbase environment

On Browserbase, keep-alive keeps the cloud session active so you can reconnect later with `browserbase.connect()`. This is useful for long-running workflows that span multiple script executions.

<Note>
  Keeping sessions alive is available on the Browserbase Startup plan and above.
</Note>

### Fixed CDP debugging port

Specify a fixed Chrome DevTools Protocol (CDP) debugging port instead of using a randomly assigned one.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { localBrowser, Stagehand } from "@browserbasehq/stagehand";

    const browser = await localBrowser.launch({ port: 9222 });
    const stagehand = await Stagehand.create({ browser });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from stagehand import Stagehand, local_browser

    browser = await local_browser.launch(port=9222)
    stagehand = await Stagehand.create(browser=browser)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    browser, err := stagehand.LaunchLocalBrowser(ctx, &stagehand.LocalBrowserLaunchOptions{Port: 9222})
    if err != nil {
    	return err
    }

    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser: browser,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Tip>
  Stagehand picks a random free port when you do not set one.
</Tip>

### DOM settle timeout

Configure how long Stagehand waits for the DOM to stabilize before taking actions. The default is 5000 ms.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const browser = await browserbase.launch({ apiKey: process.env.BROWSERBASE_API_KEY });
    const stagehand = await Stagehand.create({
      browser,
      domSettleTimeoutMs: 3000, // Wait up to 3 seconds for DOM to settle
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
    stagehand = await Stagehand.create(
        browser=browser,
        dom_settle_timeout_ms=3000,  # Wait up to 3 seconds for DOM to settle
    )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")
    domSettleTimeoutMs := 3000 // Wait up to 3 seconds for DOM to settle

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{APIKey: apiKey})
    if err != nil {
    	return err
    }
    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser:            browser,
    	DOMSettleTimeoutMs: &domSettleTimeoutMs,
    })
    if err != nil {
    	return err
    }

    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Note>
  DOM settling applies to `act()` calls that take a natural-language instruction. Replaying an observed `Action` skips the wait, and `observe()` and `extract()` read a snapshot of the page as they find it.
</Note>

#### What is DOM settling?

Before an instruction-based `act()` call runs, Stagehand waits for the page's network activity to go quiet, up to the timeout. That gives lazy-loaded content, JavaScript updates, and other dynamic rendering time to finish before the action targets an element.

#### When to adjust

Increase the DOM settle timeout for pages with:

* Heavy animations or transitions
* Lazy-loading or infinite scroll
* Dynamic JavaScript frameworks (React, Vue, Angular)
* Complex single-page applications

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    // For fast, static pages
    const fastBrowser = await browserbase.launch({ apiKey: process.env.BROWSERBASE_API_KEY });
    const fast = await Stagehand.create({
      browser: fastBrowser,
      domSettleTimeoutMs: 500, // Minimal wait
    });

    // For dynamic, animated pages
    const dynamicBrowser = await browserbase.launch({ apiKey: process.env.BROWSERBASE_API_KEY });
    const dynamic = await Stagehand.create({
      browser: dynamicBrowser,
      domSettleTimeoutMs: 5000, // Longer wait for stability
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # For fast, static pages
    fast_browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
    fast = await Stagehand.create(
        browser=fast_browser,
        dom_settle_timeout_ms=500,  # Minimal wait
    )

    # For dynamic, animated pages
    dynamic_browser = await browserbase.launch(api_key=os.environ["BROWSERBASE_API_KEY"])
    dynamic = await Stagehand.create(
        browser=dynamic_browser,
        dom_settle_timeout_ms=5000,  # Longer wait for stability
    )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    apiKey := os.Getenv("BROWSERBASE_API_KEY")

    browser, err := stagehand.LaunchBrowserbase(ctx, stagehand.BrowserbaseLaunchOptions{APIKey: apiKey})
    if err != nil {
    	return err
    }
    settleTimeout := 500 // Fast, static pages
    if os.Getenv("PAGE_PROFILE") == "dynamic" {
    	settleTimeout = 5000 // Dynamic, animated pages
    }
    client, err := stagehand.Create(ctx, stagehand.CreateOptions{
    	Browser:            browser,
    	DOMSettleTimeoutMs: &settleTimeout,
    })
    if err != nil {
    	return err
    }
    defer func() { err = errors.Join(err, client.Close(ctx)) }()
    ```
  </Tab>
</Tabs>

<Warning>
  Setting the DOM settle timeout too low may cause actions to fail on elements that aren't ready. Setting it too high increases execution time unnecessarily.
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Browserbase authentication errors">
    * Verify your `BROWSERBASE_API_KEY` is read and passed to `browserbase.launch()`
    * Check that your API key has the necessary permissions
    * Ensure your Browserbase account has sufficient credits
    * Remember that `browserbase.launch()` requires an API key: it fails without one
  </Accordion>

  <Accordion title="Local browser launch failures">
    * Install Chrome or Chromium on your system
    * Set the correct executable path for your Chrome installation, or set `CHROME_PATH`
    * Check that required dependencies are installed (Linux: `libnss3-dev libatk-bridge2.0-dev libgtk-3-dev libxss1 libasound2`)
    * If the extension fails to load, confirm nothing is stripping `--enable-unsafe-extension-debugging` from the launch arguments
  </Accordion>

  <Accordion title="Session timeout issues">
    * Increase the session timeout on `browserbase.launch()`
    * Turn on keep-alive for long-running sessions
    * Monitor session usage to avoid unexpected terminations
  </Accordion>
</AccordionGroup>
