84 stars on GitHub and counting. Yozh Crawler + Scraper is free, open-source, and built in public — give us a star if it earns its place in your stack.
CyberYozh Data / Yozh Scraper
Software · Yozh Scraper

One URL in, clean structured data out

Yozh Scraper renders any URL in a real Playwright browser and returns exactly what you ask for — extracted fields, raw HTML, or a full-page screenshot. Built-in CyberYozh proxies, stealth by default, presets for major sites, and authenticated sessions. No SaaS, runs on :8000.

$curl -X POST :8000/api/v1/scrape/page -d '{"url":"https://site.com", "proxy_type":"res_rotating","extract":{"type":"css","fields":{...}}}'

Scrape

Yozh Scraper renders any URL in a real Playwright browser and returns exactly what you ask for — extracted fields, raw HTML, or a full-page screenshot. Every scrape is an async job: submit a URL, poll the job, fetch the result. Proxies, stealth, presets, and authenticated sessions are all first-class.

  • Real browser render — Playwright renders JS-built pages; flip render off for static HTML.
  • Structured extraction — CSS or XPath field rules return a clean data object, far cheaper than downloading and parsing raw HTML yourself.
  • Built-in proxies — CyberYozh residential / mobile LTE / datacenter, with GEO targeting and no pool-id hunting.
  • Stealth by default — playwright-stealth patches (navigator.webdriver, WebGL / Canvas fingerprint, chrome runtime) to reduce bot detection.
  • Presets — scrape Amazon / Google / eBay / Walmart / YouTube / LinkedIn by name, with optional LLM self-heal.
  • Sessions — server-managed authenticated sessions for logged-in targets, reused across scrapes.
Base URLhttp://localhost:8000
OpenAPI docshttp://localhost:8000/docs
MCP endpointhttp://localhost:8000/mcp
Pairs with Yozh Crawler. The crawler (:8001) walks a site from one seed URL and fetches every page through this scraper — the same render, proxy, and session features apply to crawled pages.

Quickstart

The scraper comes up from the root docker-compose.yml (alongside the crawler):

bash
cp .env.example .env          # set CYBERYOZH_API_KEY if using proxies
docker compose up --build
# scraper → http://localhost:8000
# crawler → http://localhost:8001

Verify it's up:

bash
curl http://localhost:8000/api/v1/health
# {"status":"ok","workers":2}
Proxies need an API key — set CYBERYOZH_API_KEY in .env (get one at app.cyberyozh.com/api-access). Without it, only proxy_type: none works.

Basic usage

Every scrape endpoint creates a background job and returns a job_id. Poll the job, then fetch its results.

cURL
curl -X POST http://localhost:8000/api/v1/scrape/page \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "proxy_type": "none" }'
# → { "job_id": "req_abc123" }

curl http://localhost:8000/api/v1/scrape/req_abc123          # status
curl http://localhost:8000/api/v1/scrape/req_abc123/results  # results

A finished result carries metadata plus whatever you requested (data, raw_html, screenshot_base64):

JSON
{
  "job_id": "req_abc123",
  "status": "done",
  "total": 1, "done": 1,
  "results": [
    {
      "request_id": "req_abc123",
      "took_ms": 1234,
      "meta": { "url": "https://example.com", "final_url": "https://example.com/",
                "status_code": 200, "device": "desktop", "proxy_type": "none", "retries": 0 },
      "data": null, "raw_html": null, "screenshot_base64": null, "warnings": []
    }
  ]
}
status moves queuedrunningdone (or failed / cancelled). Results are available for done, failed, and cancelled jobs.

Extract data

Pass an extract rule and the response includes a data object keyed by your field names — much cheaper than downloading raw_html and parsing it yourself. Rules are css or xpath.

cURL
curl -X POST http://localhost:8000/api/v1/scrape/page \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "extract": {
      "type": "css",
      "fields": {
        "title": { "selector": "h1", "attr": "text", "required": true }
      }
    }
  }'
# result → { "data": { "title": "Example Domain" } }

Each field is a rule:

Field keyTypeDefaultDescription
selectorstringrequiredCSS selector or XPath expression for the field.
attrstringtextWhat to read — text or an attribute name (e.g. href, src).
allboolfalseReturn all matches as a list instead of the first.
requiredboolfalseFlag when missing — drives preset LLM self-heal.

Use "type": "xpath" with XPath selectors (e.g. //h1) for the same shape.

Screenshots & raw HTML

Set screenshot: true for a full-page PNG (base64 in screenshot_base64), or raw_html: true to get the full post-render HTML in raw_html.

cURL
curl -X POST http://localhost:8000/api/v1/scrape/page \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "screenshot": true, "raw_html": true }'
Screenshots trigger a scroll pass to load lazy images. If you also use block_assets, turn it off for screenshots so images render.

Proxies

For reliable scraping, proxies are essential — most modern sites block direct requests. Yozh integrates with the CyberYozh Proxy Service; set proxy_type on any request.

proxy_typeWhat it is
res_rotatingResidential rotating — recommended default.
res_staticResidential static (dedicated IP).
mobileMobile / LTE, dedicated.
mobile_sharedMobile / LTE, shared pool.
dc_staticDatacenter static.
noneDirect connection, no proxy.

Target a location with proxy_geo (country_code / region / city). Discover what you've purchased without hunting for pool ids:

cURL
curl "http://localhost:8000/api/v1/proxies/available?proxy_type=res_rotating"
curl "http://localhost:8000/api/v1/proxies/countries"
Proxies require CYBERYOZH_API_KEY in the scraper's .env. Get one at app.cyberyozh.com/api-access, then restart the container.

Presets

A preset bundles a request profile + URL template + parsing recipe, so you scrape a site by name instead of hand-assembling a request. Built-ins ship for Amazon, Google, eBay, Walmart, YouTube and LinkedIn; you can also build your own (deterministic CSS/XPath or AI-generated).

cURL
curl -X POST http://localhost:8000/api/v1/scrape/preset/page \
  -H "Content-Type: application/json" \
  -d '{
    "source": "amazon_product",
    "preset_params": { "asin": "B08N5WRWNW" },
    "locale": "us",
    "llm": { "model": "openai/gpt-5.4-mini" }
  }'
# → { "job_id": "..." }  then GET /api/v1/scrape/<job_id>/results
llm is optional. Without it the deterministic parser runs alone; with it, selectors self-heal when a required field comes back empty. Provider keys (OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY / OPENROUTER_API_KEY) are server-side in .env.

Manage presets via GET /api/v1/presets, GET /api/v1/presets/{name}, and POST /api/v1/presets (user preset names must start with user_).

Sessions

Server-managed authenticated sessions: create one, log in once, then pass its session_id to any scrape so pages are fetched with the stored cookies + storage state. Required for logged-in targets such as the linkedin_profile preset.

  1. POST /api/v1/sessions — create. Pins device / proxy_type / proxy_geo and a TTL. Returns { session_id, expires_at }.
  2. POST /api/v1/sessions/{id}/login — replay a declarative login script with creds in the body: { script, creds }.
  3. POST /api/v1/scrape/page with session_id set — scrape authenticated.
  4. DELETE /api/v1/sessions/{id} — clean up.
cURL
# 1. create  →  {"session_id":"sess_...","expires_at":...}
curl -X POST http://localhost:8000/api/v1/sessions \
  -H "Content-Type: application/json" \
  -d '{ "device": "desktop", "proxy_type": "res_rotating" }'

# 2. log in (declarative DSL + creds)
curl -X POST http://localhost:8000/api/v1/sessions/sess_.../login \
  -H "Content-Type: application/json" \
  -d '{ "script": { "steps": [ {"op":"goto","url":"https://site/login"} ] },
        "creds": { "username": "...", "password": "..." } }'

# 3. scrape with the session
curl -X POST http://localhost:8000/api/v1/scrape/page \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://site/secure", "session_id": "sess_...", "raw_html": true }'
session_id and cookies together → 422. The scrape's device / proxy_type / proxy_pool_id / proxy_geo must match the session's pinned values. For CAPTCHA / 2FA the login DSL can't solve, skip the script and inject cookies into the session instead.

Batch scraping

Submit many pages in one job with POST /api/v1/scrape/pages — each entry is a full scrape request. Poll status and fetch results through the same job endpoints.

cURL
curl -X POST http://localhost:8000/api/v1/scrape/pages \
  -H "Content-Type: application/json" \
  -d '{
    "pages": [
      { "url": "https://example.com", "proxy_type": "none" },
      { "url": "https://example.org", "proxy_type": "none" }
    ]
  }'
A top-level session_id applies to every page in the batch (rejected with 422 if a page already pins a different one).

MCP

The scraper mounts a Model Context Protocol endpoint at /mcp (Streamable HTTP). Point Claude or Cursor at it and the tools appear automatically:

  • run_scrape_page
  • run_scrape_pages
  • get_job_status
  • get_job_result
  • cancel_scrape_job
  • health
~/.claude/settings.json
"yozh-scraper": {
  "type": "http",
  "url": "http://localhost:8000/mcp"
}

Then just ask: "Scrape https://example.com and tell me what's on the page." The same endpoint works from a LangChain agent or an n8n MCP Client Tool node.

Configuration reference

Request — ScrapeRequest (selected)

FieldTypeDefaultDescription
urlstring (URL)requiredThe page to render and scrape.
renderbooltrueRender with the browser (needed for JS-built pages).
wait_untildomcontentloaded · networkidledomcontentloadedWhen the page is considered ready.
wait_for_selectorstring · nullnullWait for a specific element before capturing.
devicedesktop · mobiledesktopViewport / UA profile.
proxy_typesee ProxiesnoneProxy pool the fetch routes through.
proxy_geoProxyGeo · nullnullCountry / region / city targeting.
session_idstring · nullnullUse an authenticated session — see Sessions.
stealthbooltrueApply anti-detection hardening.
block_assetsbool · nullenvBlock images/fonts/media for speed (falls back to BLOCK_ASSETS).
extractExtractRule · nullnullCSS/XPath field rules → structured data.
raw_htmlboolfalseInclude full post-render HTML.
screenshotboolfalseCapture a full-page PNG (base64).

API reference

MethodPathPurpose
POST/api/v1/scrape/pageScrape one page. Returns {"job_id":"…"}.
POST/api/v1/scrape/pagesBatch scrape multiple pages in one job.
POST/api/v1/scrape/preset/pageScrape by preset name (Amazon, Google, …).
GET/api/v1/scrape/{id}Job status (queued/running/done/…).
GET/api/v1/scrape/{id}/resultsJob results (pages + ScrapeResponse).
DELETE/api/v1/scrape/{id}Soft-cancel — in-flight pages finish.
POST/api/v1/sessionsCreate an authenticated session.
POST/api/v1/sessions/{id}/loginRun a declarative login script.
DELETE/api/v1/sessions/{id}Delete a session.
GET/api/v1/proxies/availableList purchased proxies of a type.
GET/api/v1/presetsList built-in + user presets.
GET/api/v1/healthHealth + worker count.

Important details

Proxies need an API key. Set CYBERYOZH_API_KEY in the scraper's .env. Without it, only proxy_type: none (direct) works.
Async, in-memory jobs. Every scrape is a background job; the store is in-memory and resets on container restart. Fetch results while they're available, or persist them yourself.
Sessions are exclusive with cookies. Passing both session_id and cookies returns 422, and a scrape must match the session's pinned device / proxy_type / proxy_geo.
LLM self-heal is server-side. Preset self-heal uses provider keys (OPENAI / ANTHROPIC / GEMINI / OPENROUTER) from .env — never sent by the client.

Loved by data teams and AI builders

What people building with Yozh say

5.0 / 5 · 5 reviews
GitHub
Swapped our in-house Playwright cluster for Yozh in an afternoon. The MCP endpoint dropped straight into our Claude agent — zero glue code, crawler and scraper just worked.
Marcus Reinhardt Lead Data Engineer Northwind Analytics
X
The preset system is the killer feature. We pass a source name and get clean JSON back; the self-heal even caught two Amazon layout changes before we noticed.
Priya Nair Founder ScrapeStack
Reddit
Finally an open-source scraper that treats proxies and sessions as first-class. We run it behind login walls for partner portals — sessions persist and results stay consistent across regions.
Daniel Osei Backend Engineer Loopfeed
X
Connected it to Cursor over MCP and my agent pulls live web data mid-task now. The streaming crawl over SSE is exactly what agent workflows were missing.
Elena Kovac AI Engineer Vektor Labs
GitHub
We moved off a paid scraping API to cut costs and braced for a downgrade — got the opposite. Self-hosted, no per-request billing, and the output schema is cleaner than what we used to pay for.
Sofia Almeida Engineering Manager Tabbly
Open Source · MIT License · 84 ★

Render a URL. Get clean data back

Yozh Scraper is the rendering engine behind the whole stack — one docker compose up and you have proxies, stealth, presets, sessions and extraction, MCP-ready, on your own infra. Free. Forever.

Yozh Scraper + Crawler is distributed under the MIT license. Use it. Fork it. Build with it.