Skip to content

API reference

Vermin API

Web data for AI: scrape, crawl, map, search, extract and brand intelligence. Every response reports credits_used. Failed requests cost 0 credits.

Authentication. Send an API key as Authorization: Bearer vmn_live_…. Keys are created self-serve in the dashboard (free plan, no card) and are scoped to one organization; playground keys also carry their own credit budget.

Errors. Every 4xx/5xx response uses the Error schema: success: false, a machine-readable error.code (stable enum), a human-readable error.message and, when retrying makes sense, error.retry_after_ms. Branch on error.code, not on the message.

Permissions. Keys have full access by default. A restricted key carries scopes (run, read); each operation lists the scope it needs under security, and other requests get 403 forbidden. Keys can also have a credit budget per UTC day, week or month and an expiry date. See https://vermin.dev/docs/authentication.

Rate limits. Authenticated /v1 responses carry IETF RateLimit-Policy ("default";q=<requests>;w=60) and, when the request used the per-minute bucket, RateLimit ("default";r=<remaining>;t=<seconds>). A 429 rate_limited also sends Retry-After (seconds). See https://vermin.dev/docs/rate-limits.

Versioning. The major version is in the URL path (/v1). Within a major version changes are additive only: new endpoints, optional request fields, response fields and error codes. Breaking changes ship as a new path version (/v2). A deprecated operation is marked deprecated: true here and its responses carry Deprecation (RFC 9745) and Sunset (RFC 8594) headers at least 90 days before removal, with a Link: <…>; rel="deprecation" to the migration notes.

Base URL
https://api.vermin.dev
Spec
OpenAPI 3.1.0 · v1.0.0
POST/v1/scrape#

Scrape a single URL

operationId: scrape

Fetch one URL and return clean, LLM-ready content (markdown by default; also html, text, links, screenshot, token-counted chunks, metadata or brand). Starts with plain HTTP and escalates to a headless browser only when the page needs it. 1 credit per page, browser included; failed pages cost 0.

Try it in the Playground ▸
Parameters
  • Idempotency-Keystringheadermax 255 chars
Request body · application/json · ScrapeRequest
  • urlstring (uri)required
  • formatsFormat[]default ["markdown","metadata"]
    Basic metadata (title, description, language, canonical, status) is always returned; metadata adds parsed JSON-LD.
  • onlyMainContentbooleandefault true
  • siteMarkdownbooleandefault false
    Use the site's own markdown when it serves one (Accept: text/markdown, e.g. many docs sites) and it passes the quality check; otherwise the HTML is extracted as usual. About 2x faster where available, but usually with less metadata (no language or canonical URL). Ignored with the html, rawHtml or links formats.
  • includeTagsstring[]
  • excludeTagsstring[]
  • tier"auto" | "fetch" | "render" | "premium"default "auto"
    auto escalates fetch → render when the page needs it. premium is not available yet.
  • allowPremiumbooleandefault false
    Not available yet; requests with it set are refused.
  • actionsAction[]max 20 items
  • waitForinteger≤ 30000
  • timeoutintegerdefault 300001000–60000
  • maxAgeintegerdefault 172800000
    Serve a cached copy younger than this (ms). Default 2 days; 0 forces a fresh scrape. Cache hits are served from the nearest data center.
  • headersRecord<string, string>
  • screenshotOptionsScreenshotOptions
    How the screenshot formats capture the page. Needs the screenshot or screenshotFull format. Page actions run in the same window first. If the capture fails the scrape still succeeds without a screenshot, and quality.escalations says why: screenshot:element_not_found, screenshot:invalid_selector, screenshot:element_not_visible, screenshot:timeout or screenshot:render_failed.
  • locationobject
    Show 2 fields
    • countrystring
    • languagesstring[]
  • chunkSizeintegerdefault 1500200–8000
  • ignoreRobotsbooleandefault false
    Skip the robots.txt check. Only set this when you have permission to fetch the page.
Responses

OKapplication/json

  • successtruerequired
  • dataDocumentrequired
  • credits_usedintegerrequired
  • request_idstringrequired
  • latency_msinteger
curl -X POST "https://api.vermin.dev/v1/scrape" \
  -H "Authorization: Bearer $VERMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "formats": ["markdown", "metadata"] }'
Example 200 response
{
  "success": true,
  "data": {
    "url": "https://example.com",
    "finalUrl": "https://example.com/",
    "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples.",
    "metadata": {
      "title": "Example Domain",
      "description": "This domain is for use in illustrative examples.",
      "language": "en",
      "canonicalUrl": "https://example.com/",
      "statusCode": 200,
      "contentType": "text/html; charset=UTF-8",
      "publishedAt": "2026-09-01",
      "author": "Jane Doe",
      "ogImage": "https://example.com/og.png"
    },
    "tier": "fetch",
    "cached": false,
    "quality": {
      "score": 0.97,
      "issues": [
        "thin_content"
      ],
      "escalations": [
        "string"
      ],
      "reason": "Plain fetch returned complete content; no escalation needed."
    }
  },
  "credits_used": 1,
  "latency_ms": 184,
  "request_id": "req_01J9V3KX7Q"
}
POST/v1/crawl#

Start an asynchronous crawl

operationId: startCrawl

Start crawling a whole site. Sitemap-first discovery with include/exclude globs, robots.txt respected. Returns a job immediately; follow it with getCrawl, streamCrawl (SSE) or a signed webhook. 1 credit per page scraped successfully.

Try it in the Playground ▸
Parameters
  • Idempotency-Keystringheadermax 255 chars
Request body · application/json · CrawlRequest
  • urlstring (uri)required
  • limitintegerdefault 1001–100000
  • maxDepthinteger0–20
    Link hops from the start URL (sitemap URLs count as depth 1). 0 = only the start URL.
  • includePathsstring[]
    Path globs matched against path + query: * within a segment, ** across segments, a trailing $ anchors the end; patterns starting with / match from the start of the path.
  • excludePathsstring[]
    Path globs (same syntax as includePaths) never crawled.
  • allowSubdomainsbooleandefault false
  • sitemap"include" | "skip" | "only"default "include"
  • removeBoilerplatebooleandefault true
    Strip content repeated across pages (nav, footers)
  • ignoreRobotsbooleandefault false
  • scrapeOptionsScrapeOptions
  • webhookobject
    Deliver crawl events to this URL as signed POSTs (see the crawlEvent webhook). Deliveries are signed with your organisation's webhook signing secret (Dashboard → Webhooks); endpoints registered in the dashboard also receive crawl.* events, signed with their own secret.
    Show 3 fields
    • urlstring (uri)
    • events("started" | "page" | "completed" | "failed" | "cancelled")[]
      Default: all events.
    • metadataobject
      Echoed back in every delivery.
Responses

Acceptedapplication/json

  • idstringrequired
  • status"queued" | "running" | "completed" | "failed" | "cancelled"required
  • urlstring
  • createdAtstring (date-time)
curl -X POST "https://api.vermin.dev/v1/crawl" \
  -H "Authorization: Bearer $VERMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://docs.example.com",
    "limit": 100,
    "includePaths": ["/guides/**"]
  }'
Example 202 response
{
  "id": "crawl_01J9V4A7",
  "status": "queued",
  "url": "https://example.com",
  "createdAt": "2026-09-28T09:40:00Z"
}
GET/v1/crawl/{id}#

Get crawl status and a page of results

operationId: getCrawl

Return a crawl job's status and progress plus one page of scraped documents. Pass next from the previous response as cursor to page through results.

Try it in the Playground ▸
Parameters
  • idstringpathrequired
  • cursorstringquery
  • limitintegerquerydefault 501–100
Responses

OKapplication/json

  • idstringrequired
  • status"queued" | "running" | "completed" | "failed" | "cancelled"required
  • urlstring
  • createdAtstring (date-time)
  • totalinteger
  • completedinteger
  • failedinteger
  • credits_usedinteger
  • dataDocument[]
  • nextstring | null
    Cursor for the next page of results; null once the crawl is finished and every page has been returned.
  • errorobject
    Why the crawl failed or stopped early (e.g. spend_cap_reached, insufficient_credits, blocked_by_site). Pages crawled before the stop are still returned.
    Show 2 fields
    • codestringrequired
    • messagestringrequired
curl "https://api.vermin.dev/v1/crawl/crawl_01J9V4A7?limit=50" \
  -H "Authorization: Bearer $VERMIN_API_KEY"
Example 200 response
{
  "id": "crawl_01J9V4A7",
  "status": "queued",
  "url": "https://example.com",
  "createdAt": "2026-09-28T09:40:00Z",
  "total": 120,
  "completed": 118,
  "failed": 2,
  "credits_used": 1,
  "data": [
    {
      "url": "https://example.com",
      "finalUrl": "https://example.com/",
      "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples.",
      "metadata": {},
      "tier": "fetch",
      "cached": false,
      "quality": {}
    }
  ],
  "next": "cur_8Hq2",
  "error": {
    "code": "string",
    "message": "Human-readable explanation"
  }
}
DELETE/v1/crawl/{id}#

Cancel a crawl

operationId: cancelCrawl

Stop a running crawl. Pages already scraped stay available and are charged; queued pages are dropped at no cost.

Try it in the Playground ▸
Parameters
  • idstringpathrequired
Responses

OKapplication/json

  • idstringrequired
  • status"queued" | "running" | "completed" | "failed" | "cancelled"required
  • urlstring
  • createdAtstring (date-time)
curl -X DELETE "https://api.vermin.dev/v1/crawl/crawl_01J9V4A7" \
  -H "Authorization: Bearer $VERMIN_API_KEY"
Example 200 response
{
  "id": "crawl_01J9V4A7",
  "status": "queued",
  "url": "https://example.com",
  "createdAt": "2026-09-28T09:40:00Z"
}
GET/v1/crawl/{id}/events#

Server-sent events stream of crawl pages and status

operationId: streamCrawl

Events: page (data = Document JSON), status (data = CrawlStatus JSON without data), done (data = final CrawlStatus JSON without data). Each event has an id usable as Last-Event-ID. The stream replays every event after Last-Event-ID (or from the start), then follows the crawl live and closes after done. Comment lines (: keep-alive) are sent every 15s.

Try it in the Playground ▸
Parameters
  • idstringpathrequired
  • Last-Event-IDstringheader
    Resume after this event id (sent automatically by EventSource on reconnect).
Responses

SSE stream (page, status, done events)text/event-stream

curl -N "https://api.vermin.dev/v1/crawl/crawl_01J9V4A7/events" \
  -H "Authorization: Bearer $VERMIN_API_KEY" \
  -H "Accept: text/event-stream"
POST/v1/map#

Discover URLs on a site

operationId: map

Costs 3 credits per 10 HTTP fetches the map made (robots.txt, sitemap files, pages fetched for links, and the rendered-links fallback each count as one), rounded up, with a minimum of 3, however many URLs come back. The worst case (42 credits with sitemap: include) is reserved up front and the actual count is settled. With fewer credits left than the worst case, the map runs on the largest multiple of 3 you can afford (at least 3; otherwise insufficient_credits), stops fetching once that is used up, and returns what it found with budgetLimited: true. A map whose start page fails to load costs 0.

Try it in the Playground ▸
Request body · application/json · MapRequest
  • urlstring (uri)required
  • searchstring
    Rank URLs by relevance to this query
  • limitintegerdefault 50001–100000
  • includeSubdomainsbooleandefault false
  • sitemap"include" | "skip" | "only"default "include"
  • includePathsstring[]
    Only return URLs whose path matches one of these globs (CrawlRequest.includePaths syntax).
  • excludePathsstring[]
    Never return URLs whose path matches one of these globs.
Responses

OKapplication/json

  • successbooleanrequired
  • linksobject[]required
    Show 3 fields
    • urlstringrequired
    • titlestring
    • source"sitemap" | "link" | "render"
  • credits_usedintegerrequired
  • budgetLimitedboolean
    Set (true) when discovery stopped early because the credits available couldn't cover more fetches; links holds what was found, and credits_used never exceeds what could be reserved.
  • request_idstring
curl -X POST "https://api.vermin.dev/v1/map" \
  -H "Authorization: Bearer $VERMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://docs.example.com", "search": "pricing" }'
Example 200 response
{
  "success": true,
  "links": [
    {
      "url": "https://example.com",
      "title": "Example Domain",
      "source": "sitemap"
    }
  ],
  "budgetLimited": false,
  "credits_used": 1,
  "request_id": "req_01J9V3KX7Q"
}
POST/v1/extract#

Extract structured JSON from one or more URLs

operationId: extract

Scrapes every URL, then returns one merged data value that is validated against schema (every successful response conforms). Give a schema, a prompt, or both. Costs 15 credits per URL scraped successfully (29 when the page needed the premium proxy); URLs that fail cost nothing and are reported in sources. If no valid JSON can be produced the request fails with extract_failed (422) at 0 credits. Each organization can have a limited number of failed extracts per UTC day (25 on Free; more on paid plans); past that, extract answers rate_limited until midnight UTC.

Limits: schemas up to 32 KB; $ref must point inside the schema (#/$defs/…) and can't be recursive; patterns up to 256 characters without nested quantifiers. Pages are trimmed to about 12,000 tokens of the most relevant content. Scraping has a 60-second budget and the whole request a 150-second deadline (timeout, 504, at 0 credits); set your client timeout above that.

Try it in the Playground ▸
Parameters
  • Idempotency-Keystringheadermax 255 chars
Request body · application/json · ExtractRequest
  • urlsstring (uri)[]requiredmin 1 itemsmax 25 items
  • schemaobject
    JSON Schema for the output
  • promptstringmax 4000 chars
  • scrapeOptionsScrapeOptions
Responses

OKapplication/json

  • successbooleanrequired
  • dataanyrequired
    JSON matching the requested schema
  • credits_usedintegerrequired
  • sourcesobject[]
    Show 3 fields
    • urlstring
    • okboolean
    • errorstring
      Error code when the URL couldn't be scraped, e.g. blocked_url or timeout
  • request_idstring
curl -X POST "https://api.vermin.dev/v1/extract" \
  -H "Authorization: Bearer $VERMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com/pricing"],
    "schema": {
      "type": "object",
      "properties": {
        "plans": { "type": "array", "items": { "type": "string" } }
      }
    }
  }'
Example 200 response
{
  "success": true,
  "data": null,
  "sources": [
    {
      "url": "https://example.com",
      "ok": true,
      "error": "string"
    }
  ],
  "credits_used": 1,
  "request_id": "req_01J9V3KX7Q"
}
GET/v1/brand#

Brand identity for a domain or URL

operationId: brand

Return a company's logo, icon, colours, fonts, name and social links for a domain. 5 credits for a fresh lookup, 1 when served from cache.

Try it in the Playground ▸
Parameters
  • domainstringqueryrequired
    A domain or URL (IDNs accepted). Brands are per host: the path is ignored and a leading www. is dropped.
  • maxAgeintegerquery
    Accept a cached result up to this age (ms). Default 30 days. 0 forces a fresh lookup. Cached results older than 7 days are refreshed in the background.
Responses

OKapplication/json

  • successbooleanrequired
  • dataBrandrequired
  • credits_usedintegerrequired
  • cachedboolean
  • staleboolean
    A cached result older than maxAge, served because a fresh lookup failed transiently.
  • request_idstring
curl "https://api.vermin.dev/v1/brand?domain=stripe.com" \
  -H "Authorization: Bearer $VERMIN_API_KEY"
Example 200 response
{
  "success": true,
  "data": {
    "domain": "stripe.com",
    "name": "Stripe",
    "description": "This domain is for use in illustrative examples.",
    "logo": {
      "url": "https://example.com",
      "sourceUrl": "string",
      "type": "logo",
      "format": "png",
      "width": 256,
      "height": 256,
      "background": "light",
      "source": "sitemap"
    },
    "icon": {
      "url": "https://example.com",
      "sourceUrl": "string",
      "type": "logo",
      "format": "png",
      "width": 256,
      "height": 256,
      "background": "light",
      "source": "sitemap"
    },
    "colors": [
      {
        "hex": "#635BFF"
      }
    ],
    "logos": [
      {
        "url": "https://example.com"
      }
    ],
    "fonts": [
      {}
    ],
    "socials": [
      {}
    ],
    "confidence": 0.96
  },
  "cached": false,
  "stale": false,
  "credits_used": 1,
  "request_id": "req_01J9V3KX7Q"
}

Webhooks

Requests Vermin sends to you. See Webhooks & streaming for verification code in every language.

POSTwebhook: crawlEvent#

Crawl lifecycle and page events

operationId: crawlEvent

Sent for crawl.started, crawl.page, crawl.completed, crawl.failed and crawl.cancelled.

Signature. Every delivery carries Vermin-Signature: t=<unix seconds>,v1=<hex> where <hex> is the lowercase hex HMAC-SHA256 of "<t>.<raw request body>" keyed with the signing secret. Verify it with a constant-time comparison over the raw bytes (before parsing JSON) and reject timestamps more than 300 seconds from your clock. During secret rotation a header may carry several v1= values; accept if any matches. Every SDK ships a verifyWebhook helper; shared test vectors live in packages/spec/webhook-test-vectors.json.

Delivery. Respond with any 2xx within 10 seconds. Other responses and timeouts are retried with exponential backoff (10s, 20s, 40s … up to 1h between attempts, 8 attempts in total); a 410 Gone stops retries. Undeliverable events are recorded as dead letters in the dashboard. Deliveries may arrive out of order or more than once: de-duplicate on id and order on createdAt.

Parameters
  • Vermin-Signaturestringheaderrequired
  • Vermin-Eventstringheader
    The event type, e.g. crawl.page.
  • Vermin-Delivery-Attemptintegerheader≥ 1
Request body · application/json · WebhookEvent
  • idstringrequired
    Unique event id; identical across retries of the same event.
  • type"crawl.started" | "crawl.page" | "crawl.completed" | "crawl.failed" | "crawl.cancelled"required
  • createdAtstring (date-time)required
  • dataobjectrequired
    crawl.page: { id, url, document } (document = Document, boilerplate removed as in GET /v1/crawl/{id}); other events: CrawlStatus without data.
    Show 9 fields
    • idstringrequired
      Crawl id
    • urlstring
    • documentDocument
    • status"queued" | "running" | "completed" | "failed" | "cancelled"
    • totalinteger
    • completedinteger
    • failedinteger
    • credits_usedinteger
    • errorobject
      Show 2 fields
      • codestring
      • messagestring
  • metadataobject
    webhook.metadata from the crawl request.
Responses

Any 2xx acknowledges the delivery.

Example payload
{
  "id": "crawl_01J9V4A7",
  "type": "crawl.started",
  "createdAt": "2026-09-28T09:40:00Z",
  "data": {
    "id": "crawl_01J9V4A7",
    "url": "https://example.com",
    "document": {
      "url": "https://example.com",
      "finalUrl": "https://example.com/",
      "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples.",
      "metadata": {},
      "tier": "fetch",
      "cached": false,
      "quality": {}
    },
    "status": "queued",
    "total": 120,
    "completed": 118,
    "failed": 2,
    "credits_used": 1,
    "error": {
      "code": "string",
      "message": "Human-readable explanation"
    }
  },
  "metadata": {}
}

Schemas

Error

#
  • successfalserequired
  • errorobjectrequired
    Show 3 fields
    • codeenum (17)requiredone of invalid_request, unauthorized, forbidden, not_found, not_implemented, rate_limited, insufficient_credits, spend_cap_reached, blocked_url, fetch_failed, blocked_by_site, timeout, unsupported_content, capacity_exceeded, not_configured, extract_failed, internal
      insufficient_credits: the org lacks credits for the request's worst-case cost, or a playground key has spent its own credit budget. capacity_exceeded: our browser capacity, or the search or extraction provider's capacity, is exhausted (retry after retry_after_ms; the site did not block you). rate_limited: the key's requests per minute, or the organization's daily allowance of failed extracts, is used up (retry after retry_after_ms). not_configured: the requested tier or feature (e.g. premium, search, extract) is unavailable on this deployment. extract_failed: the model could not produce JSON matching your schema (after one retry).
      invalid_requestunauthorizedforbiddennot_foundnot_implementedrate_limitedinsufficient_creditsspend_cap_reachedblocked_urlfetch_failedblocked_by_sitetimeoutunsupported_contentcapacity_exceedednot_configuredextract_failedinternal
    • messagestringrequired
    • retry_after_msinteger
  • credits_used0
  • request_idstring

Format

#
markdownhtmlrawHtmltextlinksscreenshotscreenshotFullchunksmetadatabrand

Action

#
  • type"wait" | "click" | "scroll" | "type" | "press"required
  • selectorstring
  • textstring
  • msinteger≤ 30000
  • keystring

ScreenshotOptions

#

How the screenshot formats capture the page. Needs the screenshot or screenshotFull format. Page actions run in the same window first. If the capture fails the scrape still succeeds without a screenshot, and quality.escalations says why: screenshot:element_not_found, screenshot:invalid_selector, screenshot:element_not_visible, screenshot:timeout or screenshot:render_failed.

  • viewportobject
    Browser window in CSS pixels (default 1280 × 800 at 1x, desktop). Width and height × deviceScaleFactor must each be at most 7680.
    Show 4 fields
    • widthintegerrequired240–3840
    • heightintegerrequired240–3840
    • deviceScaleFactornumberdefault 11–3
      Device pixel ratio: 2 gives a retina image twice the width and height.
    • mobilebooleandefault false
      Mobile emulation: honours the page's meta viewport and enables touch. The site still sees the normal browser user agent.
  • selectorstringmax 1000 chars
    Capture only the first element matching this CSS selector, whole, even beyond the window (cut off past 16384 device pixels). Waits up to 5 seconds for it to appear. Only with the screenshot format, not screenshotFull.

ScrapeOptions

#
  • formatsFormat[]default ["markdown","metadata"]
    Basic metadata (title, description, language, canonical, status) is always returned; metadata adds parsed JSON-LD.
  • onlyMainContentbooleandefault true
  • siteMarkdownbooleandefault false
    Use the site's own markdown when it serves one (Accept: text/markdown, e.g. many docs sites) and it passes the quality check; otherwise the HTML is extracted as usual. About 2x faster where available, but usually with less metadata (no language or canonical URL). Ignored with the html, rawHtml or links formats.
  • includeTagsstring[]
  • excludeTagsstring[]
  • tier"auto" | "fetch" | "render" | "premium"default "auto"
    auto escalates fetch → render when the page needs it. premium is not available yet.
  • allowPremiumbooleandefault false
    Not available yet; requests with it set are refused.
  • actionsAction[]max 20 items
  • waitForinteger≤ 30000
  • timeoutintegerdefault 300001000–60000
  • maxAgeintegerdefault 172800000
    Serve a cached copy younger than this (ms). Default 2 days; 0 forces a fresh scrape. Cache hits are served from the nearest data center.
  • headersRecord<string, string>
  • screenshotOptionsScreenshotOptions
    How the screenshot formats capture the page. Needs the screenshot or screenshotFull format. Page actions run in the same window first. If the capture fails the scrape still succeeds without a screenshot, and quality.escalations says why: screenshot:element_not_found, screenshot:invalid_selector, screenshot:element_not_visible, screenshot:timeout or screenshot:render_failed.
  • locationobject
    Show 2 fields
    • countrystring
    • languagesstring[]
  • chunkSizeintegerdefault 1500200–8000
  • ignoreRobotsbooleandefault false
    Skip the robots.txt check. Only set this when you have permission to fetch the page.

ScrapeRequest

#
  • urlstring (uri)required
  • formatsFormat[]default ["markdown","metadata"]
    Basic metadata (title, description, language, canonical, status) is always returned; metadata adds parsed JSON-LD.
  • onlyMainContentbooleandefault true
  • siteMarkdownbooleandefault false
    Use the site's own markdown when it serves one (Accept: text/markdown, e.g. many docs sites) and it passes the quality check; otherwise the HTML is extracted as usual. About 2x faster where available, but usually with less metadata (no language or canonical URL). Ignored with the html, rawHtml or links formats.
  • includeTagsstring[]
  • excludeTagsstring[]
  • tier"auto" | "fetch" | "render" | "premium"default "auto"
    auto escalates fetch → render when the page needs it. premium is not available yet.
  • allowPremiumbooleandefault false
    Not available yet; requests with it set are refused.
  • actionsAction[]max 20 items
  • waitForinteger≤ 30000
  • timeoutintegerdefault 300001000–60000
  • maxAgeintegerdefault 172800000
    Serve a cached copy younger than this (ms). Default 2 days; 0 forces a fresh scrape. Cache hits are served from the nearest data center.
  • headersRecord<string, string>
  • screenshotOptionsScreenshotOptions
    How the screenshot formats capture the page. Needs the screenshot or screenshotFull format. Page actions run in the same window first. If the capture fails the scrape still succeeds without a screenshot, and quality.escalations says why: screenshot:element_not_found, screenshot:invalid_selector, screenshot:element_not_visible, screenshot:timeout or screenshot:render_failed.
  • locationobject
    Show 2 fields
    • countrystring
    • languagesstring[]
  • chunkSizeintegerdefault 1500200–8000
  • ignoreRobotsbooleandefault false
    Skip the robots.txt check. Only set this when you have permission to fetch the page.

Chunk

#
  • indexintegerrequired
  • textstringrequired
  • headingPathstring[]
  • tokensinteger

Metadata

#
  • titlestring
  • descriptionstring
  • languagestring
  • canonicalUrlstring
  • statusCodeinteger
  • contentTypestring
  • publishedAtstring
  • authorstring
  • ogImagestring

Quality

#

Why content may be thin, and what the pipeline did about it.

  • scorenumber0–1
  • issuesenum (8)[]
  • escalationsstring[]
  • reasonstring
    Why the returned tier was used or why escalation stopped, in words.

Document

#
  • urlstringrequired
  • finalUrlstring
  • markdownstring
  • htmlstring
  • rawHtmlstring
  • textstring
  • linksstring[]
  • screenshotstring
    Signed URL (expires in 24h)
  • chunksChunk[]
  • metadataMetadata
  • brandBrand
  • tier"fetch" | "render" | "premium"
  • cachedboolean
  • qualityQuality
    Why content may be thin, and what the pipeline did about it.

ScrapeResponse

#
  • successtruerequired
  • dataDocumentrequired
  • credits_usedintegerrequired
  • request_idstringrequired
  • latency_msinteger

CrawlRequest

#
  • urlstring (uri)required
  • limitintegerdefault 1001–100000
  • maxDepthinteger0–20
    Link hops from the start URL (sitemap URLs count as depth 1). 0 = only the start URL.
  • includePathsstring[]
    Path globs matched against path + query: * within a segment, ** across segments, a trailing $ anchors the end; patterns starting with / match from the start of the path.
  • excludePathsstring[]
    Path globs (same syntax as includePaths) never crawled.
  • allowSubdomainsbooleandefault false
  • sitemap"include" | "skip" | "only"default "include"
  • removeBoilerplatebooleandefault true
    Strip content repeated across pages (nav, footers)
  • ignoreRobotsbooleandefault false
  • scrapeOptionsScrapeOptions
  • webhookobject
    Deliver crawl events to this URL as signed POSTs (see the crawlEvent webhook). Deliveries are signed with your organisation's webhook signing secret (Dashboard → Webhooks); endpoints registered in the dashboard also receive crawl.* events, signed with their own secret.
    Show 3 fields
    • urlstring (uri)
    • events("started" | "page" | "completed" | "failed" | "cancelled")[]
      Default: all events.
    • metadataobject
      Echoed back in every delivery.

CrawlJob

#
  • idstringrequired
  • status"queued" | "running" | "completed" | "failed" | "cancelled"required
  • urlstring
  • createdAtstring (date-time)

CrawlStatus

#
  • idstringrequired
  • status"queued" | "running" | "completed" | "failed" | "cancelled"required
  • urlstring
  • createdAtstring (date-time)
  • totalinteger
  • completedinteger
  • failedinteger
  • credits_usedinteger
  • dataDocument[]
  • nextstring | null
    Cursor for the next page of results; null once the crawl is finished and every page has been returned.
  • errorobject
    Why the crawl failed or stopped early (e.g. spend_cap_reached, insufficient_credits, blocked_by_site). Pages crawled before the stop are still returned.
    Show 2 fields
    • codestringrequired
    • messagestringrequired

WebhookEvent

#
  • idstringrequired
    Unique event id; identical across retries of the same event.
  • type"crawl.started" | "crawl.page" | "crawl.completed" | "crawl.failed" | "crawl.cancelled"required
  • createdAtstring (date-time)required
  • dataobjectrequired
    crawl.page: { id, url, document } (document = Document, boilerplate removed as in GET /v1/crawl/{id}); other events: CrawlStatus without data.
    Show 9 fields
    • idstringrequired
      Crawl id
    • urlstring
    • documentDocument
    • status"queued" | "running" | "completed" | "failed" | "cancelled"
    • totalinteger
    • completedinteger
    • failedinteger
    • credits_usedinteger
    • errorobject
      Show 2 fields
      • codestring
      • messagestring
  • metadataobject
    webhook.metadata from the crawl request.

MapRequest

#
  • urlstring (uri)required
  • searchstring
    Rank URLs by relevance to this query
  • limitintegerdefault 50001–100000
  • includeSubdomainsbooleandefault false
  • sitemap"include" | "skip" | "only"default "include"
  • includePathsstring[]
    Only return URLs whose path matches one of these globs (CrawlRequest.includePaths syntax).
  • excludePathsstring[]
    Never return URLs whose path matches one of these globs.

MapResponse

#
  • successbooleanrequired
  • linksobject[]required
    Show 3 fields
    • urlstringrequired
    • titlestring
    • source"sitemap" | "link" | "render"
  • credits_usedintegerrequired
  • budgetLimitedboolean
    Set (true) when discovery stopped early because the credits available couldn't cover more fetches; links holds what was found, and credits_used never exceeds what could be reserved.
  • request_idstring

SearchRequest

#
  • querystringrequiredmax 500 chars
  • limitintegerdefault 101–50
  • countrystring
    Two-letter ISO 3166-1 country code for localised results, e.g. us, de
  • languagestring
    Language code (ISO 639-1, optionally with a region), e.g. en, pt-BR
  • timeRange"day" | "week" | "month" | "year"
  • scrapeOptionsScrapeOptions
    If set, each result is scraped (billed as scrapes). Results are scraped within a fixed time budget; any not scraped in time are returned without a document and cost nothing.

SearchResponse

#
  • successbooleanrequired
  • resultsobject[]required
    Show 7 fields
    • urlstringrequired
    • titlestringrequired
    • snippetstring
    • positioninteger
    • datestring
      Publication date as reported by the search provider (ISO 8601 when available, otherwise its text such as 3 days ago)
    • sourcestring
      Site or publisher name when the provider reports one
    • documentDocument
  • credits_usedintegerrequired
  • request_idstring

ExtractRequest

#
  • urlsstring (uri)[]requiredmin 1 itemsmax 25 items
  • schemaobject
    JSON Schema for the output
  • promptstringmax 4000 chars
  • scrapeOptionsScrapeOptions

ExtractResponse

#
  • successbooleanrequired
  • dataanyrequired
    JSON matching the requested schema
  • credits_usedintegerrequired
  • sourcesobject[]
    Show 3 fields
    • urlstring
    • okboolean
    • errorstring
      Error code when the URL couldn't be scraped, e.g. blocked_url or timeout
  • request_idstring

Brand

#
  • domainstring
  • namestring
  • descriptionstring
  • colorsobject[]
    Show 4 fields
    • hexstringrequired
    • role"primary" | "accent" | "background" | "text"
    • textColorstring
      Readable text colour on this background
    • contrastnumber
      WCAG contrast ratio of textColor on hex (1-21).
  • logosBrandImage[]
    Every image found (icon and logo), each tagged with its type.
  • fontsobject[]
    Show 2 fields
    • familystring
    • role"heading" | "body" | "mono"
  • socialsobject[]
    Show 2 fields
    • networkstring
    • urlstring
  • confidencenumber

BrandImage

#
  • urlstringrequired
    Vermin-hosted copy when available (stable, sanitised), else the site's own URL.
  • sourceUrlstring
    The image's original URL on the site (absent for inline SVGs).
  • type"logo" | "icon" | "wordmark"
    icon: square mark for avatars. logo: the full lockup. wordmark: a text-only logo.
  • formatenum (7)one of png, jpeg, gif, webp, ico, svg, avif
    pngjpeggifwebpicosvgavif
  • widthinteger
  • heightinteger
  • background"light" | "dark" | "any"
    Backgrounds the image reads well on: light (dark artwork on transparency), dark (light artwork), any (opaque or mixed).
  • sourcestring

BrandResponse

#
  • successbooleanrequired
  • dataBrandrequired
  • credits_usedintegerrequired
  • cachedboolean
  • staleboolean
    A cached result older than maxAge, served because a fresh lookup failed transiently.
  • request_idstring