Sniff out any page.
Get clean data back.
SnoopScan is a web scraping API for AI agents. One call turns any public page into markdown, links or JSON in your own schema — including the pages that block everything else.
1,500 credits a month on the free plan. No card needed. Or try it right here:
Try it on a competitor pricing page, a product listing, a docs page for your RAG index or a company about page — or one of these:
Every tool. One key.
One key, plain REST and JSON. Pick a tool and the call appears beside it, ready to paste.
import requestsr = requests.post( 'https://api.snoopscan.com/v1/scrape', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://example.com/pricing', 'formats': [ 'markdown', 'links' ], 'onlyMainContent': True },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/scrape', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com/pricing", "formats": [ "markdown", "links" ], "onlyMainContent": true }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/scrape \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com/pricing", "formats": [ "markdown", "links" ], "onlyMainContent": true}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/crawl', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://example.com', 'limit': 100, 'maxDepth': 3, 'includePaths': [ '/blog/*' ] },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/crawl', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com", "limit": 100, "maxDepth": 3, "includePaths": [ "/blog/*" ] }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/crawl \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "limit": 100, "maxDepth": 3, "includePaths": [ "/blog/*" ]}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/map', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://example.com', 'search': 'pricing' },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/map', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com", "search": "pricing" }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/map \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "search": "pricing"}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/search', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'query': 'best headless browsers', 'limit': 5, 'scrapeOptions': { 'formats': [ 'markdown' ] } },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/search', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "query": "best headless browsers", "limit": 5, "scrapeOptions": { "formats": [ "markdown" ] } }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/search \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "query": "best headless browsers", "limit": 5, "scrapeOptions": { "formats": [ "markdown" ] }}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/extract', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'urls': [ 'https://example.com/pricing' ], 'schema': { 'type': 'object', 'properties': { 'plan': { 'type': 'string' }, 'price': { 'type': 'number' } } }, 'prompt': 'The cheapest paid plan' },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/extract', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "urls": [ "https://example.com/pricing" ], "schema": { "type": "object", "properties": { "plan": { "type": "string" }, "price": { "type": "number" } } }, "prompt": "The cheapest paid plan" }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/extract \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "urls": [ "https://example.com/pricing" ], "schema": { "type": "object", "properties": { "plan": { "type": "string" }, "price": { "type": "number" } } }, "prompt": "The cheapest paid plan"}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/batch/scrape', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'urls': [ 'https://example.com/a', 'https://example.com/b' ], 'maxConcurrency': 10, 'scrapeOptions': { 'formats': [ 'markdown' ] } },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/batch/scrape', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "urls": [ "https://example.com/a", "https://example.com/b" ], "maxConcurrency": 10, "scrapeOptions": { "formats": [ "markdown" ] } }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/batch/scrape \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "urls": [ "https://example.com/a", "https://example.com/b" ], "maxConcurrency": 10, "scrapeOptions": { "formats": [ "markdown" ] }}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/products', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://store.example.com', 'limit': 500 },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/products', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://store.example.com", "limit": 500 }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/products \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://store.example.com", "limit": 500}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/posts', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://blog.example.com', 'limit': 200 },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/posts', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://blog.example.com", "limit": 200 }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/posts \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://blog.example.com", "limit": 200}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/monitor', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'name': 'Pricing', 'urls': [ 'https://example.com/pricing' ], 'intervalMinutes': 60, 'webhook': 'https://hooks.example.com/snoop' },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/monitor', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "name": "Pricing", "urls": [ "https://example.com/pricing" ], "intervalMinutes": 60, "webhook": "https://hooks.example.com/snoop" }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/monitor \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "name": "Pricing", "urls": [ "https://example.com/pricing" ], "intervalMinutes": 60, "webhook": "https://hooks.example.com/snoop"}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/places/search', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'query': 'coffee shops', 'location': 'Austin, TX', 'limit': 20, 'enrich': True },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/places/search', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "query": "coffee shops", "location": "Austin, TX", "limit": 20, "enrich": true }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/places/search \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "query": "coffee shops", "location": "Austin, TX", "limit": 20, "enrich": true}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/domain', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'domain': 'https://example.com', 'backlinkLimit': 100 },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/domain', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "domain": "https://example.com", "backlinkLimit": 100 }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/domain \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "domain": "https://example.com", "backlinkLimit": 100}'
import requestsr = requests.post( 'https://api.snoopscan.com/v1/company', headers={'Authorization': 'Bearer sk_YOUR_KEY'}, json={ 'url': 'https://example.com', 'contacts': True },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/company', { method: 'POST', headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com", "contacts": true }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/company \ -H 'Authorization: Bearer sk_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "contacts": true}'
Same shape for every tool. A URL or a list, a few options, and back comes data plus what it cost.
One URL in. Structured data out.
The engine follows the trail a page leaves — its sitemap, its links, its own JSON where it publishes any — and hands back markdown, links or JSON in the shape you asked for. When a page fights back, it climbs the tiers until something reads it.
POST /v1/scrape
{ "url": "https://example.com/pricing", "formats": ["markdown", "links"]}
200 · 1 credit
{ "success": true, "data": { "markdown": "# Pricing\n\nStarter — $12 a month…", "links": ["/signup", "/docs", "/enterprise"], "metadata": { "title": "Pricing", "statusCode": 200 }, "cost": { "credits": 1, "cached": false } }}
Most pages cost one credit. You pay for what worked.
An easy page costs 1 credit. One that needs a proxy costs 2, and one that needs a real browser costs 5. You are billed for what the page actually took, and never for an attempt that failed.
Blocked pages come back. Nothing to configure.
Bot walls, pages that only exist once JavaScript has run, sites that want a particular country: one call, the same shape every time. What each kind of page costs is on the pricing page, and in your activity log after every call.
Most of the web. Fast, and 1 credit.
Sites that turn bots away still answer. Pick the country the request should come from; 12 to choose from.
Pages that draw themselves with JavaScript are read once they have. Interact clicks, types and scrolls first when you ask.
Asked for it lately? Your own comes straight back, free. You set how fresh is fresh enough.
Open the page in the free web unblocker: read it as markdown or browse it in a frame, from the country you pick. No account, no scripts, no cookies.
The rest of the job, already done.
PDFs and documents come back as text and tables, priced by the page.
A JSON schema and up to 100 URLs in, rows out. Reads the page's own structured markup first.
Click, type, scroll and wait in a real browser before the page is read.
Watch a page on a schedule and see what changed, run by run.
Thousands of URLs as one job, with a webhook when it finishes.
Ask again for a page you already fetched and it costs nothing. You set the window per request.
The work people buy this for.
Each one is a few calls in a row. The list on the card is the order they run in, and every step is an endpoint you can call today.
Crawl a whole site into markdown that chunks cleanly, and keep the index fresh as the source moves.
Read a product page for price, stock and variants, on one store or ten thousand, and know the hour a figure moves.
Search the web and read every result in full instead of a snippet, then map a competitor site end to end.
Map a company site, keep the pages that matter, and pull people, roles and contact details into your columns.
Track what a rival publishes, prices and promises, and get told the moment any of it changes.
Property, flights, fares and stock: pages built by JavaScript, read the same way as any other.
Plug it into the tools you already use.
Every tool is also an MCP server: one URL and your key, and your agent can read the web from inside its editor. Anything that speaks plain HTTP gets the same API.
And any other MCP client: a URL and a header is all it takes.
Python, Node, cURL, or anything that can make an HTTP request. Nothing to install.
Crawls and batches call you back when they finish, so nothing polls.
Invite people from Team. Everyone shares the credits, and every request shows who made it.
Every call you made, what came back and what it cost.
Priced by the page. From $0.45 a thousand.
One allowance across every tool. A failed request is never billed, and re-reading your own cached page costs nothing.
The questions people ask first.
What is SnoopScan?+
A web scraping API for AI agents. Give it a URL and it hands back the page as clean markdown, HTML, links or JSON in your own schema. It handles the fetching, the proxies, the browser and the parsing, so you never build or babysit scraping infrastructure.
Who is it for?+
Developers and teams whose agents, research pipelines or products need pages from the web: agent builders, deep research, lead enrichment, price and stock monitoring, RAG and search indexes.
Which sites can it reach?+
Most public pages, including ones that only render with JavaScript and ones that block plain requests. It does not log in as you or keep cookies. Pages behind a login are out of scope for the API too.
Is web scraping legal?+
Scraping public pages is lawful in most places; what you do with the data is governed by the site's terms and by privacy law where you are. Robots rules are a per-request setting you control, and you are responsible for how you use what you collect.
What does a credit buy?+
One page. A plain fetch is 1 credit, a page that needed a proxy is 2, a page that needed a real browser is 5, and re-reading a page you already fetched is free. A page someone else fetched first comes from the shared index for a single credit. The rate card on the pricing page lists every unit, and your activity log shows what each call actually cost.
Am I charged for failed requests?+
No. A request that fails carries no cost and never counts against your balance. You only pay for pages that come back.
What is the difference between scrape and crawl?+
Scrape reads the pages you name. Crawl discovers pages by following links from a start URL, with depth and path filters, and reads each one. Map lists a site's URLs without reading them, which is the cheap way to decide what to crawl.
What formats come back?+
Markdown, HTML, raw HTML, links, a summary, a screenshot, or JSON shaped to a schema you give it. Ask for several in one call. PDFs and Word documents come back as text through Parse.
Does it respect robots.txt?+
Robots handling is a per-request setting, as is how hard it paces a host. Both are yours to set; neither is a promise made on your behalf.
Does it work with my stack?+
It is plain REST and JSON. Python, Node, cURL, or anything that can make an HTTP request. Nothing to install.
Can I extract data with a prompt?+
Yes. Extract takes a JSON schema and, optionally, a prompt, and answers per page with a confidence score. For fixed shapes at volume, a schema alone is more consistent than a prompt.
Do I need an AI key to use Extract?+
No. Extract reads a page's own structured markup and fills your schema from that. For fields the markup cannot answer you can add your own Anthropic or OpenAI key in your account; it runs on your key and costs no credits.
How do long jobs report back?+
Crawls and batches return a job id. Poll it, or give a webhook URL and the job calls you when it finishes.
Can my team share an account?+
Yes. Invite people from Team. Everyone shares the credits and keys, and every request shows who made it.
Is there a free plan?+
Yes. 1,500 credits a month, refreshed on the day you signed up, no card needed. It is a real plan, not a trial that expires.
Do unused credits roll over?+
No. Your free credits refresh monthly on the day you signed up, and your dashboard shows the date. Paid plans refresh on their billing date.
What happens when I run out?+
Requests return a clear 402 with the reason, and nothing is charged. Move up a plan from Billing and the credits land the same day.
Can I switch plans later?+
Yes, from Billing, up or down. The new allowance applies from the switch.
Where does my data go?+
Fetched pages are cached briefly, then dropped. Re-reading your own cached page is free. Nothing you scrape is used for anything else.
What will the web unblocker not do?+
Log in as you, run scripts or keep cookies. It reads and browses public pages only. Sites that need those will not work there, and the API has Interact for that.
Try it on a page you actually need.
Sign up, paste a URL into the Playground, and copy the code it gives you. The first 1,500 credits are on us.
Start for free