SnapAPI Documentation
SnapAPI is a developer-first API for capturing screenshots, generating PDFs, scraping web data, extracting content for LLMs, and recording videos. Built on Playwright with residential proxies, ad blocking, and cookie banner dismissal out of the box.
What can you build?
- Screenshot capture -- URL, HTML, or Markdown to PNG, JPEG, WebP, or AVIF
- PDF generation -- URL or HTML to PDF with full page-size and margin control
- Web scraping -- Extract text, HTML, or links from any page with proxy support
- Content extraction -- Clean markdown for LLM pipelines, article parsing, metadata
- Video recording -- Record MP4, WebM, or GIF with auto-scroll
- AI analysis -- Feed page content to OpenAI or Anthropic with your own key (BYOK)
- Batch processing -- Screenshot up to 100 URLs in a single request
Authentication
All API requests require authentication. SnapAPI supports three methods:
1. X-Api-Key header (recommended)
X-Api-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2. Authorization header
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3. Query parameter
https://api.snapapi.pics/v1/screenshot?access_key=sk_live_xxx&url=https://example.com
API keys are prefixed with sk_live_ and can be created, rotated, and revoked from the Dashboard. Each account supports up to 10 API keys with independent permissions.
Quickstart
Capture your first screenshot in under a minute.
curl -X POST https://api.snapapi.pics/v1/screenshot \ -H "X-Api-Key: sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "format": "png"}' \ -o screenshot.png
const response = await fetch('https://api.snapapi.pics/v1/screenshot', { method: 'POST', headers: { 'X-Api-Key': 'sk_live_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://example.com', format: 'png', }), }); const buffer = await response.arrayBuffer(); // Save or process the screenshot
import httpx response = httpx.post( "https://api.snapapi.pics/v1/screenshot", headers={"X-Api-Key": "sk_live_YOUR_API_KEY"}, json={"url": "https://example.com", "format": "png"}, ) with open("screenshot.png", "wb") as f: f.write(response.content)
package main import ( "bytes" "encoding/json" "io" "net/http" "os" ) func main() { body, _ := json.Marshal(map[string]any{ "url": "https://example.com", "format": "png", }) req, _ := http.NewRequest("POST", "https://api.snapapi.pics/v1/screenshot", bytes.NewReader(body)) req.Header.Set("X-Api-Key", "sk_live_YOUR_API_KEY") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() file, _ := os.Create("screenshot.png") io.Copy(file, resp.Body) }
<?php $ch = curl_init('https://api.snapapi.pics/v1/screenshot'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Api-Key: sk_live_YOUR_API_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'url' => 'https://example.com', 'format' => 'png', ]), ]); $image = curl_exec($ch); file_put_contents('screenshot.png', $image);
import Foundation let url = URL(string: "https://api.snapapi.pics/v1/screenshot")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("sk_live_YOUR_API_KEY", forHTTPHeaderField: "X-Api-Key") request.setValue("application/json", forHTTPHeaderField: "Content-Type") let body: [String: Any] = ["url": "https://example.com", "format": "png"] request.httpBody = try JSONSerialization.data(withJSONObject: body) let (data, _) = try await URLSession.shared.data(for: request) try data.write(to: URL(fileURLWithPath: "screenshot.png"))
val client = HttpClient(CIO) { install(ContentNegotiation) { json() } } val response = client.post("https://api.snapapi.pics/v1/screenshot") { header("X-Api-Key", "sk_live_YOUR_API_KEY") contentType(ContentType.Application.Json) setBody(mapOf("url" to "https://example.com", "format" to "png")) } File("screenshot.png").writeBytes(response.readBytes())
Base URL
All API endpoints are served from a single base URL:
https://api.snapapi.pics/v1
All requests must use HTTPS. HTTP requests will be rejected.
GET /v1/capabilities to see all available features, supported formats, and plan-specific limits for your account.
Screenshot
Capture a screenshot of a web page, raw HTML, or Markdown. Returns the image as binary data by default, or as base64/JSON with metadata.
Content source (one required)
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | Target URL to capture. Must include protocol (https://). | |
| html | string | Raw HTML to render and capture. Max 5 MB. | |
| markdown | string | Markdown to render with styled template. Max 1 MB. |
url, html, or markdown is required.
If you provide markdown, it is automatically converted to styled HTML before rendering.
Output format
| Parameter | Type | Default | Description |
|---|---|---|---|
| format | string | png | Output format: png, jpeg, webp, avif, or pdf. |
| quality | integer | 80 | Image quality 1-100. Applies to JPEG, WebP, and AVIF only. |
| responseType | string | binary | binary returns raw image data. base64 returns base64-encoded string. json returns JSON with metadata and base64 data. |
| includeMetadata | boolean | false | Include page title, fonts, colors, and links in JSON response. |
Viewport
| Parameter | Type | Default | Description |
|---|---|---|---|
| device | string | Device preset (e.g. iphone-15-pro, macbook-pro-16). Overrides width/height. See device list. | |
| width | integer | 1280 | Viewport width in pixels. Range: 100-3840. |
| height | integer | 800 | Viewport height in pixels. Range: 100-2160. |
| deviceScaleFactor | number | 1 | Device pixel ratio (1-3). Use 2 for retina-quality images. |
| retina | boolean | false | Shortcut for deviceScaleFactor: 2. |
| isMobile | boolean | false | Emulate mobile browser viewport. |
| hasTouch | boolean | false | Emulate touch support. |
| isLandscape | boolean | false | Landscape orientation. |
Full page & element selection
| Parameter | Type | Default | Description |
|---|---|---|---|
| fullPage | boolean | false | Capture the full scrollable page height. |
| fullPageScrollDelay | integer | 400 | Delay (ms) between scroll steps for lazy-loaded content. |
| fullPageMaxHeight | integer | Maximum height for full-page captures (100-50000 px). | |
| scrollToBottom | boolean | false | Scroll to bottom before capturing (triggers lazy loads). |
| selector | string | CSS selector -- capture only that element. | |
| clipX, clipY, clipWidth, clipHeight | integer | Clip a rectangular region from the viewport. |
Timing
| Parameter | Type | Default | Description |
|---|---|---|---|
| delay | integer | 0 | Wait (ms) after page load before capturing. Range: 0-30000. |
| timeout | integer | 30000 | Navigation timeout (ms). Range: 1000-60000. |
| waitUntil | string | load | load, domcontentloaded, or networkidle. Use networkidle for SPAs. |
| waitForSelector | string | Wait for this CSS selector to appear before capturing. | |
| waitForSelectorTimeout | integer | 10000 | Timeout for waitForSelector (ms). |
Customization
| Parameter | Type | Default | Description |
|---|---|---|---|
| darkMode | boolean | false | Enable prefers-color-scheme: dark. |
| reducedMotion | boolean | false | Enable prefers-reduced-motion. |
| css | string | Custom CSS to inject before capture. Max 100 KB. Starter+ | |
| javascript | string | Custom JavaScript to execute before capture. Max 100 KB. Pro+ | |
| hideSelectors | string[] | Array of CSS selectors to hide before capture. Max 50. | |
| clickSelector | string | Click this element before capturing. | |
| clickDelay | integer | Wait (ms) after clicking before capture. |
Blocking
| Parameter | Type | Default | Description |
|---|---|---|---|
| blockAds | boolean | false | Block advertisements. Starter+ |
| blockTrackers | boolean | false | Block tracking scripts. Pro+ |
| blockCookieBanners | boolean | false | Auto-dismiss cookie consent banners (supports 20+ CMPs). |
| blockChatWidgets | boolean | false | Hide chat/support widgets. |
| blockResources | string[] | Resource types to block: image, font, stylesheet, script, media, xhr, fetch, websocket. |
Request options
| Parameter | Type | Default | Description |
|---|---|---|---|
| userAgent | string | Override the browser User-Agent string. | |
| extraHeaders | object | Additional HTTP headers to send with the request. | |
| cookies | object[] | Cookies to set before navigation. Each: {name, value, domain?, path?}. | |
| httpAuth | object | HTTP Basic Auth: {username, password}. | |
| proxy | object | Custom proxy: {server, username?, password?}. | |
| premiumProxy | boolean | Route through managed residential proxy for geo-restricted or bot-protected sites. | |
| geolocation | object | Emulate GPS: {latitude, longitude, accuracy?}. | |
| timezone | string | Timezone override (e.g. America/New_York). | |
| locale | string | Browser locale (e.g. en-US). |
Content validation
| Parameter | Type | Default | Description |
|---|---|---|---|
| failOnHttpError | boolean | false | Return an error if the page responds with an HTTP error status. |
| failIfContentMissing | string[] | Fail if any of these text strings are not found on the page. | |
| failIfContentContains | string[] | Fail if any of these text strings are found on the page. |
Storage & caching
| Parameter | Type | Default | Description |
|---|---|---|---|
| cache | boolean | false | Cache the result in Redis. Pro+ |
| cacheTtl | integer | 86400 | Cache TTL in seconds (60 to 2,592,000 = 30 days). |
| storage | object | {enabled: true, destination: "snapapi" | "user_s3"}. Store in SnapAPI Vault or your own S3 bucket. | |
| s3 | object | Direct upload to your S3 bucket inline: {bucket, key?, region?, endpoint?, access_key_id, secret_access_key, acl?}. |
Async & webhooks
| Parameter | Type | Default | Description |
|---|---|---|---|
| async | boolean | false | Return a job ID immediately and process in the background. |
| webhookUrl | string | URL to POST the result to when processing completes. | |
| webhookHeaders | object | Custom headers to include in webhook delivery. |
Thumbnail
| Parameter | Type | Default | Description |
|---|---|---|---|
| thumbnail | object | {enabled: true, width?: 50-800, height?: 50-600, fit?: "cover"|"contain"|"fill"}. Generate a thumbnail alongside the screenshot. |
Response
When responseType is "binary" (default), the response is the raw image with the appropriate Content-Type header.
When responseType is "json":
{
"success": true,
"data": "iVBORw0KGgo...", // base64 image data
"format": "png",
"width": 1280,
"height": 800,
"fileSize": 245832, // bytes
"took": 1523, // processing time in ms
"cached": false
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed on 1 field. See details for per-field errors.",
"details": [
{ "field": "url", "message": "'url' must be a valid URL (e.g. https://example.com)" }
],
"requestId": "req_abc123",
"docs": "https://api.snapapi.pics/v1/docs"
}
}Full example with options
curl -X POST https://api.snapapi.pics/v1/screenshot \ -H "X-Api-Key: sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://github.com", "format": "webp", "quality": 90, "width": 1440, "height": 900, "fullPage": true, "darkMode": true, "blockAds": true, "blockCookieBanners": true, "delay": 1000, "responseType": "json" }'
const response = await fetch('https://api.snapapi.pics/v1/screenshot', { method: 'POST', headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://github.com', format: 'webp', quality: 90, width: 1440, height: 900, fullPage: true, darkMode: true, blockAds: true, blockCookieBanners: true, delay: 1000, responseType: 'json', }), }); const { data, format, width, height, took } = await response.json(); console.log(`Captured ${width}x${height} in ${took}ms`);
import httpx import os response = httpx.post( "https://api.snapapi.pics/v1/screenshot", headers={"X-Api-Key": os.environ["SNAPAPI_KEY"]}, json={ "url": "https://github.com", "format": "webp", "quality": 90, "width": 1440, "height": 900, "fullPage": True, "darkMode": True, "blockAds": True, "blockCookieBanners": True, "delay": 1000, "responseType": "json", }, ) data = response.json() print(f"Captured {data['width']}x{data['height']} in {data['took']}ms")
Screenshot (GET)
Convenience GET endpoint for simple screenshot requests. Pass options as query parameters. Useful for embedding screenshots directly in <img> tags.
# Direct browser link: https://api.snapapi.pics/v1/screenshot?access_key=sk_live_xxx&url=https://example.com&format=png&width=1280 # Embed in HTML: <img src="https://api.snapapi.pics/v1/screenshot?access_key=sk_live_xxx&url=https://example.com" />
Supported query parameters: url, format, quality, width, height, full_page, delay, dark_mode, block_ads, cache, response_type.
PDF Generation
Generate a PDF from a URL or HTML. This is a shortcut for POST /v1/screenshot with format: "pdf". You can also pass format: "pdf" directly to the screenshot endpoint.
PDF-specific options
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | URL to convert to PDF. | |
| html | string | Raw HTML to convert to PDF. | |
| pdfOptions.pageSize | string | a4, a3, a5, letter, legal, tabloid, or custom. | |
| pdfOptions.landscape | boolean | false | Landscape page orientation. |
| pdfOptions.printBackground | boolean | false | Include background colors and images. |
| pdfOptions.scale | number | 1 | Scale factor (0.1 to 2). |
| pdfOptions.marginTop/Right/Bottom/Left | string | Margins (e.g. "1cm", "0.5in"). | |
| pdfOptions.headerTemplate | string | HTML template for the header. Use classes: date, title, url, pageNumber, totalPages. | |
| pdfOptions.footerTemplate | string | HTML template for the footer. | |
| pdfOptions.displayHeaderFooter | boolean | false | Show header and footer. |
| pdfOptions.pageRanges | string | Paper ranges to print (e.g. "1-5", "1,3,5"). |
Example
curl -X POST https://api.snapapi.pics/v1/pdf \ -H "X-Api-Key: sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/invoice", "pdfOptions": { "pageSize": "a4", "printBackground": true, "marginTop": "1cm", "marginBottom": "1cm" } }' \ -o invoice.pdf
const response = await fetch('https://api.snapapi.pics/v1/pdf', { method: 'POST', headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://example.com/invoice', pdfOptions: { pageSize: 'a4', printBackground: true, marginTop: '1cm', marginBottom: '1cm', }, }), }); const pdf = await response.arrayBuffer();
import httpx response = httpx.post( "https://api.snapapi.pics/v1/pdf", headers={"X-Api-Key": "sk_live_YOUR_API_KEY"}, json={ "url": "https://example.com/invoice", "pdfOptions": { "pageSize": "a4", "printBackground": True, "marginTop": "1cm", "marginBottom": "1cm", }, }, ) with open("invoice.pdf", "wb") as f: f.write(response.content)
Batch Screenshots
Queue up to 100 URLs for asynchronous screenshot processing. Returns a job ID to poll.
| Parameter | Type | Default | Description |
|---|---|---|---|
| urls* | string[] | Array of URLs to screenshot. 1-100 items. | |
| format | string | png | Output format for all screenshots. |
| width | integer | 1280 | Viewport width for all screenshots. |
| height | integer | 800 | Viewport height for all screenshots. |
| fullPage | boolean | false | Full page capture for all. |
| darkMode | boolean | false | Dark mode for all. |
| blockAds | boolean | false | Block ads for all. |
| webhookUrl | string | Webhook called when the batch completes. |
{
"success": true,
"jobId": "batch_abc123",
"status": "pending",
"total": 5,
"message": "Batch job created. Poll /v1/screenshot/batch/batch_abc123 for status."
}Poll the status with GET /v1/screenshot/batch/:jobId.
Web Scraping
Scrape content from any web page. Returns clean text (as markdown), raw HTML, or extracted links. Supports multi-page scraping and residential proxies.
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | URL to scrape. | |
| type | string | text | text (converted to markdown), html (raw HTML), or links (all anchor tags). |
| pages | integer | 1 | Number of pages to scrape (1-10). Follows pagination. |
| waitMs | integer | 0 | Wait (ms) after page load before scraping. Useful for JS-rendered content. |
| proxy | string | Custom proxy URL (e.g. http://user:pass@host:port). | |
| premiumProxy | boolean | Use managed residential proxy. | |
| blockResources | boolean | false | Block images, media, and fonts for faster scraping. |
| locale | string | Browser locale (e.g. en-US). |
Also available as GET /v1/scrape with query parameters: url, type, pages, wait_ms, block_resources, locale.
{
"success": true,
"results": [
{
"page": 1,
"url": "https://example.com",
"data": "# Example Domain\n\nThis domain is for use in illustrative examples..."
}
]
}Example: scrape and extract links
curl -X POST https://api.snapapi.pics/v1/scrape \ -H "X-Api-Key: sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://news.ycombinator.com", "type": "links"}'
const response = await fetch('https://api.snapapi.pics/v1/scrape', { method: 'POST', headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://news.ycombinator.com', type: 'links', }), }); const { results } = await response.json(); const links = JSON.parse(results[0].data);
import httpx response = httpx.post( "https://api.snapapi.pics/v1/scrape", headers={"X-Api-Key": "sk_live_YOUR_API_KEY"}, json={"url": "https://news.ycombinator.com", "type": "links"}, ) data = response.json() links = data["results"][0]["data"]
Content Extraction
Extract clean, structured content from any web page. Optimized for LLM pipelines -- feed the output directly to ChatGPT, Claude, or your own models.
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | URL to extract content from. | |
| type | string | markdown |
markdown -- clean markdown (best for LLMs)text -- plain texthtml -- raw HTMLarticle -- article with title, author, excerpt via Readabilitylinks -- all links on the pageimages -- all images with src, alt, dimensionsmetadata -- page metadata (OG tags, title, description)structured -- combined: content + metadata + word count
|
| selector | string | CSS selector to extract from a specific element. | |
| waitFor | string | Wait for this selector before extracting. | |
| timeout | integer | 30000 | Navigation timeout (ms). |
| darkMode | boolean | false | Enable dark mode. |
| blockAds | boolean | false | Block ads. |
| blockCookieBanners | boolean | false | Dismiss cookie banners. |
| includeImages | boolean | true | Include images in extracted content. |
| maxLength | integer | Truncate content to this many characters (100-500000). | |
| cleanOutput | boolean | true | Remove empty links, redundant whitespace. |
| fields | object | Structured field extraction: {"price": "product price", "title": "main heading"}. |
{
"success": true,
"type": "article",
"url": "https://example.com/blog/post",
"data": {
"title": "How to Build a REST API",
"byline": "Jane Doe",
"siteName": "Example Blog",
"excerpt": "A comprehensive guide to building REST APIs...",
"length": 4523,
"markdown": "# How to Build a REST API\n\nA comprehensive guide..."
},
"responseTime": 2341
}type: "markdown" with maxLength: 50000 to get clean content that fits within most LLM context windows. Combine with blockAds: true and blockCookieBanners: true for noise-free extraction.
Video Recording
Record a video of a web page. Supports auto-scrolling, multiple output formats, and custom viewport sizes. Max 3 concurrent video jobs per server.
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | URL to record. | |
| format | string | mp4 | mp4, webm, or gif. |
| width | integer | 1280 | Viewport width (320-1920). |
| height | integer | 720 | Viewport height (240-1080). |
| duration | integer | 5 | Recording duration in seconds (1-30). |
| fps | integer | 25 | Frames per second (10-30). |
| scrolling | boolean | false | Enable auto-scroll recording. |
| scrollSpeed | integer | 100 | Scroll speed (50-500 px/step). |
| scrollDelay | integer | 1000 | Delay between scrolls (ms). |
| scrollDuration | integer | 1500 | Duration of each scroll animation (ms). |
| scrollBy | integer | 800 | Pixels per scroll step (100-2000). |
| scrollEasing | string | ease_in_out | linear, ease_in, ease_out, ease_in_out, ease_in_out_quint. |
| scrollBack | boolean | true | Scroll back to top at end. |
| scrollComplete | boolean | true | Stop recording when scroll completes. |
| darkMode | boolean | false | Enable dark mode. |
| blockAds | boolean | false | Block ads during recording. |
| blockCookieBanners | boolean | false | Dismiss cookie banners. |
| delay | integer | 0 | Wait (ms) before starting recording. |
| responseType | string | binary | binary, base64, or json. |
Example
curl -X POST https://api.snapapi.pics/v1/video \ -H "X-Api-Key: sk_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "format": "mp4", "duration": 10, "scrolling": true, "scrollEasing": "ease_in_out" }' \ -o recording.mp4
const response = await fetch('https://api.snapapi.pics/v1/video', { method: 'POST', headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://example.com', format: 'mp4', duration: 10, scrolling: true, scrollEasing: 'ease_in_out', }), }); const fs = await import('fs'); fs.writeFileSync('recording.mp4', Buffer.from(await response.arrayBuffer()));
AI Analysis (BYOK)
Analyze a web page using AI. SnapAPI loads the page, extracts content, and sends it to OpenAI or Anthropic with your prompt. Bring Your Own Key (BYOK) -- you provide your own LLM API key.
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | string | URL to analyze. | |
| prompt* | string | Your analysis prompt (max 5000 chars). | |
| provider | string | openai | openai or anthropic. |
| apiKey* | string | Your OpenAI or Anthropic API key. | |
| model | string | Model override (e.g. gpt-4o, claude-3-opus-20240229). | |
| jsonSchema | object | JSON schema for structured output (OpenAI only). | |
| includeScreenshot | boolean | false | Include a screenshot for vision models. |
| includeMetadata | boolean | true | Include page metadata in the prompt context. |
| maxContentLength | integer | 50000 | Max content characters sent to the LLM. |
Job Status
Check the status of an async screenshot or batch job. Add ?includeData=true to include the base64 image data in the response once complete.
{
"success": true,
"jobId": "abc123",
"status": "completed", // pending | processing | completed | failed
"format": "png",
"width": 1280,
"height": 800,
"createdAt": "2026-03-23T10:00:00Z",
"completedAt": "2026-03-23T10:00:03Z"
}Device Presets
Returns all supported device presets grouped by category. No authentication required.
Available presets
- Desktop:
desktop-1080p,desktop-1440p,desktop-4k,macbook-pro-13,macbook-pro-16,imac-24 - Mobile:
iphone-12throughiphone-15-pro-max,iphone-se,pixel-7,pixel-8,pixel-8-pro,samsung-galaxy-s23,samsung-galaxy-s24 - Tablet:
ipad,ipad-mini,ipad-air,ipad-pro-11,ipad-pro-12.9,samsung-galaxy-tab-s9
Scheduled Screenshots
Create recurring screenshot jobs that run on a schedule. Manage schedules via the API or Dashboard.
Management endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/scheduled | List all scheduled jobs. |
| POST | /v1/scheduled | Create a scheduled screenshot job. |
| GET | /v1/scheduled/:id | Get schedule details. |
| PATCH | /v1/scheduled/:id | Update a schedule. |
| DELETE | /v1/scheduled/:id | Delete a schedule. |
| POST | /v1/scheduled/:id/run | Trigger an immediate run of a scheduled job. |
| GET | /v1/scheduled/presets | List available schedule presets (e.g. hourly, daily, weekly). |
Webhooks
Register webhook endpoints to receive notifications when async jobs complete, batches finish, or quota thresholds are hit. Manage webhooks via the API or Dashboard.
Supported events
screenshot.completed-- async screenshot finished successfullyscreenshot.failed-- async screenshot failedbatch.completed-- batch job finishedquota.warning-- 80% of monthly quota usedquota.exceeded-- monthly quota exceeded
To see all supported events programmatically, call GET /v1/webhooks/events.
Webhook management
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/webhooks | List all webhooks. |
| POST | /v1/webhooks | Create a webhook. Body: {url, events[], secret?}. Max 5 per account. |
| PATCH | /v1/webhooks/:id | Update a webhook. |
| DELETE | /v1/webhooks/:id | Delete a webhook. |
| POST | /v1/webhooks/:id/test | Send a test payload. |
| GET | /v1/webhooks/:id/deliveries | View webhook delivery history. |
Webhook payload
{
"event": "screenshot.completed",
"jobId": "abc123",
"success": true,
"data": "iVBORw0KGgo...",
"format": "png",
"width": 1280,
"height": 800,
"fileSize": 245832
}Verifying signatures
Every webhook delivery includes an X-SnapAPI-Signature header with an HMAC-SHA256 signature of the payload body, signed with your webhook secret.
const crypto = require('crypto'); function verifyWebhook(body, signature, secret) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); }
Webhook deliveries are retried up to 5 times with exponential backoff: immediate, 1 min, 5 min, 15 min, 1 hour.
Storage / Vault
Store screenshots, PDFs, and videos in SnapAPI's managed cloud storage (Vault) or upload directly to your own S3-compatible bucket.
Using SnapAPI Vault
Enable Vault storage by passing "storage": {"enabled": true, "destination": "snapapi"} in your screenshot request. Files are stored securely and accessible via signed URLs.
Using your own S3 bucket
Configure your S3 credentials in the Dashboard under Storage, or pass them inline with each request:
{
"url": "https://example.com",
"s3": {
"bucket": "my-screenshots",
"region": "us-east-1",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLE",
"acl": "private"
}
}Storage management endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /storage/overview | Storage usage summary (requires Bearer token auth). |
| GET | /storage/files | List stored files. |
| POST | /storage/user-s3 | Configure your S3 bucket settings. |
| POST | /storage/vault-preference | Enable/disable Vault storage. |
Caching
Enable caching to avoid re-capturing identical screenshots. Cached results are stored in Redis and returned instantly without consuming quota. Pro+
How it works
- Pass
"cache": truein your request. - SnapAPI generates a cache key from the relevant parameters: URL, format, quality, width, height, deviceScaleFactor, fullPage, selector, darkMode, and custom CSS.
- If a cached result exists and is within the TTL, it is returned immediately with
X-Screenshot-Cached: trueheader. - Cached responses do not count toward your monthly quota.
Cache TTL
Set "cacheTtl" in seconds. Default: 86400 (24 hours). Range: 60 seconds to 2,592,000 seconds (30 days).
cache parameter or set it to false. Changing any parameter that is part of the cache key (e.g. width, format) will also produce a new capture.
Async Processing
For long-running captures or when you do not want to hold an HTTP connection open, use async mode. The API returns a job ID immediately and processes the request in the background.
How to use async
- Add
"async": trueto any screenshot request. - The API returns
202 Acceptedwith ajobIdandstatusUrl. - Poll
GET /v1/jobs/:jobIduntilstatusis"completed"or"failed". - Alternatively, set a
webhookUrland receive the result via HTTP POST when done.
// Step 1: Start async job const { jobId, statusUrl } = await fetch('https://api.snapapi.pics/v1/screenshot', { method: 'POST', headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com', async: true }), }).then(r => r.json()); // Step 2: Poll for result let result; do { await new Promise(r => setTimeout(r, 2000)); result = await fetch(statusUrl, { headers: { 'X-Api-Key': apiKey }, }).then(r => r.json()); } while (result.status === 'pending' || result.status === 'processing');
Error Codes
All errors follow a consistent format with a machine-readable code, human-readable message, and a requestId for debugging.
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | One or more request parameters are invalid. Check the details array for per-field errors. |
BAD_REQUEST | 400 | Generic bad request (e.g. missing required fields). |
INVALID_URL | 400 | The URL is invalid, unreachable, or points to a private/internal IP (SSRF protection). |
MISSING_API_KEY | 401 | No API key provided in the request. |
UNAUTHORIZED | 401 | API key is invalid, expired, or revoked. |
FORBIDDEN | 403 | Your plan does not include this feature. Upgrade at snapapi.pics/pricing. |
NOT_FOUND | 404 | Resource or endpoint not found. |
CONFLICT | 409 | Conflict (e.g. email already registered). |
RATE_LIMITED | 429 | Too many requests. See rate limit headers. |
QUOTA_EXCEEDED | 429 | Monthly quota exceeded. Upgrade your plan or wait for reset. |
INTERNAL_ERROR | 500 | Something went wrong on our end. The requestId can be used for support requests. |
Error response format
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed on 2 fields. See details for per-field errors.",
"details": [
{ "field": "url", "message": "'url' must be a valid URL", "code": "invalid_string" },
{ "field": "format", "message": "'format' must be one of: 'png', 'jpeg', 'webp', 'avif', 'pdf'" }
],
"requestId": "req_abc123",
"docs": "https://api.snapapi.pics/v1/docs"
}
}429 errors, implement exponential backoff. For 500 errors, retry up to 3 times with 1s, 2s, 4s delays. Never retry 400 or 401 errors -- fix the request first.
Rate Limits
SnapAPI enforces monthly request quotas per plan. Rate limit information is included in every response via headers.
Free
Starter
Pro
Business
Rate limit headers
| Header | Description |
|---|---|
| X-RateLimit-Limit | Your monthly quota. |
| X-RateLimit-Remaining | Remaining requests this month. |
| X-RateLimit-Reset | ISO 8601 timestamp when quota resets. |
When you exceed your quota, the API returns a 429 status with the QUOTA_EXCEEDED error code. Cached responses (cache: true) do not count against your quota.
Additionally, nginx enforces a global rate limit of 50 requests per second per IP with a burst capacity of 100 to protect against abuse.
SDKs
Official client libraries for 8 languages. All SDKs include type definitions, retry logic with exponential backoff, and consistent error handling.
SDK quickstart (JavaScript)
import { SnapAPI } from 'snapapi'; const client = new SnapAPI({ apiKey: process.env.SNAPAPI_KEY }); // Screenshot const screenshot = await client.screenshot({ url: 'https://example.com', format: 'png', fullPage: true, }); // Scrape const content = await client.scrape({ url: 'https://example.com', type: 'text', }); // Extract for LLM const markdown = await client.extract({ url: 'https://example.com/blog/post', type: 'article', });
SDK quickstart (Python)
from snapapi import SnapAPI client = SnapAPI(api_key="sk_live_YOUR_API_KEY") # Screenshot screenshot = client.screenshot(url="https://example.com", format="png") # Extract for LLM pipeline article = client.extract( url="https://example.com/blog/post", type="article", block_ads=True, )
SDK quickstart (Ruby)
require 'snapapi' client = SnapAPI::Client.new(api_key: ENV['SNAPAPI_KEY']) # Screenshot screenshot = client.screenshot(url: 'https://example.com', format: 'png') # Extract for LLM pipeline article = client.extract(url: 'https://example.com/blog/post', type: 'article')
SDK quickstart (Java)
import pics.snapapi.SnapAPIClient; import pics.snapapi.ScreenshotRequest; SnapAPIClient client = new SnapAPIClient("sk_live_YOUR_API_KEY"); // Screenshot byte[] screenshot = client.screenshot( ScreenshotRequest.builder() .url("https://example.com") .format("png") .fullPage(true) .build() ); // Extract for LLM pipeline String article = client.extract( ExtractRequest.builder() .url("https://example.com/blog/post") .type("article") .build() );
All SDK documentation pages
Changelog
v2.0.0 -- March 2026
- Added AVIF format support for screenshots
- Added
devicepresets (25+ devices) - Added batch screenshot endpoint (
/v1/screenshot/batch) - Added async processing with webhook delivery
- Added inline S3 direct upload (
s3parameter) - Added AI analysis endpoint (
/v1/analyze) with BYOK - Added content validation (
failIfContentMissing,failIfContentContains) - Added thumbnail generation
- Added
extracttypes: article, images, metadata, structured - Added scroll video recording with easing functions
- Improved cookie banner dismissal (20+ CMP support)
- Improved error messages with per-field validation details
- All 8 SDKs updated to v2.0.0
v1.0.0 -- January 2026
- Initial release
- Screenshot, scrape, extract, video, PDF endpoints
- Free, Starter, and Pro plans
- JavaScript and Python SDKs