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

# Web automation

> Playwright-driven browser automation in an isolated Chromium. Boot a Next.js dev server and screenshot agent-generated websites.

If your agent fetches a web page, scrapes data, or previews a
website it just generated, the sandbox is where that happens. Each
browser session runs in its own microVM — no cross-tenant cookie
leakage, no shared Chromium profile, no "oops, the last agent left
a modal open."

## Headless browser — Playwright

```python theme={null}
from podflare import Sandbox
import base64

sbx = Sandbox(template="python-datasci", idle_timeout_seconds=900)

# Install Playwright + Chromium
sbx.run_code("pip install -q playwright && playwright install chromium")

r = sbx.run_code("""
from playwright.sync_api import sync_playwright
import base64

with sync_playwright() as p:
    br = p.chromium.launch()
    page = br.new_page()
    page.goto('https://news.ycombinator.com')
    titles = page.locator('.titleline > a').all_inner_texts()[:5]
    print('\\n'.join(titles))

    img = page.screenshot()
    print('SCREENSHOT_B64:' + base64.b64encode(img).decode())

    br.close()
""")

text, _, png_b64 = r.stdout.partition("SCREENSHOT_B64:")
print("Headlines:\n", text)
print("Screenshot bytes:", len(png_b64))
sbx.close()
```

### Real-world uses

* **Research agents** that gather links + screenshots for a report.
* **Accessibility audits** — launch Chromium, inject `axe-core`,
  return violations.
* **Form-filling bots** for routine admin work on sites without an
  API (expense reports, government portals, etc.).
* **"Visual diff" on generated UIs** — screenshot before and after
  a change, pipe both to a vision model.

<Tip>
  First-time `playwright install` downloads \~120 MB. Do it in the
  first agent turn and keep the sandbox alive for the session so
  subsequent turns skip the download.
</Tip>

## Boot a Next.js dev server, screenshot the result

This is the pattern for v0.dev-style "AI-generated websites." The
agent writes React files, the sandbox runs `next dev`, Playwright
takes a screenshot so the user can see what the agent built.

```python theme={null}
from podflare import Sandbox

sbx = Sandbox(idle_timeout_seconds=1800)

# 1. Scaffold + install
sbx.run_code("""
mkdir -p /tmp/site && cd /tmp/site

cat > package.json <<'PKG'
{"dependencies":{"next":"^15","react":"^19","react-dom":"^19"}}
PKG

mkdir -p app
cat > app/page.tsx <<'TSX'
export default function P() {
  return <h1>hi from sandbox</h1>;
}
TSX
cat > app/layout.tsx <<'TSX'
export default function L({children}:{children:React.ReactNode}) {
  return <html><body>{children}</body></html>;
}
TSX

npm install --silent
""", language="bash")

# 2. Start dev server in background
sbx.run_code(
    "cd /tmp/site && nohup npx next dev -p 3000 > /tmp/next.log 2>&1 &",
    language="bash",
)

# 3. Screenshot
sbx.run_code("pip install -q playwright && playwright install chromium -q")
r = sbx.run_code("""
from playwright.sync_api import sync_playwright
import base64, time

time.sleep(3)  # give next a beat

with sync_playwright() as p:
    br = p.chromium.launch()
    page = br.new_page()
    page.goto('http://localhost:3000')
    img = page.screenshot()
    print('B64:' + base64.b64encode(img).decode()[:120] + '…')
    br.close()
""")
print(r.stdout)
sbx.close()
```

## Why isolation matters

* **Cookies + localStorage.** Fresh per sandbox. Nothing to leak
  between customers.
* **Downloaded files.** `/tmp` is the sandbox's, not yours. When
  the sandbox dies, so do the downloads.
* **Long-running headless Chromium.** Chromium likes to eat RAM
  and sometimes crash. The host is never affected; your box dies,
  your box respawns, the user doesn't see it.

## Pitfalls

* **Headless Chromium is memory-hungry.** 1 GB free-tier RAM is
  tight — expect OOMs on heavy pages. Pro (4 GB) is the happy
  place.
* **`page.wait_for_load_state('networkidle')`** is more reliable
  than `time.sleep(3)`. Use it for agent-generated pages where you
  don't know how long hydration takes.
* **Don't `pip install playwright` per turn.** Install once at
  sandbox start; subsequent turns reuse the venv.
