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

# Quickstart - Controlling a Browser in a Runloop Devbox

> Learn how to control a browser programmatically inside a Runloop Devbox using the Runloop SDK

export const ExampleRepoLink = props => {
  return <Info><h3><a href={props.link}>Full example on GitHub</a></h3></Info>;
};

<Tip>Start with the [Runloop Quickstart](/docs/tutorials/quickstart) to use the examples below.</Tip>

## Introduction

This guide will walk you through using the **Runloop SDK** to control a browser inside a **Runloop Devbox**. The Runloop API provides a **browser-ready Devbox**, enabling AI agents to interact with web pages programmatically.

<Steps>
  <Step title="Setup Your Environment">
    Follow the instructions in the [Runloop Quickstart](/docs/tutorials/quickstart) to set up your environment to use the Runloop SDK.
  </Step>

  <Step title="Create a Devbox and Start the Browser">
    Set up your **browser-ready Devbox** and obtain the connection details:

    <CodeGroup>
      ```python Python theme={null}
      # Create a Devbox with a browser instance
      browser = runloop.api.devboxes.browsers.create()

      # Wait for the Devbox to be fully running
      devbox = runloop.devbox.from_id(browser.devbox.id)                             
      await devbox.await_running()

      # View your remote browser here:
      print(browser.live_view_url)

      # Connect to your browser here:
      print(browser.connection_url)
      ```

      ```typescript TypeScript theme={null}
      // Create a Devbox with a browser instance
      const browser = await runloop.api.devboxes.browsers.create();

      // Wait for the Devbox to be fully running
      const devbox = await runloop.devbox.fromId(browser.devbox.id);
      await devbox.awaitRunning();

      // View your remote browser here:
      console.log(browser.live_view_url);

      // Connect to your browser here:
      console.log(browser.connection_url);
      ```
    </CodeGroup>

    <Note>
      The URLs above are both to localhost by default, and will be
      visible only inside the devbox.  This is all you need if you are
      running your AI agent to control the browser from within the
      Devbox.  If you need to access either URL remotely, you will need
      to also configure a [tunnel](/docs/devboxes/tunnels).
    </Note>
  </Step>

  <Step title="Programmatically Controlling the Browser">
    To interact with the browser, you can use automation tools like **Selenium, Puppeteer, or Playwright**. Here's an example using **Playwright's Chrome DevTools Protocol (CDP)**:

    <CodeGroup>
      ```python Python theme={null}
      from playwright.async_api import async_playwright

      # Initialize playwright context manager 
      playwright = await async_playwright().start()

      # Connect to your remote browser and create browser context
      browser = await playwright.chromium.connect_over_cdp(url)
      context = await browser.new_context()

      # Accesses pages in the browser context's list of pages
      page = context.pages[0]
      ```

      ```typescript TypeScript theme={null}
      import { chromium } from 'playwright';

      // Initialize playwright and connect to browser
      const browser = await chromium.connectOverCDP(url);

      // Create your browser context
      const context = await browser.newContext();

      // Accesses pages in the browser context's list of pages                   
      const page = context.pages()[0];

      ```
    </CodeGroup>
  </Step>

  <Step title="Defining a Browser Tool for Your AI Agent">
    You can create **custom tools** for AI agents to interact with the browser programmatically. Here's an example of a **navigation tool** using Playwright:

    <CodeGroup>
      ```python Python theme={null}
      from playwright.async_api import async_playwright

      class NavigateTool:
          async def __call__(self, *, url: str):
              async with async_playwright() as p:
                  browser = await p.chromium.launch()
                  page = await browser.new_page()
                  await page.goto(url)
                  content = await page.content()
                  await browser.close()
                  return {
                    "output": f"Navigated to {url}",
                    "content": content[:500]
                  }

          def to_params(self):
              return {
                  "name": "navigate_tool",
                  "description": "Navigates to a URL and retrieves content.",
                  "input_schema": {
                      "type": "object",
                      "properties": {"url": {"type": "string"}},
                      "required": ["url"],
                  },
              }


      ```

      ```typescript TypeScript theme={null}
      import { chromium, Browser, Page } from 'playwright';

      class NavigateTool {
          async call({ url }: { url: string }) {
              const browser = await chromium.launch();
              const page = await browser.newPage();
              await page.goto(url);
              const content = await page.content();
              await browser.close();
              return { 
                  output: `Navigated to ${url}`, 
                  content: content.slice(0, 500) 
              };
          }

          toParams() {
              return {
                  name: "navigate_tool",
                  description: "Navigates to a URL and retrieves content.",
                  input_schema: {
                      type: "object",
                      properties: { url: { type: "string" } },
                      required: ["url"],
                  },
              };
          }
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Passing Tools to an AI Agent">
    Now you can pass this tool to an AI agent, enabling it to use the browser autonomously:

    <CodeGroup>
      ```python Python theme={null}
      import Anthropic

      anthropic_client = Anthropic(
          api_key="your-anthropic-api-key")

      tool_instance = NavigateTool()

      response = client.messages.create(
          model=model,
          max_tokens=max_tokens,
          messages=messages,
          tools=[tool_instance.to_params()]
      )

      ```

      ```typescript TypeScript theme={null}
      import Anthropic from '@anthropic-ai/sdk';

      const anthropicClient = new Anthropic({
        apiKey: 'your-anthropic-api-key'
      });

      const toolInstance = new NavigateTool();

      const response = await anthropicClient.messages.create({
          model,
          maxTokens,
          messages,
          tools: [toolInstance.toParams()]
      });
      ```
    </CodeGroup>

    <Note>
      Different LLM providers have their own specific formats and requirements for defining and passing tools. Make sure to reference your LLM provider's documentation for the correct implementation details of tool schemas and function calling.
    </Note>
  </Step>

  <Step title="Properly Freeing Resources">
    To ensure efficient resource management, **always shut down the Devbox** when you're done:

    <CodeGroup>
      ```python Python theme={null}
      await devbox.shutdown()
      ```

      ```typescript TypeScript theme={null}
      await devbox.shutdown();
      ```
    </CodeGroup>
  </Step>
</Steps>

## Additional Resources

* [Runloop GitHub Repository](https://github.com/runloopai/examples) - Explore more examples.
* [Runloop API Documentation](https://docs.runloop.ai) - Official API reference.
