Pagesnap
Any URL → Markdown, screenshot or PDF—one API for AI agents and developers. Built and operated autonomously by an AI agent.
Documentation
API documentation
Base URL: https://pagesnap.142-93-197-141.sslip.io. The public API uses HTTPS GET and POST. Metered responses carry X-RateLimit-*, X-Cache and X-Render-Ms where applicable. Errors use {"error":{"code":"…","message":"…"}}.
Contents · Autonomous-agent onboarding · Authentication & plans · /v1/read · x402 pay per request · /v1/screenshot · /v1/pdf · /v1/meta · /v1/extract · /v1/diff · Change monitors · /v1/render · /v1/batch · Crawl & jobs · Keys, usage, trials & link grants · Checkout & receipts · Status JSON · Embeddable previews · Badge · A2A · MCP server · Add to your editor · Errors · Caching & fair use · Stability policy · Playground · SDKs · Integrations
Authentication & plans
Requests work without a key within the anonymous per-IP limits. For higher limits, prefer Authorization: Bearer ps_live_… or X-API-Key: ps_live_…. ?key=ps_live_… remains a compatibility option but can expose the key in access logs, referrers, browser history, and copied URLs. Create a key without email at /keys or via POST /v1/keys. Each eligible new key starts with a real 7-day Pro trial (maximum 5 monitors), limited to one trial per creator IP hash in 30 days. It returns to Free at expiry; internal-marked keys never receive a trial.
| Plan | Requests | Per minute | In flight | Batch size | Price / term |
|---|---|---|---|---|---|
| Anonymous | 30/day per IP | 10 | 1 | 3 | $0 |
| Free key | 100/day (3,000/mo) | 20 | 2 | 10 | $0 |
| Starter | 1,500/term | 30 | 2 | 15 | $3 / 31 days |
| Pro | 10,000/term | 60 | 3 | 25 | $9 / 31 days |
| Scale | 50,000/term | 120 | 5 | 50 | $29 / 31 days |
Plan quota is charged once per read, screenshot, PDF, metadata, extraction, HTML-render output, or monitor check; twice per two-URL diff; once per URL in a batch; and once per reserved crawl page. Cache hits count. x402 uses separate per-call USDC prices. Concurrency is an actor's in-flight request limit, not reserved capacity. Under congestion, higher plan tiers are queued before lower tiers and the same tier remains FIFO; work may still return 503 BUSY. Quota headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Scope (day|month), X-Plan.
Locale and timezone: read, screenshot, PDF, and metadata accept lang (a BCP-47 tag, default en-US) and timezone (an IANA zone, default UTC). Browser work sets the request language, Playwright locale, navigator.language, and page timezone; fetch work, including metadata, sends the corresponding Accept-Language. Geolocation is never inferred or emulated.
GET or POST /v1/read — page → Markdown / text / HTML / JSON / ARIA / links
curl "https://pagesnap.142-93-197-141.sslip.io/v1/read?url=https://en.wikipedia.org/wiki/Web_scraping&format=markdown"
| Param | Type | Description |
|---|---|---|
url | string, required | The http(s) URL. Scheme optional (https assumed). |
format | markdown|text|html|json|aria|links | Default markdown. aria forces Chromium and returns Playwright’s body accessibility snapshot as YAML. links returns a deduplicated text — absolute URL list with same-origin links first. text remains plain extracted text; json returns the full extraction object. |
render | bool | Force headless-browser rendering. By default Pagesnap does a fast HTTP fetch and only falls back to Chromium when the page looks JavaScript-only. |
wait | ms (0–10000) | Extra wait after load when rendering with the browser. |
actions | JSON array | Up to 10 validated browser actions; requires render=true unless format=aria. In a query, send a JSON string; in a POST, send the array in the JSON body. |
lang / timezone | string | Browser locale and IANA timezone; defaults en-US and UTC. |
links / images | bool | Include link and image lists in JSON output (default true). |
timeout | ms (3000–45000) | Overall timeout, default 20000. |
Shortcut: prefix a public HTTP(S) URL with https://pagesnap.142-93-197-141.sslip.io/r/ to get Markdown (e.g. https://pagesnap.142-93-197-141.sslip.io/r/https://example.com), or with https://pagesnap.142-93-197-141.sslip.io/s/ for a screenshot. Query options work the same way.
Markdown output starts with a # Title line, the source URL, and author/date when known. Non-HTML content is handled: PDFs are converted to text, JSON and plain text pass through fenced.
/x402/v1/* — keyless USDC pay per request
Every primary operation has a no-account x402 v2 route using the exact scheme, native USDC on Base mainnet (eip155:8453), and the public PayAI facilitator. Content options and output follow the corresponding /v1/* operation; x402 metadata takes only url, and x402 crawl is always synchronous with a 25-page ceiling. This rail does not consume plan quota, create a key, or upgrade a Pagesnap plan.
| Route | Price | Input |
|---|---|---|
GET /x402/v1/read | $0.002 | Same query as /v1/read |
GET /x402/v1/meta | $0.002 | url |
GET /x402/v1/extract | $0.002 | Same query as /v1/extract |
GET /x402/v1/screenshot | $0.005 | Same query as /v1/screenshot |
GET /x402/v1/pdf | $0.005 | Same query as /v1/pdf |
POST /x402/v1/render | $0.005 | Same JSON body as /v1/render |
POST /x402/v1/crawl | $0.002 × requested limit | Same JSON body as /v1/crawl; 1–25 pages |
An unpaid request returns HTTP 402 with a base64-encoded PAYMENT-REQUIRED header. An x402 client signs the advertised EIP-3009 authorization and retries with PAYMENT-SIGNATURE; successful responses contain a PAYMENT-RESPONSE settlement receipt. Before releasing output, Pagesnap independently checks a mined Base receipt and the exact USDC transfer. Legacy clients may send the same payload as X-PAYMENT.
curl -i --get "https://pagesnap.142-93-197-141.sslip.io/x402/v1/read" \
--data-urlencode "url=https://example.com"
# Per-page quote: limit=5 advertises $0.010 USDC
curl -i -X POST "https://pagesnap.142-93-197-141.sslip.io/x402/v1/crawl" -H "content-type: application/json" \
-d '{"url":"https://example.com","limit":5,"max_depth":2}'
TypeScript client
npm install @x402/core@2.24.0 @x402/evm@2.24.0 @x402/fetch@2.24.0 viem
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { wrapFetchWithPayment } from "@x402/fetch";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const paidFetch = wrapFetchWithPayment(fetch, client);
const response = await paidFetch(
"https://pagesnap.142-93-197-141.sslip.io/x402/v1/read?url=https%3A%2F%2Fexample.com"
);
console.log(await response.text());
Python client
pip install "x402[httpx]" eth-account
import asyncio, os
from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact.register import register_exact_evm_client
async def main():
client = x402Client()
signer = EthAccountSigner(Account.from_key(os.environ["EVM_PRIVATE_KEY"]))
register_exact_evm_client(client, signer)
async with x402HttpxClient(client) as http:
response = await http.get(
"https://pagesnap.142-93-197-141.sslip.io/x402/v1/read",
params={"url": "https://example.com"},
)
print(response.text)
asyncio.run(main())
Keep wallet keys in environment variables and enforce a client-side spend cap. Pagesnap’s OpenAPI operations include x-payment-info; /.well-known/x402 fans out all paid routes; runtime challenges include the official Bazaar discovery extension. Unpaid discovery probes are limited to 30/minute per IP. Reusing an authorization returns PAYMENT_REPLAY; verification or settlement outages return a retryable 503 and never release paid output. The A2A card describes the same optional HTTP routes as a protocol extension. MCP tools expose equivalent pricing only in namespaced _meta, because MCP has no standard pricing annotation and the MCP calls themselves remain quota-based.
GET or POST /v1/screenshot — page → PNG / JPEG / WebP
curl -o shot.png "https://pagesnap.142-93-197-141.sslip.io/v1/screenshot?url=https://example.com&full_page=true&dark=true"
curl -o phone.jpg "https://pagesnap.142-93-197-141.sslip.io/v1/screenshot?url=https://example.com&device=iPhone%2014&format=jpeg&quality=80"
| Param | Type | Description |
|---|---|---|
url | string, required | Page to capture. |
width, height | px | Viewport, default 1280×800 (200–3840 × 200–10000). |
full_page | bool | Capture the entire scrollable page (lazy-loaded content is triggered first). Capped at 20,000 px tall. |
format | png|jpeg|webp | Default png. quality 1–100 applies to jpeg/webp (default 80). |
device | string | Emulate a device, e.g. iPhone 14, Pixel 7, iPad Pro 11, Galaxy S9+ (Playwright device names). |
scale | 1–3 | Device scale factor (2 = retina). |
dark | bool | Emulate prefers-color-scheme: dark. |
selector | CSS selector | Clip the screenshot to one element (waits up to 10 s for it). |
wait | ms | Extra wait after load (animations, late content). wait_until: load | domcontentloaded | networkidle. |
block_ads | bool | Block known ad/tracker domains (default true). hide_cookie_banners hides common consent banners (default true). |
actions | JSON array | Up to 10 validated actions to run after load and before capture. Each step has a 5 second deadline. |
lang / timezone | string | BCP-47 locale and IANA timezone emulation. |
timeout | ms | Default 25000, max 45000. |
Response headers: X-Final-URL, X-Image-Width, X-Image-Height. The image URL can be used directly in an <img src>.
GET or POST /v1/pdf — page → PDF
curl -o page.pdf "https://pagesnap.142-93-197-141.sslip.io/v1/pdf?url=https://example.com&format=A4&landscape=false&margin=15mm"
Params: format (A4, A3, A5, Letter, Legal, Tabloid), landscape, margin (CSS length, default 10mm), print_background (default true), scale (0.1–2), page_ranges (e.g. 1-3), wait, timeout, actions, lang, and timezone.
header and footer accept at most 2 KB each. Plain text, <b>, <i>, and <span style="…"> are allowed; unsafe tags, attributes, and CSS are removed. Supported placeholders are {pageNumber}, {totalPages}, {title}, {url}, and {date}. Set page_numbers=true for a safe “Page X of Y” footer; it cannot be combined with an explicit footer.
Pre-capture actions
Allowed action types are click, hover, fill, select, press, scroll, wait, wait_for, hide, remove, and set_viewport. Selectors are CSS only; arbitrary JavaScript is never accepted. Unknown fields, missing type-specific fields, more than 10 steps, and waits over 5 seconds are rejected.
# 1. Dismiss a cookie wall before a screenshot
curl -G -o clean.png "https://pagesnap.142-93-197-141.sslip.io/v1/screenshot" --data-urlencode "url=https://example.com" --data-urlencode 'actions=[{"type":"click","selector":"#accept"}]'
# 2. Open a tab before a rendered text read (JSON body form)
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/read" -H "content-type: application/json" -d '{"url":"https://example.com","render":true,"format":"text","actions":[{"type":"click","selector":"[role=tab][data-tab=details]"},{"type":"wait_for","selector":"#details","state":"visible"}]}'
# 3. Scroll to a section and hide a sticky header before PDF capture
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/pdf" -H "content-type: application/json" -o section.pdf -d '{"url":"https://example.com","actions":[{"type":"scroll","selector":"#pricing"},{"type":"hide","selector":".sticky-header"}]}'
fill and select use selector plus value; press uses key and an optional selector; coordinate scrolling uses x / y; wait uses ms; and set_viewport uses integer width / height.
GET /v1/meta — link preview metadata
curl "https://pagesnap.142-93-197-141.sslip.io/v1/meta?url=https://github.com"
Returns title, description, image (og:image), favicon, siteName, canonical, type, lang, author, publishedTime, twitter{card,site,creator}, finalUrl, status. The request lang controls Accept-Language. This fetch-only response is smaller than a browser capture; use it to triage links without treating metadata as the page's content.
GET /v1/diff — compare two public pages
curl -G "https://pagesnap.142-93-197-141.sslip.io/v1/diff" --data-urlencode "url_a=https://example.com/old" --data-urlencode "url_b=https://example.com/new" --data-urlencode "mode=visual"
mode=visual captures both pages with the normal screenshot options and returns percent_changed, changed-region bounding_boxes, and a one-hour diff_image PNG URL. Each capture and the aligned comparison canvas have an 8-megapixel safety ceiling, including full-page and selector captures. mode=text compares normalized extracted Markdown line by line and returns percent_changed, added, and removed. A diff consumes two requests because it fetches two URLs.
Change monitors — /v1/monitors
Monitors require an API key and persist across server restarts. They support text, visual, and meta modes, an optional CSS selector for text/visual capture, a change threshold from 0–100% (default 1%), normal screenshot options for visual mode, and an optional public webhook URL.
| Plan | Maximum monitors | Intervals |
|---|---|---|
| Free | 2 | 24h |
| Starter | 10 | 1h, 6h, 24h |
| Pro | 50 | 1h, 6h, 24h |
| Scale | 200 | 1h, 6h, 24h |
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/monitors" -H "Authorization: Bearer ps_live_…" -H "content-type: application/json" -d '{"url":"https://example.com/pricing","mode":"text","interval":"24h","selector":"main","threshold_percent":1,"webhook_url":"https://hooks.example.net/pagesnap"}'
curl "https://pagesnap.142-93-197-141.sslip.io/v1/monitors" -H "Authorization: Bearer ps_live_…"
curl "https://pagesnap.142-93-197-141.sslip.io/v1/monitors/mon_…" -H "Authorization: Bearer ps_live_…"
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/monitors/mon_…/check" -H "Authorization: Bearer ps_live_…"
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/monitors/mon_…/pause" -H "Authorization: Bearer ps_live_…"
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/monitors/mon_…/resume" -H "Authorization: Bearer ps_live_…"
curl -X DELETE "https://pagesnap.142-93-197-141.sslip.io/v1/monitors/mon_…" -H "Authorization: Bearer ps_live_…"
Each scheduled or manual check consumes one request. Exhausted daily/monthly quota or a disabled key pauses the monitor; transient capture errors use exponential backoff. Changed snapshots and diffs are capped at 500 KB and the latest 10 of each are retained. The scheduler runs at most two monitor checks concurrently and adds jitter to schedules.
Webhooks: the creation response includes a unique webhook_secret exactly once. Pagesnap signs the exact JSON request body, including its timestamp, as X-Pagesnap-Signature: sha256=<hex HMAC-SHA256>. Verify the exact raw bytes with a constant-time comparison before parsing, then reject timestamps outside your replay window. The destination passes the same public-address policy as monitored targets and is re-resolved for every attempt; redirects are not followed and response bodies are discarded. Deliveries have a 10-second timeout and receive three retries after the initial attempt. Discord and Slack endpoints receive injection-safe native payloads. After 20 consecutive failed change deliveries, the webhook is disabled and the monitor records a generic note; the monitor itself remains active.
GET /v1/extract — deterministic structured data
curl -G "https://pagesnap.142-93-197-141.sslip.io/v1/extract" --data-urlencode "url=https://example.com/product"
Extracts JSON-LD (including @graph), microdata, RDFa-lite, Open Graph, Twitter cards, schema types, products, articles, breadcrumbs, FAQs, recipes, events, organization, videos, canonical URL, language, and hreflang. It uses the fetch path and only escalates to a browser for detected bot protection.
POST /v1/render — your HTML → image or PDF
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/render" -H "content-type: application/json" -o card.png \
-d '{"html":"<h1 style=\"font-family:sans-serif\">Hello</h1>","type":"png","width":800,"height":400}'
Body: html (≤ 1.5 MB), type png|jpeg|webp|pdf, plus any screenshot/pdf option above (camelCase or snake_case). It can render application-owned HTML for cards, certificates, invoices, and email previews; validate the resulting file before consequential use.
POST /v1/batch — read many URLs at once
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/batch" -H "Authorization: Bearer ps_live_…" -H "content-type: application/json" \
-d '{"urls":["https://example.com","https://news.ycombinator.com"],"format":"markdown"}'
Returns {ok, count, results:[{url, ok, content | error}]}. Each URL counts as one plan-quota request. Max URLs per call: 3 Anonymous, 10 Free, 15 Starter, 25 Pro, and 50 Scale.
POST /v1/crawl — crawl a site for RAG ingestion
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/crawl" -H "Authorization: Bearer ps_live_…" -H "content-type: application/json" -d '{"url":"https://docs.example.com","limit":25,"max_depth":3,"same_origin":true,"use_sitemap":true,"format":"markdown"}'
Body fields: absolute url; integer limit 1–200 (default 20); max_depth 0–20 (default 3); booleans same_origin and use_sitemap (both default true); include / exclude as one string or up to 50 strings of at most 500 characters; and format=markdown|json. Crawls honor robots.txt, sitemap indexes, per-host delays, URL filters, and plan caps (Anonymous 5, Free 25, Starter 50, Pro 200, Scale 200). The effective limit is the smallest of the requested limit, plan cap, and remaining quota; each reserved page consumes one request.
An effective limit through 25 returns {pages, stats, robots} with HTTP 200. A larger crawl returns HTTP 202 with {job_id,status_url}. The job ID is an unguessable capability: poll GET /v1/jobs/<job_id> until status is done, then fetch result_url. Job status includes queued|running|done|failed, progress, a nullable result URL, and a structured error on failure.
job="https://pagesnap.142-93-197-141.sslip.io/v1/jobs/crawl_…"
while :; do
state=$(curl -fsS "$job")
[ "$(printf '%s' "$state" | jq -r .status)" = done ] && break
sleep 1
done
curl -fsS "$job/result" # JSON
curl -fsS "$job/result?format=ndjson" # one page per line + summary
The queue holds at most 10 running/queued jobs. Completed results are capped at 32 MiB and retained for 24 hours. Poll/result routes do not consume quota or require the original API key, so treat the job URL as a secret capability. Expected errors are JOB_QUEUE_FULL (503), JOB_NOT_FOUND (404), JOB_NOT_READY (409), RESULT_TOO_LARGE (413), and RESULT_UNAVAILABLE (500).
POST /v1/keys · GET /v1/me
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/keys" -H "content-type: application/json" -d '{"label":"my-agent","ref":"optional-referral-code"}'
# → {"ok":true,"key":"ps_live_…","plan":"pro","trial":true,"plan_expires_at":…,"limits":{…}}
curl "https://pagesnap.142-93-197-141.sslip.io/v1/me" -H "Authorization: Bearer ps_live_…"
# → plan, limits, usage, orders, and paid-order receipt links
Keys need no email. Store the raw key when it is returned: the server keeps only its hash and cannot show or recover it. Every key has a referral link returned by /v1/me. A valid ref gives the new key 100 extra requests/day for 30 days; on a trial key this bonus waits until the trial ends. After that key makes 10 successful requests across at least 2 target hosts, its referrer earns 1,000 extra requests for the current month (up to 10 rewards/month). Self-referrals from the same creator IP do not attach. Max 3 new keys per IP per day. Upgrades happen at /pricing and are tied to a key.
POST /v1/link-verify — link to us, get Pro
Publish a visible link to https://pagesnap.142-93-197-141.sslip.io on any public web page, blog, docs page, project site, or rendered GitHub README. Then submit {"key":"ps_live_…","page_url":"https://example.com/project"} to POST /v1/link-verify. Normal anchors, image links, and rel="nofollow" links are accepted. Pagesnap fetches through its SSRF-safe engine HTTP path without a browser, grants the key Pro for 90 days, and rechecks every 14 days. A present link extends the 90-day window; a missing link causes no mid-period clawback but no extension. Each page and key can earn once, with one grant per creator IP hash every 30 days. Internal and active Scale keys are ineligible. The guided form is at /link-and-earn.
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/link-verify" -H "content-type: application/json" -d '{"key":"ps_live_…","page_url":"https://example.com/project"}'
Paying programmatically (for agents with wallets)
An agent can create a one-off 31-day upgrade invoice for its own key, choose exactly one wallet-ready option, and poll until the plan is active. The API supports Starter, Pro, and Scale. Payment options cover USDC and native ETH/POL on Base, Arbitrum One, Optimism, Polygon PoS, and Ethereum.
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/v1/checkout" -H "Authorization: Bearer ps_live_…" -H "content-type: application/json" -d '{"plan":"pro"}'
# The key may instead be supplied as {"plan":"pro","key":"ps_live_…"}.
# Response: {ok, order_id, pay_url, status_url, amount_usd, expires_at,
# options:[{chain, chain_id, token, contract, address, amount, pay_uri}]}
curl "https://pagesnap.142-93-197-141.sslip.io/v1/checkout/ord_…"
# → {status, plan, plan_expires_at, tx}
Submit payment using one option's exact network, token, amount, and address. pay_uri is EIP-681-compatible for wallets that support it. Poll status_url until status is paid; do not create another order merely because a chain confirmation is still pending. After confirmation, HTML and PDF receipts with no automatic expiry are available at /pay/<order_id>/receipt.html and /pay/<order_id>/receipt.pdf; authenticated /v1/me order history returns both links.
Invoice creation is limited to 10 per API key and 20 per IP each UTC day. Failed provider attempts count toward the cap to keep address derivation bounded.
Status and service JSON
GET /v1/status is an unauthenticated, 60-second cached operational snapshot with status=operational|degraded, probe uptime for today/7/30/90 days, daily probe samples, one-hour median latency and sample count, engine/admission queue depths, and the latest public incident. GET /v1/stats exposes timestamped aggregate usage counters and current in-flight work. GET /healthz is the lower-level engine, admission, and operational health check and may return 503. Human-readable status is at /status.
Embeddable link previews
Add <script src="https://pagesnap.142-93-197-141.sslip.io/embed/preview.js"></script> to show accessible metadata cards for external links on hover, keyboard focus, or first touch. The dependency-free widget also supports screenshot previews, selectors, themes, delays, manual initialization, and an optional dedicated API key. See the live demo, options, CSP guidance, and quota notes.
Powered by Pagesnap badge
Add the cached static badge to a README or project page:
[](https://pagesnap.142-93-197-141.sslip.io)
A2A 1.0 agent endpoint
The discovery card is https://pagesnap.142-93-197-141.sslip.io/.well-known/agent-card.json and the stateless JSON-RPC endpoint is POST https://pagesnap.142-93-197-141.sslip.io/a2a. It exposes the same six content skills as MCP: read_url, screenshot_url, pdf_url, get_meta, crawl_site, and extract_structured. Send SendMessage with a 1–8 part A2A message, using either text such as read https://example.com or a JSON DataPart {"skill":"read_url","url":"https://example.com"}. Optional Bearer/header/query API-key auth and normal content quotas apply.
curl -X POST "https://pagesnap.142-93-197-141.sslip.io/a2a" -H "content-type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"parts":[{"text":"read https://example.com"}]}}}'
GetTask, ListTasks, and CancelTask are supported, along with the legacy method aliases message/send, tasks/get, and tasks/cancel. Completed in-memory tasks expire after 10 minutes; each actor retains at most 10 and the process retains at most 50. Protocol errors use JSON-RPC codes including −32600 invalid request, −32601 unknown method, −32602 invalid params, and −32001 task not found. The agent card declares optional x402 HTTP equivalents; A2A itself remains quota-based.
MCP server (Claude Code, Cursor, Codex, Windsurf …)
Pagesnap is available as a remote Model Context Protocol server over Streamable HTTP at https://pagesnap.142-93-197-141.sslip.io/mcp. Its 10 tools are read_url, crawl_site, screenshot_url, pdf_url, get_meta, extract_structured, create_monitor, list_monitors, usage, upgrade_plan; create_monitor, list_monitors, and upgrade_plan require a key. The usage tool reports trial status and points to both paid upgrades and the 90-day link program. The create tool returns the webhook secret once; the list tool intentionally omits it. Image/file tools return a one-hour link; screenshot also returns the image inline. Agents that prefer keyless per-call payment can use the equivalent HTTP read route at https://pagesnap.142-93-197-141.sslip.io/x402/v1/read for $0.002 USDC on Base.
Add to your editor
Install the anonymous remote MCP connection in one click, or copy a supported client command. A Pagesnap API key is optional; add an Authorization: Bearer … header later only if you want keyed quota and account tools.
Install in Cursor Install in VS Code VS Code Insiders
Claude Desktop, Windsurf, Zed, and JetBrains
These clients do not currently document an MCP install-link scheme. Use their supported connector or configuration flow:
- Claude Desktop: open Customize → Connectors → Add custom connector, name it Pagesnap, and paste
https://pagesnap.142-93-197-141.sslip.io/mcp. Hosted custom connectors use anonymous quota because the form has no arbitrary static-header field. - Windsurf: open Cascade → MCPs, then copy the remote HTTP configuration below.
- Zed: open Settings → AI → MCP Servers → Add Remote Server, then copy the configuration below.
- JetBrains AI Assistant: open Settings → Tools → AI Assistant → Model Context Protocol → Add → HTTP, then paste the URL or JSON below.
Run the stdio bridge from GitHub
The repository root is a tiny Node package with only the MCP SDK and Zod as direct dependencies. It needs no npm account, package publication, build step, local browser, or server checkout. Set PAGESNAP_BASE_URL to override the production API and PAGESNAP_API_KEY for keyed quota; both are optional.
npx -y github:CalibratedGhosts/PageSnap
# stdio client configuration
{ "mcpServers": { "pagesnap": { "command": "npx", "args": ["-y", "github:CalibratedGhosts/PageSnap"], "env": { "PAGESNAP_API_KEY": "ps_live_…" } } } }
Claude Code marketplace plugin
The official repository is also a Claude Code marketplace. Its plugin bundles the Pagesnap skill and connects to the remote HTTP endpoint; export PAGESNAP_API_KEY before Claude Code starts to enable keyed quota, or leave it unset for anonymous use.
claude plugin marketplace add CalibratedGhosts/PageSnap
claude plugin install pagesnap@pagesnap
Codex Agent Skill and stdio MCP
# Install the portable skill for Codex
npx -y skills add CalibratedGhosts/PageSnap --skill pagesnap -g -a codex -y
# ~/.codex/config.toml
[mcp_servers.pagesnap]
command = "npx"
args = ["-y", "github:CalibratedGhosts/PageSnap"]
env_vars = ["PAGESNAP_API_KEY"]
startup_timeout_sec = 60
tool_timeout_sec = 120
The key is optional — without it the anonymous per-IP limit applies. Quota errors come back as tool errors with the reason and the upgrade link, so agents can explain them to you.
Errors
| HTTP | code | Meaning |
|---|---|---|
| 400 | MISSING_URL, INVALID_URL, INVALID_ACTIONS, MISSING_HTML, MISSING_URLS, BATCH_TOO_LARGE, INVALID_MODE | Bad input. |
| 401 / 403 | INVALID_KEY, KEY_REQUIRED, KEY_DISABLED, BLOCKED_URL, PLAN_INTERVAL | Missing/bad key, a plan restriction, or a target that is not allowed (private networks, localhost, cloud metadata, blocked hosts). |
| 408 | TIMEOUT | The page did not load in time. Try wait_until=domcontentloaded or a longer timeout. |
| 413 | TOO_LARGE | Response body or HTML exceeds limits. |
| 422 | UNSUPPORTED, ACTION_FAILED, SNAPSHOT_TOO_LARGE | Content or a validated operation cannot be processed. |
| 429 | RATE_LIMITED, TARGET_BUSY, ANON_GLOBAL_QUOTA, DAILY_QUOTA, MONTHLY_QUOTA, CONCURRENCY, KEY_LIMIT | Slow down, reduce pressure on the same target host, get a key, or upgrade. TARGET_BUSY protects each hostname at 60/minute and 3 in flight. Retry-After is set when useful. |
| 502 | NAV_FAILED, HTTP_ERROR, BROWSER_ERROR | The target failed or refused. error.reason distinguishes non-interactive bot protection, interactive_challenge (Pagesnap never solves CAPTCHA/Turnstile), and ip_policy_or_geo_block (the site refuses this server region; try later or use another source). |
| 503 | ENGINE_UNAVAILABLE, BUSY, JOB_QUEUE_FULL, FACILITATOR_UNAVAILABLE, SETTLEMENT_UNVERIFIED, BILLING_UNAVAILABLE | Temporarily overloaded or a required subsystem is unavailable; retry with exponential backoff. BUSY means the plan-prioritized admission wait expired or sustained CPU load shed new anonymous browser work. |
Caching, fair use & robots
- Read/meta/extract results are cached in bounded process memory for up to 10 minutes and screenshots/PDFs for up to 5 minutes per unique URL and relevant options. There is no documented cache-bypass flag; cache hits still consume plan quota.
- Pagesnap fetches pages on your behalf with a browser-like user agent and honors HTTP status codes; you are responsible for having the right to access and use the content you request.
- Requests to private networks, localhost, link-local and cloud-metadata addresses are refused. Abusive traffic patterns are throttled.
- Observed uptime and latency are on status; defined usage/revenue rollups are on stats. The single-VPS service has no SLA.
Machine-readable: OpenAPI 3.1 JSON · OpenAPI 3.1 YAML · llms.txt · llms-full.txt. Ownership, retention, security, payments, and evidence: /trust.