SnoopScan
Web scraping API

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.

Paste the setup prompt into your AI chat and your agent does the rest. 1,500 credits a month free, no card. 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:

Tools

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}'
200markdown · links1 credit, 0 from cache
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/*"    ]}'
job idpoll or webhookfrom 1 credit a page
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"}'
url listsitemaps · links1 credit
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"        ]    }}'
resultseach page scraped2 credits
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"}'
jsonin your schemavalidated
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"        ]    }}'
job idone jobfrom 1 credit a page
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}'
catalogup to 250 a page1 credit a page
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}'
postsfull text · dates · authors1 credit a page
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"}'
monitor idwebhook on changebilled per page checked
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}'
placeswebsite · phone · contacts10 credits a search
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}'
registrationdns · backlinks1 credit
import requestsr = requests.post(    'https://api.snoopscan.com/v1/domain/report',    headers={'Authorization': 'Bearer sk_YOUR_KEY'},    json={        'domain': 'example.com',        'depth': 'buyer'    },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/domain/report', {  method: 'POST',  headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' },  body: JSON.stringify({      "domain": "example.com",      "depth": "buyer"  }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/domain/report \  -H 'Authorization: Bearer sk_YOUR_KEY' \  -H 'Content-Type: application/json' \  -d '{    "domain": "example.com",    "depth": "buyer"}'
findings · concernsarchive history · link checksquick 5 · buyer 33 · deep 160 credits
import requestsr = requests.post(    'https://api.snoopscan.com/v1/domain/screen',    headers={'Authorization': 'Bearer sk_YOUR_KEY'},    json={        'domains': [            'example.com',            'example.io',            'example.net'        ],        'dns': True    },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/domain/screen', {  method: 'POST',  headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' },  body: JSON.stringify({      "domains": [          "example.com",          "example.io",          "example.net"      ],      "dns": true  }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/domain/screen \  -H 'Authorization: Bearer sk_YOUR_KEY' \  -H 'Content-Type: application/json' \  -d '{    "domains": [        "example.com",        "example.io",        "example.net"    ],    "dns": true}'
availabilityup to 500 names1 credit a name
import requestsr = requests.post(    'https://api.snoopscan.com/v1/leads',    headers={'Authorization': 'Bearer sk_YOUR_KEY'},    json={        'who': 'roofing contractors',        'where': 'Houston, TX',        'limit': 50    },)print(r.json())
const r = await fetch('https://api.snoopscan.com/v1/leads', {  method: 'POST',  headers: { Authorization: 'Bearer sk_YOUR_KEY', 'Content-Type': 'application/json' },  body: JSON.stringify({      "who": "roofing contractors",      "where": "Houston, TX",      "limit": 50  }),});console.log(await r.json());
curl -X POST https://api.snoopscan.com/v1/leads \  -H 'Authorization: Bearer sk_YOUR_KEY' \  -H 'Content-Type: application/json' \  -d '{    "who": "roofing contractors",    "where": "Houston, TX",    "limit": 50}'
job idphone · website · emails4 credits a lead
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}'
companycontacts · socials3 credits

Same shape for every tool. A URL or a list, a few options, and back comes data plus what it cost.

How it works

One URL in. Structured data out.

Send a URL and get back markdown, links or JSON in the shape you asked for. Pages that block bots or need JavaScript come back the same way, with nothing to configure.

In POST /v1/scrape
{  "url": "https://example.com/pricing",  "formats": ["markdown", "links"]}
Out 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 }  }}
What it costs

Most pages cost one credit. You pay for what worked.

An ordinary page costs 1 credit. Pages that need a premium proxy or a browser cost more, and the pricing page lists every rate. Failed requests are never charged.

Unblocking

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.

Plain pages

Most of the web. Fast, and 1 credit.

Protected pages

Sites that block bots still come back. Pick the country the request should come from; 12 to choose from.

JavaScript pages

Pages that render in the browser come back fully loaded. Interact can click, type and scroll first.

Your cached pages

Ask again for a page you already fetched and it comes back free. You set how fresh it needs to be.

Blocked yourself?

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.

Open the web unblocker ›
More tools

The rest of the job, already done.

Parse

PDFs and documents come back as text and tables, priced by the page.

Extract

A JSON schema and up to 100 URLs in, rows out.

Interact

Click, type, scroll and wait in a real browser before the page is read.

Monitor

Watch a page on a schedule and see what changed, run by run.

Batch

Thousands of URLs as one job, with a webhook when it finishes.

Free re-reads

Ask again for a page you already fetched and it costs nothing. You set the window per request.

Use cases

The work people buy this for.

Common jobs, and the endpoints that do them.

Pages funneling into a robot reading an open book
AI and RAG

Crawl a whole site into markdown that chunks cleanly, and keep the index fresh as the source moves.

Crawl · Batch · Monitor
Crawl the whole site
Main content only, as markdown
A webhook when it finishes
Re-crawl on a schedule
A price tag beside a grid of products
E-commerce and pricing

Read a product page for price, stock and variants, on one store or ten thousand, and know the hour a figure moves.

Products · Monitor · Batch
Price, stock and variants
Shopify and WooCommerce direct
Watch a page on a schedule
A diff of what changed
A search results list with a rank line rising beside it
SEO and search

Search the web and read every result in full instead of a snippet, then map a competitor site end to end.

Search · Map · Scrape
Search, results already read
Every URL on a site
Titles, meta and headings
1 credit for most pages
A business card above a funnel of data points
Lead generation

Map a company site, keep the pages that matter, and pull people, roles and contact details into your columns.

Map · Extract · Batch
Map every URL on the site
Keep the team and contact pages
Names, titles and emails
Batch a thousand sites
Scattered site thumbnails funneling into a single chart
Market and competitor research

Track what a rival publishes, prices and promises, and get told the moment any of it changes.

Monitor · Crawl · Extract
Watch the pages that matter
See what moved, run by run
Extract the facts to a schema
Unchanged pages come from cache
A house outline above a grid of property listing cards
Listings and travel

Property, flights, fares and stock: pages built by JavaScript, read the same way as any other.

Scrape · Batch · Extract
Rendered pages, not shells
Numbers out as numbers
Thousands of URLs at once
Past the wall when it appears
Your stack

Plug it into the tools you already use.

Every tool is also an MCP server. Paste one line into Claude Code, Codex, Cursor, the Claude app or ChatGPT, and your agent connects it for you. Anything that speaks plain HTTP gets the same API.

Prompt Read and follow https://snoopscan.com/agent-onboarding/SKILL.md
Claude ChatGPT Claude Code Cursor VS Code GitHub Copilot Windsurf Cline Roo Code Kilo Code Continue Claude ChatGPT Claude Code Cursor VS Code GitHub Copilot Windsurf Cline Roo Code Kilo Code Continue

And any other MCP client: a URL and a header is all it takes.

Plain REST and JSON

Python, Node, cURL, or anything that can make an HTTP request. Nothing to install.

Webhooks

Crawls and batches call you back when they finish, so nothing polls.

Team keys

Invite people from Team. Everyone shares the credits, and every request shows who made it.

Activity log

Every call you made, what came back and what it cost.

Pricing

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.

FAQ

The questions people ask first.

The basics
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 pages that need JavaScript and pages that block bots. Pages behind a login are not supported.

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. You are responsible for how you use what you collect.

From your AI chat
Does it work in Claude Code, Codex, Cursor or ChatGPT?+

Yes. SnoopScan is an MCP server, so any AI app or agent that supports MCP can use it: Claude Code, Codex, Cursor, VS Code, Windsurf, the Claude app, ChatGPT and more. Paste "Read and follow https://snoopscan.com/agent-onboarding/SKILL.md" into the chat and the agent sets it up. You sign in and press Approve.

Is there an MCP server?+

Yes, at https://api.snoopscan.com/mcp. Its tools scrape, crawl, map, search and extract, list a store's products or a blog's posts, check pages for changes, and look up domains and companies. The Claude app and ChatGPT connect at https://api.snoopscan.com/mcp-oauth by signing in, with no key to copy.

Do I need to install anything?+

No. The MCP server and the API are hosted. When an agent sets SnoopScan up, it runs any command itself; you only sign in and press Approve in your browser.

How it works
What does a credit buy?+

One page. Most pages cost 1 credit. Pages that need a premium proxy or a browser cost more, and re-reading a page you already fetched is free. The rate card on the pricing page lists every unit, and your activity log shows what each call 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.

The API
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.

Billing
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. A move up applies straight away; a move down applies when the period you paid for ends. Billing says which before you confirm.

Can I try it without signing up?+

Yes. An AI agent can call scrape, search and parse with no key at all, over the MCP server or the API: 20 free successful calls a day, on plain pages. Every answer says how many are left, and a blocked page does not use one up. Pages that need a browser, and anything past 20 a day, need the free account.

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.

Try it on a page you actually need.

Paste the setup prompt into your AI chat, or try a URL in the Playground and copy the code it gives you. The first 1,500 credits are on us.

Start for free