spicrawlspicrawlDocs

spicrawl scrape

Fetch one URL, or many from stdin, as markdown, html, text, PDF or extracted JSON with spicrawl scrape; every flag maps to a POST /v1/scrape field.

spicrawl scrape <url> calls POST /v1/scrape and prints the result. On a terminal it prints the document itself; piped, it prints one JSON object.

spicrawl scrape https://example.com/blog/launch --format markdown

Only the flags you set are sent. Everything else takes the API's default, or your project's stored default. Each flag below lists the API field it sets; see POST /v1/scrape for the full field rules.

Examples

# A page as clean markdown, for an LLM context window
spicrawl scrape https://example.com/blog/launch --format markdown

# A JS-rendered page, waiting for the element that holds the data
spicrawl scrape https://example.com/pricing --render --wait-for '.plans' --format markdown

# Through your own proxy, with the metadata on stderr
spicrawl scrape https://example.de/produkt/42 --proxy "$MY_PROXY_URL" --meta

# A Cloudflare-protected page, through your own (residential) proxy
spicrawl scrape https://example.com/products/42 --proxy "$MY_PROXY_URL" --format markdown

# Selector extraction; prints only the extracted data
spicrawl scrape https://example.com/products/1 --extract '{"title":"h1","price":".price"}'

# Render JavaScript, retrying transient failures
spicrawl scrape https://news.example.com --render --retry 3

# Many URLs from a file, 8 at a time, one JSON line each; keep the failures
spicrawl scrape - --format markdown --concurrency 8 < urls.txt | jq -c 'select(.error or .target_error)'

Flags

Target

FlagAPI fieldMeaning
<url>urlTarget URL, http or https. - reads URLs from stdin (see many URLs).
--method MmethodHTTP method sent to the target, uppercased. Default GET. No request body is sent; only GET results are cached.
--header 'Name: value'custom_headersHeader sent to the target. Repeatable. A value without : exits 2. Hop-by-hop headers (Host, Connection, Content-Length, ...) are refused with ERR::REQUEST::INVALID_PARAMETER.
--body JSON|@filewhole bodyRaw request body as inline JSON, @path, or @- for stdin. Flags you also set override its fields.

Rendering

FlagAPI fieldMeaning
--renderjs_renderRender JavaScript (3 credits). Screenshots, PDFs, --actions and challenged pages move to full-browser rendering by themselves, at that tier's price. See credits.
--impersonateimpersonateFetch tier only: present a current browser TLS/HTTP2 fingerprint. The API does this by default; --impersonate=false turns it off. No extra cost.
--engine EengineDeprecated and ignored: rendering is chosen automatically from the rest of the request. The response carries X-Warning: ENGINE_IGNORED. Remove it. Hidden from --help since 0.1.4, but still accepted.
--mode automodeStart with a plain fetch and move to stronger rendering only when the previous step met a challenge. Bills only the step that served. Cannot be combined with --render.
--headless=falseheadlessDeprecated and ignored, like --engine: rendering is chosen automatically. Remove it. Also hidden from --help.
--session IDsession_idReuse a session's cookies and storage. See sessions.

You do not pick a rendering tier. The request picks it, and the result names the one that ran: fetch (a plain fetch, no JavaScript), js (JavaScript rendering) or browser (full-browser rendering). It is the engine field in JSON and the engine: line of --meta. See response headers.

Proxy

FlagAPI fieldMeaning
--proxy URLproxyYour own proxy (http, https, socks5, socks5h).
--proxy-verifyproxy_verifyProbe --proxy before spending engine time. Requires --proxy.

Two more options are on the way: stealth mode Coming soon (full-browser rendering with a hardened fingerprint), and the managed proxy flags --premium-proxy, --country and --sticky-key Coming soon.

Cloudflare-protected pages, and pages behind similar managed challenges, are handled automatically when you bring your own --proxy (residential works best): a challenged page is retried on stronger rendering on the same exit, and you get the real page. This is not guaranteed on every site. Automatic handling on managed --premium-proxy exits is Coming soon. On the default shared pool a challenged page can return ERR::UPSTREAM::CHALLENGE; retry with your own residential --proxy. If a request uses your own proxy and hits a blocking challenge that Spicrawl solves, your proxy's address and credentials are shared with the third-party solving service for that solve.

Waiting and page control

These need a browser (--render or --mode auto); otherwise the API returns ERR::REQUEST::INCOMPATIBLE_FLAGS.

FlagAPI fieldMeaning
--wait MSwaitFixed delay after load, in milliseconds.
--wait-for SELwait_forCSS selector to wait for before capture. If it never appears the page is returned with the warning RENDER_DEGRADED.
--wait-for-timeout MSwait_for_timeoutCap on --wait-for, in milliseconds. Requires --wait-for.
--block LISTblock_resourcesComma-separated: images, fonts, media, stylesheets, scripts, or none. Full-browser rendering blocks nothing by default.
--actions JSON|@fileactionsOrdered browser actions (click, fill, scroll, ...). See browser actions.
--network-capture JSON|@filenetwork_captureRecord the XHR/fetch responses the page made. Returned under network. See network capture.

Extraction

Each of these makes the API return its JSON envelope instead of the raw document.

FlagAPI fieldMeaning
--format Fresponse_formathtml (API default), markdown, text, json (the envelope) or pdf (rendered by a full browser, chosen for you).
--extract JSON|@fileextractSelector map or JSON Schema. Result under data.
--ai PROMPTai_extract.promptComing soon Model extraction from a plain-language prompt. Adds 4 credits.
--ai-schema JSON|@fileai_extract.schemaComing soon Model extraction to a JSON Schema. Cannot be combined with --ai (exit 2): the API takes a prompt or a schema, not both.
--autoparseautoparseThe page's own JSON-LD, OpenGraph and embedded state, under data.
--linkslinksThe page's absolute, de-duplicated links, under links.

Cleaning

FlagAPI fieldMeaning
--main-contentmain_content_only: trueKeep only the main article.
--no-main-contentmain_content_only: falseKeep the whole document. Markdown, text and json default to main content; html does not. Setting both flags exits 2.
--include SELinclude_tagsKeep only matching subtrees. Repeatable.
--exclude SELexclude_tagsRemove matching elements. Repeatable.
--no-parse-pdfparse_pdf: falseRefuse PDF targets instead of parsing them to text (ERR::EXTRACT::FAILED, 0 credits).

Screenshots

A screenshot is rendered by a full browser, chosen for you. If it cannot be taken, the API returns ERR::REQUEST::CAPABILITY_UNSUPPORTED at 0 credits.

FlagAPI fieldMeaning
--screenshotscreenshotCapture a screenshot.
--screenshot-full-pagescreenshot_fullpageFull scrollable page.
--screenshot-selector SELscreenshot_selectorOne element. Mutually exclusive with --screenshot-full-page.
--screenshot-format Fscreenshot_formatpng, jpeg or webp.
--screenshot-quality Nscreenshot_qualityjpeg/webp quality, 1-100.
--screenshot-dir DIRnoneSave screenshots into DIR as <label>.<format>.

Cache and cost

FlagAPI fieldMeaning
--no-cachecache: falseForce a fresh fetch. A cache hit is billed at the same price as the fetch that stored it, so this changes freshness, not price.
--cache-ttl Scache_ttlAccept a cached copy no older than this many seconds. Default 172800 (48 hours).
--max-cost Nmax_costRefuse with ERR::LIMIT::MAX_COST_EXCEEDED (0 credits, exit 5) if the request could cost more.
--allowed-status 403,451allowed_status_codesExtra target statuses to treat as billable success (200, 404 and 410 always are). The command exits 0 on them instead of 6 (see target status errors). A non-number exits 2.
--original-statusoriginal_statusUse the target's status as the HTTP status. The CLI reads the target status either way, so exit codes and output do not change.

Output and retries (CLI only)

FlagDefaultMeaning
-o, --output FILEstdoutWrite the document to FILE (raw bytes for PDF), atomically. -o - means stdout. See writing to a file.
--metaoffHuman mode: print the rendering tier, credits, cache state, target status, proxy, final URL and request id to stderr. A no-op under --json (and when stdout is not a terminal): the JSON output already carries all of it.
--retry N1Attempts for errors the API marks retryable. 1 means no retry. Waits retry_after_seconds when given, else backs off from 1 s, doubling to at most 30 s.
--concurrency N4With -: parallel requests.
--jsonlon for -Print the result as one compact JSON line, even on a terminal. Always on for -.

Output

Human mode (terminal)

Prints the document itself: markdown, html or text. With --extract, --ai, --ai-schema or --autoparse it prints only the extracted data, indented, with the fields in the order the API returned them (0.1.4 or later; earlier versions sorted them alphabetically). With --links or --network-capture it prints that data after the document, under --- links (N) --- and --- network (N) --- lines. API warnings go to stderr as warning: ...: they print even with -q, and never in JSON mode, where they are in the output's warnings field.

--format pdf is binary and is never written to a terminal: pass -o FILE, or --json to get it base64-encoded. Without either, the command exits 2.

spicrawl scrape https://example.com/pricing --format markdown --meta
stderr
engine: fetch
credits: 1 (request cost 1)
cache: miss
target status: 200
proxy: direct
final url: https://example.com/pricing
request id: 01J9ZQ4M7R3T8VX2K5N6P0B1CD

engine: is a rendering tier (fetch, js or browser) whatever the API sent. --json and --jsonl print the API's values unchanged.

JSON mode (piped, or --json)

When the API answers with its JSON envelope (--format json, or any of --extract, --ai, --ai-schema, --autoparse, --links, --network-capture, --screenshot, or --actions containing a screenshot step or an evaluate with return_value), the envelope is printed as-is, in the API's key order, and large integers are not rounded (0.1.4 or later): url, final_url, status, content, credits, engine, proxy_source, warnings, data, links, screenshots, network.

When the API answers with the raw document (html, markdown, text), the CLI wraps it with the response headers:

{
  "cache_state": "miss",
  "content": "# Launch\n\nToday we ...",
  "content_type": "text/markdown; charset=utf-8",
  "credits": 1,
  "credits_charged": 1,
  "engine": "fetch",
  "final_url": "https://example.com/blog/launch",
  "proxy_source": "direct",
  "request_cost": 1,
  "request_id": "01J9ZQ4M7R3T8VX2K5N6P0B1CD",
  "status": 200,
  "url": "https://example.com/blog/launch",
  "warnings": []
}

status is the target site's status (X-Target-Status), not the API's. credits repeats credits_charged under the envelope's name. Binary documents (--format pdf) carry content_base64 instead of content. When the target's status is outside 2xx, the object also has target_error (see target status errors).

stderr in JSON mode is empty on success. On a failure it holds only the failure document, so spicrawl scrape ... 2>err.json; jq . err.json always parses: the API's problem document (code, retryable, request_id, ...), or {"error": "...", "exit_code": N} when the failure did not come from the API (a usage error, a network failure). Warnings, wrote N bytes and the deprecation notices are human-mode output only (0.1.4 or later; earlier versions printed them as prose on stderr, sometimes before the problem document).

spicrawl scrape https://example.com/blog/launch --format markdown | jq -r .content

Target status errors

A site that answers with an error is still a successful API call: the API returns HTTP 200 with the site's status in X-Target-Status (status in the output). The CLI treats a target status outside 2xx as a failure unless --allowed-status lists it. The document is still printed, stderr gets error: target returned 404 (pass --allowed-status 404 to accept it), and the exit code is 6. In JSON mode the printed object also has "target_error": "target returned 404".

spicrawl scrape https://example.com/old-page --allowed-status 404   # exits 0 on a 404 page

Writing to a file with -o

-o FILE writes the document (the extracted data when extraction is on, raw bytes for PDF) to FILE. In human mode it prints wrote N bytes to FILE on stderr (not with -q, never in JSON mode). The write is atomic: a temporary file in the same directory, then a rename, so a failure never truncates an existing FILE. -o - means stdout. In JSON mode stdout still carries the metadata object, without the written field and with "output": "FILE" added.

FILE, and the screenshots next to it, are written only when the target answered 2xx or its status is in --allowed-status (0.1.4 or later). On any other target status the earlier file is left alone, human mode prints warning: not writing FILE: the target failed (--allowed-status accepts it), and the command exits 6. If the write itself fails (no such directory, no permission), the billed result is printed to stdout as JSON instead of being lost, the error goes to stderr, and the command exits 1.

spicrawl scrape https://example.com/report --format pdf -o report.pdf

Screenshots in the envelope are saved next to FILE as <FILE stem>.<label>.<format>, or into --screenshot-dir. Their base64 data is replaced by "file": "<path>" in the printed JSON. With neither -o nor --screenshot-dir, human mode leaves screenshots unsaved and says so on stderr; JSON mode keeps them base64 in the output.

spicrawl scrape https://example.com --screenshot --screenshot-full-page -o home.html
# writes home.html and home.<label>.png

Live progress on a terminal

When stderr is a terminal (0.1.4 or later), a wait longer than about 300 ms shows a spinner there with a label and the elapsed time, for example ⠹ Scraping example.com/pricing · 4.2s. scrape and scrape - end with a one-line summary on stderr: ✓ or ✗, the page, the target status, the credits charged and the duration, for example ✓ example.com/pricing 200 · 1 credit · 4.2s. A request that finishes in under 300 ms shows the summary only. scrape - shows Scraping 2/3 done (1 failed) while it runs and ends with ✗ 3 URLs 2 ok · 1 failed · 1.5s. An API error is drawn as a short block that keeps the ERR:: code and the hint, with the target status, retryable, the request id (spicrawl logs get <id>) and a docs link.

stdout is never touched: the same bytes come out with or without it. It is off, and stderr is exactly what the sections above describe, when:

  • stderr is not a terminal (a pipe, a file, a CI log or an agent's tool call),
  • you pass --json or -q,
  • NO_COLOR is set to anything non-empty, or CLICOLOR=0,
  • TERM=dumb, or
  • CI is set to anything but empty, 0 or false.

Piping stdout alone does not turn it off: spicrawl scrape ... | jq in a terminal still shows the spinner. Outside a UTF-8 locale the glyphs fall back to ASCII (+ x and a | / - \ spinner).

Many URLs from stdin

spicrawl scrape - reads URLs from stdin, one per line. Blank lines and lines starting with # are skipped. It runs --concurrency requests at once (default 4) with the same flags for every URL, and prints one compact JSON line per URL in completion order, in every mode.

spicrawl scrape - --format markdown --concurrency 8 < urls.txt > pages.jsonl

Each success line is the result object above plus index, the 1-based position of the URL in the input. Each failure line is:

{"index":3,"url":"https://example.com/products/43","error":{"type":"https://docs.spicrawl.com/errors#UPSTREAM_CHALLENGE","title":"Target served a bot challenge","status":502,"code":"ERR::UPSTREAM::CHALLENGE","retryable":true,"doc_url":"https://docs.spicrawl.com/errors#UPSTREAM_CHALLENGE","target_status":403,"request_id":"01J9ZQ5B2C7D9EXAMPLE00000"}}

error is the API's problem document, or {"detail": "...", "exit_code": N} for a network or local failure.

  • Exit code is 0 when every URL succeeded, else the exit code of the first failure. A target status outside 2xx (and not in --allowed-status) is a failure: its line keeps the result and adds target_error, and the code is 6. In human mode a N ok, M failed line (on a live terminal, the summary line above) and error: M of N URLs failed go to stderr; in JSON mode stderr holds {"error": "M of N URLs failed", "exit_code": N}.
  • If stdout cannot be written the run stops at once with exit 1 instead of billing the remaining URLs. Ctrl-C stops it too, including while it waits for stdin (exit 130).
  • -o is refused with - (exit 2); use --screenshot-dir for screenshots, which are saved as <index>.<label>.<format>.
  • Empty stdin exits 2 with no URLs on stdin.

Errors

A failed scrape exits with the code for its code. In JSON mode stderr holds the API's problem document, or {"error": "...", "exit_code": N} for a failure that did not come from the API. In human mode it holds error:, hint:, target status:, retryable: and request id: lines, which a terminal draws as the block described under live progress. See exit codes.

You seeExitDo next
ERR::UPSTREAM::CHALLENGE6Retry with --premium-proxy Coming soon or your own residential --proxy; a challenge on those exits is retried on stronger rendering automatically. --mode auto also climbs on any exit.
ERR::UPSTREAM::TIMEOUT with --wait-for6Raise --wait-for-timeout or fix the selector.
ERR::REQUEST::INCOMPATIBLE_FLAGS4Remove the conflicting flag named in detail (for example --proxy-verify without --proxy).
ERR::LIMIT::MAX_COST_EXCEEDED5Raise --max-cost, or drop what makes the request dearer (--render, --screenshot, --format pdf).
ERR::PROXY::UNREACHABLE7Your --proxy did not accept a connection. Check the URL.
ERR::EXTRACT::FAILED8Check --ai prompt or schema; the call cost 0 credits.
ERR::ENGINE::RENDER_FAILED8A browser action failed (no element matched, a script threw); detail names the step. Fix the selector, add a wait_for before it, or mark the step "on_error":"skip". 0 credits.
target returned 404 (any status outside 2xx)6The site answered with an error page. The document is printed, but -o writes nothing. Pass --allowed-status 404 to accept it.
cannot reach the Spicrawl API10Nothing was sent. Check the network and --base-url, then retry.
timed out waiting for the Spicrawl API, or lost the connection ... after the request was sent12The request may have run and been billed. Check spicrawl logs before retrying; raise --timeout for a slow render.
Ctrl-C130Human mode prints interrupted; JSON mode prints nothing. A request already sent may still have run.

On this page