Skip to main content
Glama

Scrape a URL

spicrawl_scrape

Retrieve a web page and return its content as markdown (default), text, HTML, a JSON envelope, or a printed PDF. Handles the fetch vs. browser decision, charset decoding, and PDF-to-text for you. Optionally extract structured data with CSS selectors (extract) or autoparse (a natural-language ai_extract is coming soon), return discovered links, take a screenshot, or scope the DOM with include_tags/exclude_tags. Each successful call is billed in credits (a failure costs 0). Results are cached by default, and a cache hit is billed like the fetch that stored it (the cache saves time, not credits); set cache=false for time-sensitive pages. With the defaults it only reads the page. A method other than GET, HEAD or OPTIONS, and the actions that click, fill or run scripts on the page, can change state on the target site: set them only when the user asks for that. For markdown, text and HTML it returns { content }. With format: "json", or when extract, ai_extract, autoparse, links, screenshot or network_capture is set, it returns the JSON envelope: content, the site's status, credits, engine (the rendering tier: fetch, js or browser), warnings and data for extraction. Screenshots come back as image content blocks; each screenshots entry in the JSON keeps its metadata and says which block holds it. A PDF comes back as a resource content block, with its size, the rendering tier and the credits in the JSON.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "url": "<url>"
    },
    "name": "spicrawl_scrape"
  }
}

Input Schema

NameRequiredDescriptionDefault
urlYesThe absolute URL to retrieve.
modeNoRouting mode. `auto` lets the platform escalate fetch -> browser as needed, billing only the rung that worked.
waitNoMilliseconds to wait after load before capturing (render only).
cacheNoServe a recent cached result if fresh (on by default). A hit is billed at the same price as the fetch that stored it: the cache saves time, not credits. Set false only when you need live data.
linksNoAlso return the page's discovered links as an absolute, de-duplicated list.
proxyNoYour own proxy URL to egress through. Takes precedence over the pool; no surcharge.
formatNoOutput format. 'markdown' (default) is best for feeding to an LLM; 'html' returns the raw document; 'json' returns the main content as markdown in `content` plus `sections` ([{level, heading, text}], split on headings); 'pdf' prints the page in a full browser (routed there for you) and returns the file as a resource content block.markdown
methodNoHTTP method sent to the target (default GET; no request body can be sent). Anything but GET, HEAD or OPTIONS can change state on the target site: use it only when the user asks.
renderNoRender with a real browser (executes JavaScript). Use for single-page apps or pages whose content is painted client-side. Costs more than the default fetch tier. A plain fetch that gets only a near-empty script page (a bot challenge) fails with ERR::UPSTREAM::CHALLENGE and bills nothing; retry with `render: true` or your own `proxy`.
actionsNoBrowser steps run in order before capture (render only). Each step is {type, ...fields}. Examples: [{"type":"click","selector":"#load-more"},{"type":"wait_for","selector":".item:nth-child(20)","timeout_ms":5000}] · infinite scroll: [{"type":"scroll","to_bottom":true},{"type":"wait_for","ms":1000}] · login: [{"type":"fill","selector":"#user","value":"me"},{"type":"fill","selector":"#pass","value":"…","secret":true},{"type":"click","selector":"button[type=submit]","wait_for_navigation":true}]. A screenshot step switches the response to the JSON envelope (a FORMAT_COERCED warning says so).
extractNoSelector-based extraction: a map of field -> CSS/XPath selector, e.g. {"title":"h1","price":".amount"}. Returns structured `data`. A trailing `[]` returns every match as an array, e.g. {"titles":".titleline > a[]","links":"a[]@href"}; without it only the first match is returned. The full form {"selector":"...","kind":"list"} also works. Precise and cheap when you know the page structure.
stealthNoComing soon: not available yet, do not send. Stealth mode: render in a full browser hardened against bot detection. Default false.
max_costNoRefuse (rather than run) a request that would cost more than this many credits.
wait_forNoCSS selector to wait for before capturing (render only).
autoparseNoReturn the structured data the page publishes about itself (JSON-LD, OpenGraph, Twitter Card, microdata). No selectors; survives redesigns.
cache_ttlNoMax age in seconds of a cached copy to accept (0 forces a fresh fetch; capped at 48h). Freshness only: a hit is billed like a fetch.
parse_pdfNoTurn a PDF target into text/markdown (default true). false refuses PDFs.
ai_extractNoComing soon: not available yet, do not send. Model-driven extraction: describe what you want and let a model find it, no selectors. Refused with ERR::INTERNAL::UNAVAILABLE until it launches; use `extract` or `autoparse`.
screenshotNoCapture a screenshot (requires render).
session_idNoRun inside a session from spicrawl_session_create: same exit IP and browser state (cookies, localStorage) across calls.
sticky_keyNoComing soon: not available yet, do not send. Reuse the same managed-pool exit for every request carrying this key, without a session.
impersonateNoFetch tier only: present a real browser's TLS/JA3 + HTTP/2 fingerprint to clear passive bot gates. On by default; set false to send a plain client handshake.
exclude_tagsNoCSS selectors to remove (e.g. ['nav','.ad','#cookie']).
include_tagsNoCSS selectors to KEEP (everything else is dropped).
proxy_verifyNoVerify `proxy` is reachable before using it.
premium_proxyNoComing soon: not available yet, do not send. Use a residential exit from Spicrawl's managed pool instead of datacenter. Until then, pass your own `proxy`.
proxy_countryNoComing soon: not available yet, do not send. ISO-3166 alpha-2 country for a managed-pool exit, lowercase (e.g. 'us', 'de').
custom_headersNoExtra request headers sent to the target, e.g. {"Accept-Language":"de"}.
extract_presetNoComing soon: not available yet, do not send. A named extraction preset instead of an inline `extract` map; any value fails today. Put the rules in `extract`.
block_resourcesNoResource types the browser should not load, e.g. ["fonts","media","images"] (render only). Faster and cheaper.
network_captureNoRecord the API responses the PAGE fetched while rendering — often cleaner JSON than the DOM. Render only.
original_statusNoReturn the target's own HTTP status instead of 200 for a successful scrape.
wait_for_timeoutNoMax milliseconds to wait for `wait_for`.
main_content_onlyNoStrip the page to its main article, dropping nav/footer/aside. Defaults on for markdown and json.
screenshot_formatNoScreenshot image format.
screenshot_qualityNoScreenshot quality 1-100 (lossy formats).
screenshot_fullpageNoCapture the whole page, not just the viewport.
screenshot_selectorNoCapture only the element matching this CSS selector.
allowed_status_codesNoTarget statuses to treat as success instead of an error (e.g. [404] to capture a not-found page).

Output Schema

NameRequiredDescriptionDefault
pdfNoPresent for format=pdf: the file itself is a resource content block.
urlNoThe requested URL.
dataNoOutput of `extract` or `autoparse`.
linksNoThe page's links, absolute and de-duplicated, when `links` was set.
engineNoRendering tier that produced the page: fetch, js or browser.
statusNoThe target site's HTTP status (not this call's). Anything but 200/404/410 is charged 0.
contentNoThe page in the requested format (markdown, text or html); absent for a PDF.
creditsNoCredits charged for this call.
headersNoThe target's response headers.
networkNoResponses the page fetched, when `network_capture` was set.
warningsNoAdvisories about the call, e.g. an ignored option.
final_urlNoURL after redirects; may be empty.
truncatedNoThe target body exceeded the size cap and was cut.
diagnosticsNoWhich rendering tiers were tried, after an escalation.
screenshotsNoMetadata of each screenshot; the images are separate image content blocks.
empty_fieldsNo`extract` rules that matched nothing: usually a broken selector.
proxy_sourceNoWhose exit carried the request: pool, custom or direct.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changed
    • changedInput schema / properties / extract / description
      Previous value: -"Selector-based extraction: a map of field -> CSS/XPath selector, e.g. {\"title\":\"h1\",\"price\":\".amount\"}. Returns structured `data`. Precise and cheap when you know the page structure."New value: +"Selector-based extraction: a map of field -> CSS/XPath selector, e.g. {\"title\":\"h1\",\"price\":\".amount\"}. Returns structured `data`. A trailing `[]` returns every match as an array, e.g. {\"titles\":\".titleline > a[]\",\"links\":\"a[]@href\"}; without it only the first match is returned. The full form {\"selector\":\"...\",\"kind\":\"list\"} also works. Precise and cheap when you know the page structure."
    • changedInput schema / properties / format / description
      Previous value: -"Output format. 'markdown' (default) is best for feeding to an LLM; 'html' returns the raw document; 'json' returns a structured envelope; 'pdf' prints the page in a full browser (routed there for you) and returns the file as a resource content block."New value: +"Output format. 'markdown' (default) is best for feeding to an LLM; 'html' returns the raw document; 'json' returns the main content as markdown in `content` plus `sections` ([{level, heading, text}], split on headings); 'pdf' prints the page in a full browser (routed there for you) and returns the file as a resource content block."
    • changedInput schema / properties / main_content_only / description
      Previous value: -"Strip the page to its main article, dropping nav/footer/aside. Defaults on for markdown."New value: +"Strip the page to its main article, dropping nav/footer/aside. Defaults on for markdown and json."
    • changedInput schema / properties / render / description
      Previous value: -"Render with a real browser (executes JavaScript). Use for single-page apps or pages whose content is painted client-side. Costs more than the default fetch tier."New value: +"Render with a real browser (executes JavaScript). Use for single-page apps or pages whose content is painted client-side. Costs more than the default fetch tier. A plain fetch that gets only a near-empty script page (a bot challenge) fails with ERR::UPSTREAM::CHALLENGE and bills nothing; retry with `render: true` or your own `proxy`."
  2. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "$schema": "http://json-schema.org/draft-07/schema#",
      +  "additionalProperties": true,
      +  "properties": {
      +    "content": {
      +      "description": "The page in the requested format (markdown, text or html); absent for a PDF.",
      +      "type": "string"
      +    },
      +    "credits": {
      +      "description": "Credits charged for this call.",
      +      "type": "number"
      +    },
      +    "data": {
      +      "anyOf": [
      +        {
      +          "additionalProperties": {},
      +          "type": "object"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Output of `extract` or `autoparse`."
      +    },
      +    "diagnostics": {
      +      "anyOf": [
      +        {
      +          "additionalProperties": {},
      +          "type": "object"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Which rendering tiers were tried, after an escalation."
      +    },
      +    "empty_fields": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`extract` rules that matched nothing: usually a broken selector."
      +    },
      +    "engine": {
      +      "description": "Rendering tier that produced the page: fetch, js or browser.",
      +      "type": "string"
      +    },
      +    "final_url": {
      +      "description": "URL after redirects; may be empty.",
      +      "type": "string"
      +    },
      +    "headers": {
      +      "anyOf": [
      +        {
      +          "additionalProperties": {
      +            "type": "string"
      +          },
      +          "type": "object"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "The target's response headers."
      +    },
      +    "links": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "The page's links, absolute and de-duplicated, when `links` was set."
      +    },
      +    "network": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "additionalProperties": {},
      +            "type": "object"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Responses the page fetched, when `network_capture` was set."
      +    },
      +    "pdf": {
      +      "additionalProperties": true,
      +      "description": "Present for format=pdf: the file itself is a resource content block.",
      +      "properties": {
      +        "attached_as": {
      +          "description": "Which resource content block holds the PDF.",
      +          "type": "string"
      +        },
      +        "content_type": {
      +          "type": "string"
      +        },
      +        "not_attached": {
      +          "description": "Why the PDF is not attached, and how to get it.",
      +          "type": "string"
      +        },
      +        "size_bytes": {
      +          "type": "number"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "proxy_source": {
      +      "description": "Whose exit carried the request: pool, custom or direct.",
      +      "type": "string"
      +    },
      +    "screenshots": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "additionalProperties": true,
      +            "properties": {
      +              "attached_as": {
      +                "description": "Which image content block holds this screenshot.",
      +                "type": "string"
      +              },
      +              "format": {
      +                "type": "string"
      +              },
      +              "height": {
      +                "type": "number"
      +              },
      +              "label": {
      +                "type": "string"
      +              },
      +              "not_attached": {
      +                "description": "Why the image is not attached, and how to get it.",
      +                "type": "string"
      +              },
      +              "size_bytes": {
      +                "type": "number"
      +              },
      +              "width": {
      +                "type": "number"
      +              }
      +            },
      +            "type": "object"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Metadata of each screenshot; the images are separate image content blocks."
      +    },
      +    "status": {
      +      "description": "The target site's HTTP status (not this call's). Anything but 200/404/410 is charged 0.",
      +      "type": "number"
      +    },
      +    "truncated": {
      +      "description": "The target body exceeded the size cap and was cut.",
      +      "type": "boolean"
      +    },
      +    "url": {
      +      "description": "The requested URL.",
      +      "type": "string"
      +    },
      +    "warnings": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Advisories about the call, e.g. an ignored option."
      +    }
      +  },
      +  "type": "object"
      +}
  3. Changed4 schema fields changed
    • removedInput schema / properties / engine
      Removed value: -{
      -  "description": "Pin the execution engine instead of letting the router choose. A pinned engine the deployment does not run is refused (ERR::ENGINE::UNAVAILABLE), never substituted. `camoufox` is coming soon: do not pin it yet.",
      -  "enum": [
      -    "fetch",
      -    "obscura",
      -    "chromium",
      -    "camoufox"
      -  ],
      -  "type": "string"
      -}
    • changedInput schema / properties / format / description
      Previous value: -"Output format. 'markdown' (default) is best for feeding to an LLM; 'html' returns the raw document; 'json' returns a structured envelope; 'pdf' prints the page in a browser and returns the file as a resource content block (needs `render` or `engine: \"chromium\"`)."New value: +"Output format. 'markdown' (default) is best for feeding to an LLM; 'html' returns the raw document; 'json' returns a structured envelope; 'pdf' prints the page in a full browser (routed there for you) and returns the file as a resource content block."
    • removedInput schema / properties / headless
      Removed value: -{
      -  "description": "false runs Chromium on a real display (1920x1080 screen). chromium engine only.",
      -  "type": "boolean"
      -}
    • changedInput schema / properties / stealth / description
      Previous value: -"Coming soon: not available yet, do not send. Stealth mode: render in the hardened Camoufox browser. Default false."New value: +"Coming soon: not available yet, do not send. Stealth mode: render in a full browser hardened against bot detection. Default false."
  4. First observed

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far beyond the annotations, it discloses the billing model (credits per successful call, failures cost 0, cache hits billed at the same price), cache semantics and freshness trade-offs, the engine tier (`fetch`/`js`/`browser`), and the specific risk surface where `method`/`actions` can mutate the target. It even names a concrete failure mode (ERR::UPSTREAM::CHALLENGE) and its remedy. This is consistent with readOnlyHint=false and destructiveHint=false — the defaults are reads, while opt-in params carry the state-changing risk.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is long (roughly 300 words), but that is proportionate to a 39-parameter tool with nested objects, and it is front-loaded with output formats and safety before return shapes. There is avoidable repetition — the 'cache saves time, not credits' point is made twice in the description and again in the `cache` schema, which is wasted budget on a already-dense paragraph.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool of this complexity, the definition covers the decisions an agent must make: format choice, render escalation, cache freshness, credit exposure via `max_cost`, mutation risk, and output shape per mode. An output schema exists, yet the description still correctly sketches the envelope (`content`, `status`, `credits`, `engine`, `warnings`, `data`) without over-specifying, so nothing critical is left ambiguous.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 39 parameters, and the description largely summarizes rather than adds per-parameter meaning. It does add one genuinely useful cross-parameter rule — that `format: "json"` or any of `extract`/`autoparse`/`links`/`screenshot`/`network_capture` forces the JSON envelope — which is semantic interaction not expressible in the schema. Given the schema does the heavy lifting, this sits at the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a concrete verb and resource ('Retrieve a web page and return its content as markdown...') and enumerates the output formats and optional capabilities (extract, autoparse, links, screenshot), so the agent knows exactly what the tool produces. It never names a sibling (e.g. spicrawl_request_get or spicrawl_batch_submit) to delimit scope, leaving the fetch-vs-batch-vs-raw-request boundary to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives real usage conditions: use `render: true` for SPAs or after ERR::UPSTREAM::CHALLENGE, set `cache=false` for time-sensitive pages, and set non-GET `method`/`actions` only when the user explicitly asks. It also states the safe default ('With the defaults it only reads the page'). What's missing is explicit routing against alternatives — nothing says when to prefer the raw request tool, a batch task, or a browser session instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources