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

# Browser Examples

> Real-world examples for the Browser API

## Create a Session

Start a remote browser session and get the CDP connection URL:

<CodeGroup>
  ```python Python theme={null}
  from tinyfish import TinyFish

  client = TinyFish()
  session = client.browser.sessions.create(url="https://example.com")

  print(f"Session: {session.session_id}")
  print(f"CDP URL: {session.cdp_url}")
  print(f"Base URL: {session.base_url}")
  ```

  ```typescript TypeScript theme={null}
  import { TinyFish } from "@tiny-fish/sdk";

  const client = new TinyFish();
  const session = await client.browser.sessions.create({
    url: "https://example.com",
  });

  console.log(`Session: ${session.session_id}`);
  console.log(`CDP URL: ${session.cdp_url}`);
  console.log(`Base URL: ${session.base_url}`);
  ```

  ```bash cURL theme={null}
  curl -s -X POST https://api.browser.tinyfish.ai \
    -H "X-API-Key: $TINYFISH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://example.com"}'
  ```
</CodeGroup>

**Output:**

```json theme={null}
{
  "session_id": "tf-84ac6714-0f08-4592-b626-e51105a079f9",
  "cdp_url": "wss://ip-52-53-175-49.tetra-data.tinyfish.io/tf-84ac6714-...",
  "base_url": "https://ip-52-53-175-49.tetra-data.tinyfish.io/tf-84ac6714-..."
}
```

## Connect with Playwright

Create a session and control it with Playwright via CDP:

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

  async def main():
      client = TinyFish()
      session = client.browser.sessions.create(url="https://scrapeme.live/shop")

      async with async_playwright() as p:
          browser = await p.chromium.connect_over_cdp(session.cdp_url)
          page = browser.contexts[0].pages[0]

          await asyncio.sleep(2)
          await page.wait_for_load_state("domcontentloaded")

          title = await page.title()
          print(f"Page title: {title}")

          await page.screenshot(path="shop.png")
          print("Screenshot saved to shop.png")

  asyncio.run(main())
  ```

  ```typescript TypeScript theme={null}
  import { chromium } from "playwright";
  import { TinyFish } from "@tiny-fish/sdk";

  async function main() {
    const client = new TinyFish();
    const session = await client.browser.sessions.create({
      url: "https://scrapeme.live/shop",
    });

    const browser = await chromium.connectOverCDP(session.cdp_url);
    const page = browser.contexts()[0].pages()[0];

    await page.waitForTimeout(2000);
    await page.waitForLoadState("domcontentloaded");

    const title = await page.title();
    console.log(`Page title: ${title}`);

    await page.screenshot({ path: "shop.png" });
    console.log("Screenshot saved to shop.png");
  }

  main();
  ```
</CodeGroup>

<Note>
  Session creation takes 10-30 seconds. Set your HTTP client timeout to at least 60 seconds. The browser navigates to the URL asynchronously — wait for `domcontentloaded` before interacting.
</Note>

## Scrape with Playwright

Extract structured data from a page using Playwright selectors:

<CodeGroup>
  ```python Python theme={null}
  import asyncio, json
  from playwright.async_api import async_playwright
  from tinyfish import TinyFish

  async def main():
      client = TinyFish()
      session = client.browser.sessions.create(url="https://scrapeme.live/shop")

      async with async_playwright() as p:
          browser = await p.chromium.connect_over_cdp(session.cdp_url)
          page = browser.contexts[0].pages[0]
          await asyncio.sleep(2)
          await page.wait_for_load_state("domcontentloaded")

          products = await page.evaluate("""
              () => [...document.querySelectorAll('.product')].slice(0, 5).map(el => ({
                  name: el.querySelector('.woocommerce-loop-product__title')?.textContent,
                  price: el.querySelector('.price .amount')?.textContent,
              }))
          """)

          print(json.dumps(products, indent=2))

  asyncio.run(main())
  ```

  ```typescript TypeScript theme={null}
  import { chromium } from "playwright";
  import { TinyFish } from "@tiny-fish/sdk";

  async function main() {
    const client = new TinyFish();
    const session = await client.browser.sessions.create({
      url: "https://scrapeme.live/shop",
    });

    const browser = await chromium.connectOverCDP(session.cdp_url);
    const page = browser.contexts()[0].pages()[0];
    await page.waitForTimeout(2000);
    await page.waitForLoadState("domcontentloaded");

    const products = await page.evaluate(() =>
      [...document.querySelectorAll(".product")].slice(0, 5).map((el) => ({
        name: el.querySelector(".woocommerce-loop-product__title")?.textContent,
        price: el.querySelector(".price .amount")?.textContent,
      }))
    );

    console.log(JSON.stringify(products, null, 2));
  }

  main();
  ```
</CodeGroup>

**Output:**

```json theme={null}
[
  { "name": "Bulbasaur", "price": "£63.00" },
  { "name": "Ivysaur", "price": "£87.00" },
  { "name": "Venusaur", "price": "£105.00" },
  { "name": "Charmander", "price": "£48.00" },
  { "name": "Charmeleon", "price": "£165.00" }
]
```

***

## Related

<CardGroup cols={2}>
  <Card title="Browser Reference" icon="browser" href="/browser-api/reference">
    Session parameters, lifecycle, and debugging
  </Card>

  <Card title="Browser Profiles" icon="window" href="/key-concepts/browser-profiles">
    Standard and stealth browser modes
  </Card>
</CardGroup>
