CrawlOps Documentation

Scrape any site — managed browsers, proxies and bot-protection handling, built in.

CrawlOps gives you two ways to collect data from the web behind one account. Use the Browser WebSocket for full Playwright control with a sticky residential session, or the Scrape API to get HTML, Markdown, screenshots or structured JSON from a single POST — with rotating residential proxies and automatic protection-bypass on both. No proxy ops, no headless-browser fleet to babysit.

Your first scrape — in 30 seconds

Create an API key in the dashboard, then POST a URL. Add render_js when the page needs a real browser.

POSThttps://crawl.stratifyops.cloud/v1/scrape
curl -X POST https://crawl.stratifyops.cloud/v1/scrape \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "render_js": true }'

What you can do

Sticky browser sessions

The Browser WebSocket holds the same residential IP for the whole session (~30 min) — ideal for logins, carts and multi-step flows.

Rotating proxy API

Every Scrape API request exits from a fresh residential IP. Built for high-volume, parallel extraction with zero proxy management.

Bypasses protection layers

Cloudflare challenges, Turnstile and advanced bot protection are detected and handled automatically — on both products.

Geotargeting · 195+ countries

Add proxy_country=us (any ISO code) to route requests through a specific country's residential pool.

JS rendering & screenshots

Render SPAs with a real browser, run declarative click/fill scenarios, and capture full-page screenshots.

LLM-ready output

Return clean Markdown or structured JSON via CSS extract rules — drop straight into RAG, analytics or a database.

Which one should I use?

Browser WebSocket

bt_live_… · wss://…/ws

Sticky residential IP
  • Full Playwright API over WebSocket
  • Same IP for the whole session (~30 min)
  • Best for logins, carts, multi-step flows
  • Concurrency scales by browser token

Scrape API

sk_live_… · POST /v1/scrape

Rotating residential IP
  • One POST → HTML, Markdown, JSON or screenshot
  • Fresh IP per request, no session to manage
  • Best for high-volume, parallel extraction
  • CSS extract rules + declarative JS scenarios

Rule of thumb: need to act on a page (click, log in, paginate)? Use the Browser WebSocket. Need to fetch data fast and at scale? Use the Scrape API.

Prefer to click, not code?

The Scrape Builder in your dashboard lets you configure a request — JS rendering, proxy country, Cloudflare solving, CSS extract rules — run it live against any URL, and copy the exact API call to drop into your code.

Open Scrape Builder

Code examples & API reference

Pick a product and language below. Every snippet is copy-paste ready, with do/don't notes and the full parameter list.

Playwright WebSocket — full browser control.

Browser token → connect Playwright → disconnect when done.

…/ws?browser_token=bt_live_… &browser=chromium

Query params

  • browser_tokenreq

    string

    Browser token (bt_live_…). Create under Dashboard → Browser Tokens.

  • browser

    "chromium" | "firefox"

    Browser engine for the session. Default: "chromium". When solve_cloudflare is enabled, the session runs on Firefox — connect with firefox.connect().

  • solve_cloudflare

    boolean

    Enable automatic Cloudflare challenge solving for the session (runs on Firefox). Default: false.

Connect with Playwright

Attach your existing Playwright script to a remote Chromium session.

Create a browser token (bt_live_…) in the dashboard. · Avoid: Do not put proxy credentials in the WebSocket URL — pass proxy in new_context().

basic.py
import asyncio
from playwright.async_api import async_playwright

BROWSER_TOKEN = "bt_live_YOUR_TOKEN"

async def main():
    async with async_playwright() as pw:
        browser = await pw.chromium.connect(
            f"wss://browser.stratifyops.cloud/ws?browser_token={BROWSER_TOKEN}&browser=chromium"
        )
        page = await browser.new_page()
        await page.goto("https://example.com")
        print(await page.title())
        await browser.close()

asyncio.run(main())

Cloudflare challenge solving

solve_cloudflare=true enables automatic Cloudflare challenge solving server-side.

Set solve_cloudflare=true on the WebSocket URL. · Avoid: Do not disable JavaScript in the context — challenges need a real browser.

cloudflare.py
import asyncio
from playwright.async_api import async_playwright

TOKEN = "bt_live_YOUR_TOKEN"

async def main():
    async with async_playwright() as pw:
        browser = await pw.firefox.connect(
            f"wss://browser.stratifyops.cloud/ws?browser_token={TOKEN}&solve_cloudflare=true"
        )
        page = await browser.new_page()
        await page.goto(
            "https://nopecha.com/demo/cloudflare",
            wait_until="networkidle",
        )
        print(await page.title())
        await browser.close()

asyncio.run(main())

Advanced bot protection

Advanced bot-protection blocks are detected after load and handled automatically — no extra flags.

Use a normal WebSocket session with firefox. · Avoid: Do not expect instant HTML — bot-protection handling adds a short post-load delay.

bot-protection.py
import asyncio
from playwright.async_api import async_playwright

TOKEN = "bt_live_YOUR_TOKEN"

async def main():
    async with async_playwright() as pw:
        browser = await pw.firefox.connect(
            f"wss://browser.stratifyops.cloud/ws?browser_token={TOKEN}&browser=firefox"
        )
        page = await browser.new_page()
        await page.goto(
            "https://example.com/protected-page",
            wait_until="networkidle",
        )
        print(await page.title())
        await browser.close()

asyncio.run(main())

Proxy per session

Route traffic through your own proxy via Playwright context options.

Pass proxy: { server: 'http://user:pass@host:port' } in browser.new_context(). · Avoid: Do not embed proxy credentials in the WebSocket connect URL.

proxy.py
import asyncio
from playwright.async_api import async_playwright

TOKEN = "bt_live_YOUR_TOKEN"
PROXY = "http://user:pass@proxy.example.com:10000"

async def main():
    async with async_playwright() as pw:
        browser = await pw.chromium.connect(
            f"wss://browser.stratifyops.cloud/ws?browser_token={TOKEN}&browser=chromium"
        )
        ctx = await browser.new_context(proxy={"server": PROXY})
        page = await ctx.new_page()
        await page.goto("https://example.com")
        await browser.close()

asyncio.run(main())

Multi-step interactions

Log in, paginate, and extract with the full Playwright API.

Use wait_for_selector after actions that trigger navigation. · Avoid: Do not leave browsers connected overnight — close to free worker capacity.

interact.py
import asyncio
from playwright.async_api import async_playwright

TOKEN = "bt_live_YOUR_TOKEN"

async def main():
    async with async_playwright() as pw:
        browser = await pw.chromium.connect(
            f"wss://browser.stratifyops.cloud/ws?browser_token={TOKEN}&browser=chromium"
        )
        page = await browser.new_page()
        await page.goto("https://example.com/login")
        await page.fill("#email", "user@example.com")
        await page.fill("#password", "secret")
        await page.click('[type="submit"]')
        await page.wait_for_selector(".dashboard")
        data = await page.evaluate("""() => ({
            user: document.querySelector(".user-name")?.innerText,
        })""")
        await browser.close()
        return data

asyncio.run(main())

Free tier — API keys and browser tokens included.