Documentation
Web scraping
Scrape, crawl, and extract content from the web in vendor tiers: Exa (cheapest, no JS), Firecrawl and CRW (JS rendering plus a full scrape/crawl/map/extract platform — interchangeable request shapes), Tavily extract (the batch rung for known URLs), ScrapingAnt (headless Chrome behind a residential-proxy pool for hard pages), and Apify (typed rows from named platforms). Start with the cheapest option that works; escalate only when you need JS, multi-page, structured extraction, or the residential-proxy rung.
When to use which
| Need | Call | Price | Pick when |
|---|---|---|---|
| Cheap text from known URLs | exa/contents | 0.1¢/URL | Default for plain text extraction. No JS rendering. |
| Clean markdown / JS-rendered page | firecrawl/scrape | 1¢ | Structured output, main-content stripping, anti-bot/JS handling. |
| Same, with stealth / proxy-country / JSON-schema knobs | crw/scrape | 1¢ | Firecrawl-compatible; the fall-through vendor. |
| Batch content from known URLs (JS ok) | tavily/extract | 1¢ | ≤5 URLs per call — cheapest batch rung after Exa. |
| Multi-page crawl | firecrawl/crawl | 1¢ | Indexing docs sites, product catalogs. |
| Crawl with retrievable results | crw/crawl + crw/crawl_status | 10¢ + 1¢/poll | Async job you can actually poll for the pages. |
| Discover site URLs | firecrawl/map · crw/map | 1¢ | Recon before targeted scraping. |
| Search + scrape combined | firecrawl/search · crw/search | 1¢ | Full page content from search results in one call. |
| Structured extraction | firecrawl/extract | 1¢ | Typed data (prices, specs, contacts) via prompt or schema. |
| Structured extraction, async multi-URL | crw/extract + crw/extract_status | 5¢ + 1¢/poll | ≤10 URLs, schema + per-field source evidence. |
| Cloudflare / anti-bot / geo-fenced page | scrapingant/scrape_residential | 4¢ | Residential proxies — after plain scrapes fail. |
| Rendered page via proxy pool | scrapingant/scrape · scrapingant/markdown | 1¢ | Datacenter rung; markdown for LLM input. |
| No-schema AI extraction | scrapingant/extract | 20¢ | Plain-English field list → JSON. |
| Platform-structured data | apify/* | ≈1¢/25 rows | Social posts, reviews, listings from a named platform. |
| Interactive browser | browserbase/create_session | 0.2¢/min | Click, fill, log in — see Browser. |
The escalation ladder within scraping: exa/contents → firecrawl/crw scrape → scrapingant/scrape → scrapingant/scrape_residential → a browser session (only if you must click or log in). For a managed version of this ladder in one call, pass facets: ["scrape"] to SuperSearch.
Measured performance
From a 12-page, 5-vendor benchmark (August 2026; pages included JS SPAs and bot-hostile marketing pages):
| Provider | Success | p50 | p95 | Verdict |
|---|---|---|---|---|
firecrawl/scrape | 100% | 0.32s | 0.6s | Default full-render rung — 100% in every pass, tight tail. |
crw/scrape | 100% | 0.9s | 2.1s | Same coverage, slower — the fall-through vendor. |
exa/contents | 100%* | 0.1s | 0.2s warm | 10× cheaper, no JS — *thin content on JS-heavy pages when cold; cache-dependent. |
tavily/extract | 91.7% | 0.03s warm | 1.1s | ≤5 URLs/call; thin extract on one wiki page every pass. |
scrapingant/* | 33–92% | 4.9s | 26s | Escalation rung only — serializes browser renders; returns 409 under load (retry serially). |
- Known-static pages →
exa/contents(0.1¢); anything possibly JS-rendered →firecrawl/scrape. - Slow-tail pages differ per vendor — racing
firecrawl+crwon latency-critical scrapes cut p95 to 0.2–0.6s vs 2–26s single-vendor tails. - CRW is coverage-equal but never faster — use it when Firecrawl errors, not for speed.
- ScrapingAnt is for Cloudflare / anti-bot / geo-fenced pages the cheap rungs bounce off — send it one page at a time and retry on 409, never batch through it.
exa/contents
- Price
- 0.1¢ per (URL + id) × per content field, min 1¢
- Latency
- fast (~0.1s warm)
- JS rendering
- no
Fetch and clean text from URLs (or Exa document IDs) you already have — the cheapest text-extraction option in the catalog when JS rendering isn’t needed. Use it for pulling article bodies for summarization, extracting docs from URLs returned by a previous exa/search, or batch-fetching many pages in one call.
curl -X POST https://vaaya.ai/api/run/exa/contents \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/article", "https://example.com/about"],
"text": true
}'Provide urls (full URLs) or ids (Exa document IDs from exa/search), or both. Each content field you request — text, highlights, summary — multiplies the cost, ceiling-rounded to whole cents: 1 URL with text → 1¢ (minimum charge); 10 URLs with text → 1¢; 10 URLs with text + highlights + summary → 3¢.
Gotchas
- Asking for
highlightsandsummaryon top oftexttriples the cost without much marginal value if you’re feeding the page into an LLM anyway — pick the minimum field set. - Cold fetches of JS-heavy pages can return thin content (the extraction is cache-dependent) — escalate those to
firecrawl/scrape.
firecrawl/scrape
- Price
- 1¢
- Latency
- medium (~0.3s p50)
- Output
- clean markdown or HTML
Scrape a single URL and return clean markdown or HTML. Handles JS rendering, anti-bot bypassing, and content cleaning automatically — the go-to when you need clean, structured output from one page.
curl -X POST https://vaaya.ai/api/run/firecrawl/scrape \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"formats": ["markdown"],
"onlyMainContent": true,
"waitFor": 3000
}'url— required.formats— output formats, default["markdown"]; alsohtml.onlyMainContent— strips navigation, footers, sidebars.waitFor— milliseconds to wait for JS to finish before scraping.
Vs. Exa: use exa/contents (0.1¢/URL) when you don’t need JS rendering; use Firecrawl when you want boilerplate-stripped markdown or JS / anti-bot handling.
firecrawl/crawl, firecrawl/map, firecrawl/search
- Price
- 1¢ each
- Latency
- medium (
map,search) · slow (crawl, multi-page)
firecrawl/crawl crawls an entire website from a seed url, following links — good for indexing documentation sites or scraping product catalogs. limit caps total pages; maxDepth controls link hops from the seed.
curl -X POST https://vaaya.ai/api/run/firecrawl/crawl \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://docs.stripe.com/payments", "limit": 20, "maxDepth": 2}'- Always set
limitto avoid runaway crawls on large sites — start with 10–20 pages, check results, then increase. firecrawl/map({ url }) discovers all URLs on a site without scraping content — a reconnaissance step: map first, thenfirecrawl/scrapethe specific pages you need.firecrawl/search({ query, limit }) combines web search with scraping in one call — full page content from the results, not just snippets. Useexa/search(1¢) for quick semantic search with snippets; use this when you need the content with the hits.
firecrawl/extract
- Price
- 1¢
- Latency
- medium
Extract structured data from URLs using a natural-language prompt or a JSON schema (or both) — clean, typed data instead of raw HTML. urls (array) is required. Use cases: comparing competitor pricing, pulling product specs from e-commerce pages, extracting contact info from company pages, building datasets from structured web pages.
curl -X POST https://vaaya.ai/api/run/firecrawl/extract \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/pricing", "https://competitor.com/pricing"],
"prompt": "Extract pricing tiers: name, monthly price, annual price, key features"
}'crw — the Firecrawl-compatible second vendor
- Price
- 1¢ scrape/search/map · 10¢ crawl · 5¢ extract
- Latency
- sync (scrape/search/map) · async (crawl, extract)
- Polling
crw/crawl_status·crw/extract_status— 1¢ per poll
CRW mirrors the Firecrawl request shapes (url, formats, onlyMainContent, waitFor) at the same 1¢ scrape price, so the two are interchangeable for routine scraping. Use CRW as the fall-through when Firecrawl errors — or first when you need its extra knobs: stealth: true (anti-bot browser), country (2-letter proxy egress), renderJs (null = auto-detect), jsonSchema (structured output straight from a scrape).
crw/scrape(1¢, sync) — single URL → markdown/HTML/JSON.crw/search(1¢, sync) — web search, optionally scraping the hits in one call (scrapeOptions).tbs(qdr:h|d|w|m|y) filters by freshness;sourcespicks web, news, or images.crw/map(1¢, sync) — discover a site’s URLs without scraping (sitemap + fallback).crw/crawl(10¢, async) — multi-page crawl from a seed URL. One 10¢ price covers the whole job (≤100 pages). This is the crawl whose results you can actually retrieve — prefer it overfirecrawl/crawlwhen the crawled pages themselves are the deliverable.crw/extract(5¢, async) — LLM extraction over up to 10 URLs with apromptand/or JSONschema;basis: trueadds per-field source evidence. Readresults[].datawhen complete.
# 1) start the crawl — returns { id } immediately
curl -X POST https://vaaya.ai/api/run/crw/crawl \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://docs.example.com", "maxPages": 20, "maxDepth": 2}'
# 2) poll (1¢ per poll) until status is "completed" — data holds the pages
curl -X POST https://vaaya.ai/api/run/crw/crawl_status \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "crawl_..."}'Gotchas
- Crawl and extract bill at launch — the status polls are separate 1¢ calls, so don’t poll more than every few seconds.
- The vendor bills per page on crawls — always set
maxPagessmall first and keep it tight.
scrapingant — residential proxies + headless Chrome for hard pages
- Price
- 1¢ (
scrape,markdown) · 4¢ (scrape_residential) · 20¢ (extract) - Latency
- slow (~5s p50, long tail)
- Proxy pool
- 3M+ rotating IPs, 100+ countries
The escalation rung when normal scraping bounces: Cloudflare or anti-bot walls, geo-fenced content, sites that block datacenter IPs. A managed headless-Chrome cluster over a rotating residential-proxy pool. All actions share the same knobs: url (required), browser (default true), wait_for_selector, js_snippet (base64 JS run on the page), proxy_country (2-letter), timeout (5–60s), cookies, return_page_source.
scrapingant/scrape(1¢) — Chrome-rendered page over datacenter proxies →{ raw: "<html>…" }. The cheap rung; try it before residential.scrapingant/scrape_residential(4¢) — the same call through the residential pool. Escalate here only after a plain scrape (Firecrawl / CRW / ScrapingAnt datacenter) fails.scrapingant/markdown(1¢) — scrape → LLM-ready markdown, when you want the proxy pool behind a clean-text fetch.scrapingant/extract(20¢) — AI extraction without a schema: passextract_propertiesas plain English, e.g.product title, price(number), reviews(list: review title, review content)→ JSON. The priciest extraction rung — prefercrw/extract(5¢, schema-driven) unless you specifically want the no-schema form or the proxy pool.
curl -X POST https://vaaya.ai/api/run/scrapingant/scrape_residential \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://blocked-site.com", "proxy_country": "de"}'Gotchas
scrapeandscrape_residentialreturn raw HTML, not cleaned markdown — use themarkdownaction when feeding an LLM.- It serializes browser renders and returns 409 under load — send one page per call and retry serially, never batch through it.
tavily/extract — batch content from known URLs
- Price
- 1¢ per call (≤5 URLs)
- Latency
- very fast on repeat URLs (vendor cache)
Batch content from URLs you already have: pass urls (≤5) and an optional format (markdown default, or text). The cheapest batch rung after exa/contents (0.1¢/URL, no JS) when the pages need JS handling; for bigger batches, make multiple calls.
curl -X POST https://vaaya.ai/api/run/tavily/extract \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://example.com/a", "https://example.com/b"]}'apify — platform scraping catalog
- Price
- ≈
maxItems× per-result rate, 1¢ minimum - Latency
- synchronous, ~10–15s per run
When the target is a specific platform’s structured data — social posts, profiles, comments, followers, marketplace listings, reviews, map places — route to an apify action rather than Firecrawl or Exa. Firecrawl is for arbitrary web pages; Exa is for search; Apify actors return typed rows for a named platform. All actions take an identifier param plus an optional maxItems cap (bounded per action; the cap also sets the price), run synchronously, and return the dataset rows.
| Platform | Action | Identifier param | Notes |
|---|---|---|---|
linkedin-posts | targetUrls | recent posts | |
linkedin-jobs | jobTitles | plus locations | |
linkedin-profile-search | searchQuery | find people | |
| X | tweets | searchTerms | or handles / conversations |
| X | x-followers | user_names | follower lists |
reddit-posts | startUrls | subreddit / post URLs | |
reddit-comments | postUrls | threaded comments | |
| YouTube | youtube-videos | searchQueries | or channel URLs |
| YouTube | youtube-comments | startUrls | video URLs |
instagram-posts | username | recent posts | |
instagram-profile | usernames | profile metadata | |
instagram-hashtag | hashtags | posts by tag | |
| TikTok | tiktok-posts | keywords | or URLs |
| TikTok | tiktok-profile | usernames | profile posts |
| TikTok | tiktok-comments | postURLs | video comments |
| TikTok | tiktok-video | postURLs | video metadata |
| Google Maps | gmaps-places | searchStringsArray | business listings |
| Google Maps | gmaps-reviews | startUrls | place review URLs |
| Google Maps | gmaps-contacts | searchStringsArray | places + emails |
| Amazon | amazon-reviews | productUrls | array of {url} objects |
| Amazon | amazon-product | Params | array of ASIN/URL strings |
facebook-posts | startUrls | page / profile posts | |
facebook-pages | startUrls | business-page data | |
facebook-groups | startUrls | group posts | |
facebook-ads | urls | Meta Ad Library | |
| Jobs | indeed-jobs | position | Indeed listings |
| Company | crunchbase | url | funding data |
| Reviews | booking-reviews | startUrls | Booking.com |
curl -X POST https://vaaya.ai/api/run/apify/tweets \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"searchTerms": ["ai agents"], "maxItems": 25}'Gotchas
- Keep
maxItemssmall — it directly sets both cost and latency. - Feed the right identifier per platform: URLs vs usernames vs search terms — see the table.
- Fewer rows may return than requested; you pay for the requested cap.