Browser API

Connect Playwright or Puppeteer to Anakin's stealth browser

What is Browser API?

Browser API gives you a stealth browser in the cloud that you control with your own code. Connect Playwright, Puppeteer, or any CDP-compatible client to Anakin's anti-detection browser via a single WebSocket URL.

Unlike traditional scraping APIs where you submit a URL and get results back, Browser API gives you full browser control: navigate pages, click buttons, fill forms, take screenshots, extract data — all through your own automation scripts.


Why use Browser API?

  • Anti-detection built in — Stealth browser with fingerprint masking, WebRTC leak prevention, and navigator.webdriver = false. No configuration needed.
  • Playwright and Puppeteer support — Connect with connect_over_cdp (Playwright) or browser.connect() (Puppeteer). No code changes beyond the connection URL.
  • Smart proxy selection — Per-domain proxy optimization via Thompson Sampling. The best proxy is automatically selected for each target site.
  • No browser infrastructure — No managing headless browsers, displays, or containers. Just connect and scrape.
  • Same API key — Uses your existing Anakin API key. No separate auth flow.

Quick Start

import asyncio
from playwright.async_api import async_playwright

API_KEY = "your_api_key"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(
            "wss://api.anakin.io/v1/browser-connect",
            headers={"X-API-Key": API_KEY},
        )
        page = browser.contexts[0].pages[0]

        # Navigate and extract data
        await page.goto("https://books.toscrape.com", wait_until="domcontentloaded")
        print("Title:", await page.title())

        # Extract structured data
        books = await page.evaluate("""
            Array.from(document.querySelectorAll('article.product_pod')).slice(0, 5).map(el => ({
                title: el.querySelector('h3 a')?.title,
                price: el.querySelector('.price_color')?.textContent,
            }))
        """)
        print("Books:", books)

        # Take a screenshot
        await page.screenshot(path="screenshot.png")

        # Use locators
        count = await page.locator("article.product_pod").count()
        print(f"Found {count} products")

        await browser.close()

asyncio.run(main())

Supported Features

Playwright

FeatureStatus
page.goto()
page.title() / page.content()
page.evaluate()
page.screenshot()
page.locator() — count, text, attributes
page.wait_for_selector()
page.wait_for_function()
page.inner_text() / page.text_content()
page.set_extra_http_headers()
Mouse and keyboard input
Scroll / pagination
Cross-site navigation
Stealth (webdriver=false)
Session keepalive (60s+)
page.route() — network interception

Puppeteer

FeatureStatus
page.goto()
page.title() / page.content()
page.evaluate()
page.screenshot()
page.$$() — querySelectorAll
page.$eval() — scoped evaluate
page.waitForSelector()
page.setExtraHTTPHeaders()
Mouse and keyboard input
Scroll / pagination
Cross-site navigation
browser.pages() / browser.newPage()
page.waitForNavigation()

Selenium

Selenium with ChromeDriver is not supported. Use Playwright or Puppeteer instead.

Known limitations

These are inherent to how remote CDP connections work, not specific to Browser API:

  • page.goto() response object is null — when connecting via connect_over_cdp() / puppeteer.connect(), the HTTP response object is always null. Navigation itself works correctly (page loads, URL updates, content is accessible).
  • page.route() does not fire (Playwright) — request interception requires a locally-owned BrowserContext. It is not available when the context is owned by a remote browser.
  • page.waitForNavigation() hangs (Puppeteer) — use page.waitForSelector() or page.waitForFunction() after clicks instead.

Scraping Example: Extract Product Data

import asyncio
from playwright.async_api import async_playwright

async def scrape_books():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(
            "wss://api.anakin.io/v1/browser-connect",
            headers={"X-API-Key": "your_api_key"},
        )
        page = browser.contexts[0].pages[0]
        await page.goto("https://books.toscrape.com", wait_until="domcontentloaded")

        # Wait for products to load
        await page.wait_for_selector("article.product_pod")

        # Extract all books on the page
        books = await page.evaluate("""
            Array.from(document.querySelectorAll('article.product_pod')).map(el => ({
                title: el.querySelector('h3 a')?.title,
                price: el.querySelector('.price_color')?.textContent,
                rating: el.querySelector('.star-rating')?.className.replace('star-rating ', ''),
                inStock: !!el.querySelector('.instock'),
                link: el.querySelector('h3 a')?.href,
            }))
        """)

        print(f"Scraped {len(books)} books")
        for book in books[:3]:
            print(f"  {book['title']}{book['price']}")

        await browser.close()

asyncio.run(scrape_books())

Billing

Browser API sessions are billed at 1 credit per 2 minutes (rounded up). A session that lasts 3 minutes costs 2 credits.

Sessions auto-disconnect when your credits reach 0.

Limits

ParameterValue
Max session duration2 hours
Idle timeout60 seconds (no messages)
Credit cost1 credit / 2 min
Geo-targeting (?country=XX)
Max message size50 MB
AuthenticationX-API-Key header

Idle disconnect: The connection is closed if no CDP messages are exchanged for 60 seconds. Keep your automation actively sending commands, or call browser.close() when done rather than leaving the connection open.

Saved Sessions

Load a saved browser session to connect with pre-authenticated cookies and localStorage. This lets your scripts access pages that require login — without handling authentication in code.

Pass ?session_id=<uuid> or ?session_name=<name> as a query parameter:

import asyncio
from playwright.async_api import async_playwright

API_KEY = "your_api_key"
SESSION_ID = "your_session_id"  # from dashboard or GET /v1/sessions

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(
            f"wss://api.anakin.io/v1/browser-connect?session_id={SESSION_ID}",
            headers={"X-API-Key": API_KEY},
        )
        page = browser.contexts[0].pages[0]

        # Navigate to an authenticated page — cookies are pre-loaded
        await page.goto("https://amazon.com/your-orders", wait_until="domcontentloaded")
        print("Title:", await page.title())

        await browser.close()

asyncio.run(main())

You can also use the session name instead of ID:

wss://api.anakin.io/v1/browser-connect?session_name=my-amazon-login

How it works

  1. The API loads your saved session from encrypted storage
  2. The browser launches with your cookies and localStorage pre-injected
  3. If the session was created with a static IP proxy, the same proxy is reused — preventing IP mismatch issues
  4. Billing is the same (1 credit / 2 min) — no extra charge for session loading

Session query parameters

ParameterDescription
session_idUUID of the saved session
session_nameName of the saved session (must be unique per user)

If both are provided, session_id takes precedence. If neither is provided, a fresh browser is launched (default behavior).

Error responses

Session validation happens before the WebSocket connection is established, so errors are returned as standard HTTP responses:

StatusErrorMeaning
404session not foundSession ID/name doesn't exist or doesn't belong to you
422session has no stored data — save it firstSession was created but never saved — log in and save first
409session is being automatedSession is currently being used by an automation job

Saving Sessions

You can also save a session directly from Browser API. Pass ?save_session=<name> when connecting, and the session is automatically saved when you disconnect — no extra API calls needed.

wss://api.anakin.io/v1/browser-connect?save_session=my-amazon-login&save_url=https://amazon.com
import asyncio
from playwright.async_api import async_playwright

API_KEY = "your_api_key"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(
            "wss://api.anakin.io/v1/browser-connect?save_session=my-amazon-login&save_url=https://amazon.com",
            headers={"X-API-Key": API_KEY},
        )
        page = browser.contexts[0].pages[0]

        # Log in programmatically
        await page.goto("https://amazon.com/signin")
        await page.fill("#email", "user@example.com")
        await page.fill("#password", "mypassword")
        await page.click("#signIn")
        await page.wait_for_url("**/your-account**")

        # Disconnect — session is auto-saved
        await browser.close()

asyncio.run(main())

Next time, load the saved session with ?session_name=my-amazon-login — see Saved Sessions above.

Save query parameters

ParameterRequiredDescription
save_sessionYesName for the saved session (must be unique per user)
save_urlNoWebsite URL associated with the session (e.g. https://amazon.com)

Save error responses

Session name is validated before the WebSocket connection starts:

StatusErrorMeaning
409session name already existsChoose a different name
503session saving not configuredServer-side encryption not set up

How saving works

  1. You connect with ?save_session=my-name
  2. The session name is validated (must be unique) before the WebSocket upgrades
  3. You automate login, navigate pages, etc.
  4. When you disconnect, the browser's cookies and localStorage are automatically extracted and encrypted
  5. The session appears in your dashboard and GET /v1/sessions
  6. If the session proxy was a static IP, it's saved too — reuse preserves the same IP

Note: Save is best-effort on disconnect. If you need guaranteed saves with visual confirmation, use the interactive browser session flow instead.


Session Recording

Record your Browser API session as a video. Pass ?record=true when connecting — a WebM video is automatically captured and saved when you disconnect.

wss://api.anakin.io/v1/browser-connect?record=true

You can combine recording with other features:

wss://api.anakin.io/v1/browser-connect?record=true&save_session=my-login&save_url=https://amazon.com

Retrieving recordings

After disconnecting, your recording is available via the API:

# List all recordings
curl https://api.anakin.io/v1/recordings \
  -H "X-API-Key: your_api_key"

# Get a specific recording (includes video URL)
curl https://api.anakin.io/v1/recordings/rec-123 \
  -H "X-API-Key: your_api_key"

The response includes a presigned video URL (valid for 1 hour):

{
  "id": "abc-123",
  "connId": "rec-123",
  "duration": 45,
  "fileSize": 304205,
  "status": "completed",
  "videoUrl": "https://s3.amazonaws.com/...",
  "createdAt": "2026-04-01T12:00:00Z"
}

Recordings are also available in your dashboard.

How recording works

  1. You connect with ?record=true
  2. Playwright's built-in video recording captures all page activity as WebM
  3. When you disconnect, the video is finalized, uploaded to encrypted storage, and linked to your account
  4. No extra credit cost — recording is included in the standard Browser API rate

Geo-Targeting

Pass ?country=XX (ISO 3166-1 alpha-2) when connecting to route your session through a proxy in that country:

wss://api.anakin.io/v1/browser-connect?country=IN

Supported codes include US, IN, GB, DE, FR, JP, SG, and others depending on proxy availability. The proxy is selected via Thompson Sampling from the pool scored for that country and target domain. Defaults to US if not specified or if the requested country has no available proxies.