> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-docs-website-deploy-hook.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Pre-configure pools of reserved browsers for immediate acquisition

Browser pools let you maintain a set of reserved, identical browsers ready for immediate use. Use them to set your preferred browser configuration in advance, allowing you to minimize browser start-up latency and scale your workloads in production.

## How browser pools work

Browser pools are a way to pre-configure a fixed set of browsers without being charged for them until they are used (i.e. `acquired`). All browsers in the pool share the same settings upon instantiation.

<Steps>
  <Step title="Declare a pool">
    First, declare a pool of browsers with your specified configuration. The pool takes time to fill (see [fill rate per minute](https://www.kernel.sh/docs/api-reference/browser-pools/create-a-browser-pool#body-fill-rate-per-minute)), so declare your pool outside your browser automation / agent runtime logic.

    <Info>
      Pool declarations should be decoupled from browser runtime logic for the best performance.
    </Info>
  </Step>

  <Step title="Acquire a browser">
    You can acquire a browser as soon as you've created a pool. The request returns immediately if a browser is available, or waits until one becomes available.

    The total number of browsers is fixed to the `size` specified upon browser pool creation. When you acquire a browser, the pool's available count is decremented by one. When you release a browser, the pool's available count is incremented by one.

    <Info>
      Put differently, the pool does not top up when you acquire a browser: browsers are "borrowed" from the pool and must be returned when you're done with them, either by [releasing them](#release-a-browser) or allowing them to [timeout](#timeout-behavior).
    </Info>
  </Step>

  <Step title="Release a browser">
    When you're done with a browser, release it back to the pool. this step is important; otherwise, the browser will continue to be in an `acquired` state until it times out.

    <Info>
      Failing to release browsers may result in unexpected latency when acquiring future browsers.
    </Info>
  </Step>
</Steps>

## Create a pool of reserved browsers

Create a browser pool with a specified size and configuration. All browsers in the pool share the same settings.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import Kernel from '@onkernel/sdk';

  const kernel = new Kernel();

  const pool = await kernel.browserPools.create({
    name: "my-pool",
    size: 10,
    stealth: true,
    headless: false,
    timeout_seconds: 600,
    start_url: "https://example.com",
    viewport: {
      width: 1280,
      height: 800
    }
  });

  console.log(pool.id);
  ```

  ```python Python theme={null}
  from kernel import Kernel

  kernel = Kernel()

  pool = kernel.browser_pools.create(
      name="my-pool",
      size=10,
      stealth=True,
      headless=False,
      timeout_seconds=600,
      start_url="https://example.com",
      viewport={
          "width": 1280,
          "height": 800
      }
  )

  print(pool.id)
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"

  	"github.com/kernel/kernel-go-sdk"
  	"github.com/kernel/kernel-go-sdk/shared"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	pool, err := client.BrowserPools.New(ctx, kernel.BrowserPoolNewParams{
  		Name:           kernel.String("my-pool"),
  		Size:           10,
  		Stealth:        kernel.Bool(true),
  		Headless:       kernel.Bool(false),
  		TimeoutSeconds: kernel.Int(600),
  		StartURL:       kernel.String("https://example.com"),
  		Viewport: shared.BrowserViewportParam{
  			Width:  1280,
  			Height: 800,
  		},
  	})
  	if err != nil {
  		panic(err)
  	}

  	fmt.Println(pool.ID)
  }
  ```
</CodeGroup>

### Pool configuration options

Pools can be pre-configured with options like start url, custom extensions, supported viewports, residential proxies, profiles, and more. See the [API reference](https://kernel.sh/docs/api-reference/browser-pools/create-a-browser-pool) for more details.

A profile attached to a pool is loaded read-only: pooled browsers never persist changes back to the profile, so `save_changes` does not apply to pools (it is silently ignored if sent). To capture profile state, use a single browser session with `save_changes` instead — see [Profiles](/auth/profiles).

## Acquire a browser

Acquire a browser from the pool. The request returns immediately if a browser is available, or waits until one becomes available. The `acquire_timeout_seconds` parameter controls how long to wait; it defaults to the calculated time it would take to fill the pool at the pool's configured [fill rate](https://kernel.sh/docs/api-reference/browser-pools/create-a-browser-pool#body-fill-rate-per-minute).

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const browser = await kernel.browserPools.acquire("my-pool", {
    acquire_timeout_seconds: 30,
  });

  console.log(browser.session_id);
  console.log(browser.cdp_ws_url);
  ```

  ```python Python theme={null}
  browser = kernel.browser_pools.acquire(
      "my-pool",
      acquire_timeout_seconds=30,
  )

  print(browser.session_id)
  print(browser.cdp_ws_url)
  ```

  ```go Go theme={null}
  browser, err := client.BrowserPools.Acquire(ctx, "my-pool", kernel.BrowserPoolAcquireParams{
  	AcquireTimeoutSeconds: kernel.Int(30),
  })
  if err != nil {
  	panic(err)
  }

  fmt.Println(browser.SessionID)
  fmt.Println(browser.CdpWsURL)
  ```
</CodeGroup>

The acquired browser includes all the same properties as a regular browser session, including `cdp_ws_url` for CDP connections and `browser_live_view_url` for live viewing.

### Timeout behavior

Browsers remain in the pool indefinitely until acquired. Once acquired, the pool's `timeout_seconds` applies just like a [regular browser timeout](/browsers/termination#automatic-deletion-via-timeout)—if the browser is idle (no CDP or live view connection) for longer than the timeout, it is destroyed and **not** returned to the pool. The pool will automatically create a replacement browser at the pool's configured [fill rate](https://kernel.sh/docs/api-reference/browser-pools/create-a-browser-pool#body-fill-rate-per-minute).

## Release a browser

When you're done with a browser, release it back to the pool. By default, the browser instance is reused. Set `reuse: false` to destroy it and create a fresh one.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  await kernel.browserPools.release("my-pool", {
    session_id: browser.session_id,
    reuse: true,
  });
  ```

  ```python Python theme={null}
  kernel.browser_pools.release(
      "my-pool",
      session_id=browser.session_id,
      reuse=True,
  )
  ```

  ```go Go theme={null}
  if err := client.BrowserPools.Release(ctx, "my-pool", kernel.BrowserPoolReleaseParams{
  	SessionID: browser.SessionID,
  	Reuse:     kernel.Bool(true),
  }); err != nil {
  	panic(err)
  }
  ```
</CodeGroup>

## Update a pool

Update the pool configuration. By default, all idle browsers are discarded and rebuilt with the new configuration.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const updatedPool = await kernel.browserPools.update("my-pool", {
    size: 20,
    stealth: true,
  });
  ```

  ```python Python theme={null}
  updated_pool = kernel.browser_pools.update(
      "my-pool",
      size=20,
      stealth=True,
  )
  ```

  ```go Go theme={null}
  updatedPool, err := client.BrowserPools.Update(ctx, "my-pool", kernel.BrowserPoolUpdateParams{
  	Size:    20,
  	Stealth: kernel.Bool(true),
  })
  if err != nil {
  	panic(err)
  }
  _ = updatedPool
  ```
</CodeGroup>

<Info>
  The `size` parameter is always required when updating a pool, even if you only want to change other settings.
</Info>

By default, updating a pool discards all idle browsers and rebuilds them with the new configuration. Set `discard_all_idle: false` to keep existing idle browsers and only apply the new configuration to newly created browsers.

## Flush idle browsers

Destroy all idle browsers in the pool. Acquired browsers are not affected. The pool will automatically refill with the pool's specified configuration.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  await kernel.browserPools.flush("my-pool");
  ```

  ```python Python theme={null}
  kernel.browser_pools.flush("my-pool")
  ```

  ```go Go theme={null}
  if err := client.BrowserPools.Flush(ctx, "my-pool"); err != nil {
  	panic(err)
  }
  ```
</CodeGroup>

## Get pool details

Retrieve the current status and configuration of a pool.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const pool = await kernel.browserPools.retrieve("my-pool");

  console.log(pool.available_count);
  console.log(pool.acquired_count);
  ```

  ```python Python theme={null}
  pool = kernel.browser_pools.retrieve("my-pool")

  print(pool.available_count)
  print(pool.acquired_count)
  ```

  ```go Go theme={null}
  pool, err := client.BrowserPools.Get(ctx, "my-pool")
  if err != nil {
  	panic(err)
  }

  fmt.Println(pool.AvailableCount)
  fmt.Println(pool.AcquiredCount)
  ```
</CodeGroup>

## List pools

List all browser pools in your organization.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const pools = await kernel.browserPools.list();

  for (const pool of pools) {
    console.log(pool.name, pool.available_count);
  }
  ```

  ```python Python theme={null}
  pools = kernel.browser_pools.list()

  for pool in pools:
      print(pool.name, pool.available_count)
  ```

  ```go Go theme={null}
  pools, err := client.BrowserPools.List(ctx)
  if err != nil {
  	panic(err)
  }

  for _, pool := range *pools {
  	fmt.Println(pool.Name, pool.AvailableCount)
  }
  ```
</CodeGroup>

## Delete a pool

Delete a browser pool and all browsers in it. By default, deletion is blocked if browsers are currently acquired. Use `force: true` to terminate acquired browsers and force deletion.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  // Delete a pool (fails if browsers are acquired)
  await kernel.browserPools.delete("my-pool");

  // Force delete even if browsers are acquired
  await kernel.browserPools.delete("my-pool", { force: true });
  ```

  ```python Python theme={null}
  # Delete a pool (fails if browsers are acquired)
  kernel.browser_pools.delete("my-pool")

  # Force delete even if browsers are acquired
  kernel.browser_pools.delete("my-pool", force=True)
  ```

  ```go Go theme={null}
  // Delete a pool (fails if browsers are acquired)
  if err := client.BrowserPools.Delete(ctx, "my-pool", kernel.BrowserPoolDeleteParams{}); err != nil {
  	panic(err)
  }

  // Force delete even if browsers are acquired
  if err := client.BrowserPools.Delete(ctx, "my-pool", kernel.BrowserPoolDeleteParams{
  	Force: kernel.Bool(true),
  }); err != nil {
  	panic(err)
  }
  ```
</CodeGroup>

## Full example

This example assumes you've already created a pool named "my-pool". In practice, you'd create pools once (via the SDK, CLI, or dashboard) and then acquire from them repeatedly.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import Kernel from '@onkernel/sdk';
  import { chromium } from 'playwright';

  const kernel = new Kernel();

  // Acquire a browser from an existing pool
  const kernelBrowser = await kernel.browserPools.acquire("my-pool", {});

  try {
    // Connect via CDP
    const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url);
    const context = browser.contexts()[0];
    const page = context.pages()[0];

    await page.goto('https://example.com');
    const title = await page.title();
    console.log(title);
  } finally {
    // Release back to pool for reuse
    await kernel.browserPools.release("my-pool", {
      session_id: kernelBrowser.session_id,
    });
  }
  ```

  ```python Python theme={null}
  import asyncio
  from kernel import Kernel
  from playwright.async_api import async_playwright

  kernel = Kernel()

  async def main():
      # Acquire a browser from an existing pool
      kernel_browser = kernel.browser_pools.acquire("my-pool")

      async with async_playwright() as playwright:
          try:
              # Connect via CDP
              browser = await playwright.chromium.connect_over_cdp(kernel_browser.cdp_ws_url)
              context = browser.contexts[0]
              page = context.pages[0]

              await page.goto('https://example.com')
              title = await page.title()
              print(title)
          finally:
              # Release back to pool for reuse
              kernel.browser_pools.release(
                  "my-pool",
                  session_id=kernel_browser.session_id,
              )

  asyncio.run(main())
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"

  	"github.com/kernel/kernel-go-sdk"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	// Acquire a browser from an existing pool
  	kernelBrowser, err := client.BrowserPools.Acquire(ctx, "my-pool", kernel.BrowserPoolAcquireParams{})
  	if err != nil {
  		panic(err)
  	}

  	defer func() {
  		// Release back to pool for reuse
  		if err := client.BrowserPools.Release(ctx, "my-pool", kernel.BrowserPoolReleaseParams{
  			SessionID: kernelBrowser.SessionID,
  		}); err != nil {
  			panic(err)
  		}
  	}()

  	response, err := client.Browsers.Playwright.Execute(ctx, kernelBrowser.SessionID, kernel.BrowserPlaywrightExecuteParams{
  		Code: `
  			await page.goto('https://example.com');
  			return await page.title();
  		`,
  	})
  	if err != nil {
  		panic(err)
  	}

  	fmt.Println(response.Result)
  }
  ```
</CodeGroup>

## API reference

For more details on all available endpoints and parameters, see the [Browser Pools API reference](https://kernel.sh/docs/api-reference/browser-pools/list-browser-pools).
