Skip to main content
The Spidra Node SDK searches the web, scrapes pages, runs browser actions, batch-processes URLs, and crawls entire sites. It takes care of the plumbing you’d otherwise write yourself: job submission and polling, automatic retries with backoff for transient failures, typed errors for everything that can go wrong, and streaming results for long-running jobs. Zero dependencies, and it runs anywhere fetch exists: Node 18+, browsers, and edge runtimes like Cloudflare Workers.

Installation

To install the Spidra Node SDK, you can use npm:
Get your API key from app.spidra.io under API Keys. Never hardcode it in source files. Use an environment variable instead.

Setup

Here’s an example of initializing the Spidra client in a Node.js or TypeScript project.

Searching

Search runs a real query and returns structured results: titles, links, descriptions, thumbnails. It’s the same data you’d get scraping a search engine yourself, minus the scraping. Unlike scrape/batch/crawl, a plain search usually resolves in a few seconds, often within the single search() call below with no polling at all.
By default only web results come back. Request more with sources:
If a source is temporarily unavailable, that source comes back as an empty array rather than failing the whole request.
Parameters

Research and developer sources

research searches academic papers, and returns authors and doi alongside the usual fields:
developer searches GitHub issues and pull requests. Write the query the way you’d type it into GitHub’s own search, qualifiers and all:
includeDomains, excludeDomains, and filetype only apply to web/news/images/videos. They’re not supported on research or developer.

Pagination and time range

Pass the previous response’s nextPageTokens back as pageTokens to get the next page of a specific source:
timeRange narrows results to a recency window, useful for sources like news where older results matter less:

Domain filtering

Restrict web results to specific domains, or keep specific domains out. Pass one or the other, never both.

Scrape content from results

Add scrapeOptions to also fetch each web result’s actual page content in the same call. No second request, no separate job to poll.
This makes the search take as long as its slowest scraped page, not the usual few seconds. Real scraping isn’t instant, and Spidra would rather you wait once on one job than build your own polling loop around N separate scrape jobs. maxResults caps how many top-ranked web results get scraped, omit it to scrape all of them, up to a cap of 10. A result that fails to scrape or falls outside the time budget is simply left without markdown, not treated as an error.
Need AI extraction, a schema, or a screenshot instead of plain markdown? Scrape that specific URL directly with spidra.scrape().

Submit now, check later

If you’d rather not wait on the call at all, submit the search and check on it yourself whenever you’re ready. Unlike search(), this never tries to resolve inline, it always returns a jobId immediately:

Scraping

All scrape jobs run asynchronously. scrape() submits a job and polls until it finishes. For manual control, use startScrape() and getScrape() directly. Up to 3 URLs can be passed per request and are processed in parallel.

Scrape a web page

Submit a scrape job and wait for results.
Parameters

Fire-and-forget approach

Fire-and-forget approach: submit a job immediately and poll on your own schedule.
Job statuses: queued · waiting · active · completed · failed

Structured JSON output

Pass a schema to enforce an exact output shape. Missing fields come back as null rather than hallucinated values, which matters when the output feeds a database or a typed pipeline downstream.
Define every field you want extracted. An untyped object with no properties (or an array of them) gives the AI nothing to fill in, so those members come back empty.
Use the Spidra JSON Schema Generator to build and preview your schema visually before pasting it here.

Structured output with Zod

If you already use Zod, skip the JSON Schema entirely. Pass your Zod schema and the SDK converts it for you. The result is typed from your schema, so content needs no casting:
The same works on batchScrape() (types each item’s result) and crawl() (types each page’s data). Zod is an optional peer dependency, install it only if you use this. Passing MySchema.shape by mistake throws a helpful error instead of failing silently.
If your schema uses a keyword the API doesn’t support (e.g. anyOf, $ref), the response includes schema_warnings listing what was ignored. The rest of the schema still applies.

Geo-targeted scraping

Route through a residential proxy in a specific country for geo-restricted content or localized pricing.
Supported codes include us, gb, de, fr, jp, au, ca, br, in, nl, sg, es, it, mx, and 40+ more. Use "global" or "eu" for regional routing.

Authenticated pages

Pass session cookies as a raw header string to scrape pages behind a login.

Browser actions

Run actions against the page before extraction. They execute in order, and the scrape happens after all actions complete.
Available actions Use selector for a CSS selector or XPath. Use value for plain English, Spidra locates the element using AI.

forEach: loop over every element

forEach finds a set of matching elements on the page and processes each one individually. Use it when you need to collect data from a list of items, paginate across pages, or click into each item’s detail page.
You don’t need forEach if all the data fits on a single page. A plain prompt is simpler and works just as well.
Use forEach when:
  • The list spans multiple pages and you need pagination
  • You need to click into each item’s detail page (navigate mode)
  • You have 20+ items and want consistent per-item AI extraction (itemPrompt)

inline mode

Read each element’s content directly without navigating away. Best for product cards, search results, and table rows.
Follow each element’s link to its destination page and capture content there. Best for product listings where full details are only on individual pages.

click mode

Click each element, capture the content that appears (modal, drawer, or expanded section), then move on. Best for hotel room cards, FAQ accordions, or any UI where clicking reveals hidden content.

Pagination

After processing all elements on the current page, follow the next-page link and continue.
maxItems applies across all pages combined. The loop stops when you hit maxItems, run out of elements, or reach maxPages.

Per-element actions

Run extra browser actions on each item after navigating or clicking into it, before content is captured. Useful for scrolling below the fold or expanding collapsed sections.

itemPrompt vs top-level prompt

Both are optional and serve different purposes: Use itemPrompt to extract fields from each item individually. Use the top-level prompt to filter, sort, or reshape the combined output. They can be used together.

Controlling how long a call waits

By default scrape(), batchScrape(), and crawl() wait until the job finishes, however long that takes, so a big crawl just works. If you’d rather cap the wait, pass a timeout; when it fires, a SpidraTimeoutError is thrown and the job keeps running server-side, so you can check it later with the matching get* method or cancel it.
Transient hiccups mid-wait (a 502 blip, a dropped connection, a rate limit) don’t kill the wait; the SDK keeps polling unless several happen in a row. The same options work on batchScrape() and crawl().

Batch scraping

When you have a list of URLs to process (a product catalog, a set of listings, a pile of article links) batch is the right tool. Submit up to 50 URLs in one request and they all run in parallel. Each URL is a plain string (not an object).

Scrape a list of URLs

Give it the same prompt you’d use for a single scrape, just with more URLs:
Item statuses: pending · running · completed · failed Batch statuses: pending · running · completed · failed · cancelled

Submit now, check later

If you don’t want to hold a connection open while 50 pages scrape, submit the batch and come back whenever:

Stream results as they finish

Rather than waiting for the whole batch, watchBatch() hands you each item the moment it completes. Useful for writing results to a database as they arrive or updating a progress bar:
Every item is delivered exactly once, including ones that already finished before you started watching. watcher.stop() stops listening without cancelling the batch.

Retry failed items

Re-queue only the items that failed. Successful items are not re-run.

Cancel a batch

Stops all pending items and refunds credits for unprocessed work.

List past batches

To look back through batches you’ve already run:

Bulk search: one template, many pages

Batch scraping needs you to list every URL. Bulk search is the shortcut: give one URL template and lists of values, for example every job title in every city, and the SDK builds the URLs, runs them in batches of 40, merges the results and removes duplicates. It is built on batch scraping, so there is no new endpoint. It works with any site, not only job boards: anything you can write as a URL with a few changing parts, such as product searches by keyword, listings by city, or one page per category. Each page costs 1 credit, plus AI tokens if you set a prompt or schema. Call estimateBulk first to see the cost before you spend anything.
For any other site, describe the items with a schema and point itemsPath at the list in it:
Parameters Every other batchScrape option, such as output, prompt, schema and useProxy, works here too and applies to every page.
Bulk search only runs the pages you ask for, it does not follow “next page” links. Some sites, Indeed for one, show only the first page of a search to visitors who are not signed in. For those, depth comes from many narrow searches, which is what bulk search is for.

Crawling

Crawling is different from scraping: you give it a starting URL and it discovers pages on its own, following links according to your instructions. Good for indexing a docs site, monitoring a competitor’s blog, or building a structured dataset from an entire section of a site.

Crawl a site

Give it a starting URL, what to look for, and what to pull from each page it finds:
Parameters Each returned page has depth (clicks from baseUrl) and parentUrl (the page it was found on).

Raw content mode

Omit both transformInstruction and schema to get the raw markdown of each page with no AI processing. No token credits are charged:

Structured output with schema

Use schema when you need every page to return the same fields in the same format:
Use the Spidra JSON Schema Generator to build and preview your schema visually before pasting it here.

Scoped crawling with path filters

Use includePaths and excludePaths to keep crawls focused. Both accept glob-style patterns:

Submit now, check later

If you don’t want to hold a connection open for a long crawl, submit it and check back whenever you like:

Watch a crawl page-by-page

A 50-page crawl can take a while. Instead of waiting for the whole thing, watchCrawl() streams each page to you the moment it’s crawled:
Every page arrives exactly once, including pages crawled before you started watching, and the SDK only re-fetches page content when the crawl actually makes progress, so watching stays cheap. watcher.stop() stops listening without cancelling the crawl.

Cancel a crawl

Cancel a queued or running job at any time. Pages already processed are preserved:

Download the raw HTML and Markdown

crawlPages() returns signed URLs for the raw HTML and Markdown of each crawled page. Links expire after 1 hour.

Re-extract with a different prompt

Crawled a site and want to pull out different information? You don’t have to re-crawl. crawlExtract() runs a new AI pass over the already-crawled content and charges only transformation credits.

History and stats

Look back through crawls you’ve already run, or check your total count for the account:

Get full job details

A flat snapshot of a crawl job’s config and counters, the same data crawlHistory() returns per row, for one job.
This endpoint returns raw database field names (pages_crawled, not pagesCrawled), unlike the rest of the SDK. Same as crawlHistory()’s entries.

Retry one page’s transformation

If a single page’s extraction failed, or you want to pull it again with a fresh AI pass, retry just that page instead of re-running the whole crawl. Charges credits for that page’s transformation only.

Download results as a zip

To get the raw files instead of working through the API, download everything a crawl produced as one zip:
include defaults to ['html', 'markdown', 'data'] if you omit it.

Why a page was skipped

The crawl report says how Spidra understood the instruction, how many pages it read to find the ones it returned (free), and every link it didn’t follow, with the reason and the page the link was on:
See GET /crawl/{jobId}/report for every reason.

Parsing documents

parse() turns a PDF, Word, Excel, PowerPoint or other document into Markdown page by page. Pass the file’s bytes, a link, or a fileId from an earlier upload. It waits for the result: short documents answer at once and longer ones are polled. 1 credit per page.
Options: mode ("auto", "fast", "ocr"), pages, maxPages, includePages, pageMarkers, prompt and schema. A document that can’t be parsed throws SpidraJobError. Also: estimateParse() (pages and credits, no charge), parseBatch(), uploadForParse() for files up to 500 MB, startParse()/getParse() and deleteParseFile(). See Parse.

Mapping a site

map() lists a site’s page URLs without scraping them, from its sitemap and the links on its pages. It answers in the same request for 1 credit, and is free when nothing is found.
Options: search, limit (default 500, up to 5,000), sitemap ("include", "only" or "skip"), includeSubdomains, ignoreQueryParams, respectRobots and cookies. See POST /map.

Logs

Every API scrape job is logged automatically. Access your full history with optional filters.

List and filter your logs

Filter parameters

Get one log with its full output

Fetch a single log entry, including the complete AI extraction result for that job.

Usage statistics

Returns credit and request usage broken down by day or week.

Retries and reliability

You don’t have to write retry loops. Transient failures (network blips, 502/503/504 gateway errors) are retried automatically with exponential backoff, so a single hiccup never fails your call. Both knobs are configurable:
The retry policy is designed so it can never double-charge you: 4xx client errors are never retried, and job submissions are only retried when the server explicitly rejected them. Never on network errors or gateway timeouts, where the job may already have been queued. When the server sends a Retry-After hint, the SDK honors it instead of its own backoff.

Error handling

Every non-2xx response throws a typed error class. Catch the specific class you care about, or fall back to the base SpidraError.
Error classes All error classes expose err.status (the HTTP status code, or 0 for non-HTTP errors like job failures and timeouts) and err.message. API errors also carry err.code (a machine-readable identifier like SERVICE_BUSY) and err.details (the raw error body).

Verifying webhooks

Crawl jobs can push crawl.page, crawl.completed, and crawl.failed events to your webhookUrl. Spidra signs each delivery with HMAC-SHA256 in the X-Spidra-Signature header, and the SDK ships a helper so you never accept a forged event:
The comparison is constant-time, and the helper works in Node, browsers, and edge runtimes. Always pass the raw request body, re-serializing parsed JSON produces different bytes and fails verification.

AI agent integration

Spidra works as a tool inside AI agent pipelines. Here is an example using the Vercel AI SDK with Claude:

SDKs Overview

Browse all official Spidra SDKs in one place.

PHP

Official PHP SDK with idiomatic helpers, typed exceptions, and configurable polling.