API Documentation

Everything you need to capture screenshots with the SnapRender API.

Quick Start

Get your API key from the dashboard, then make a request:

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=png" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output screenshot.png

That's it. The API returns the screenshot binary directly. For JSON metadata + base64 image, add response_type=json.

Cache is OFF by default. Every request returns a fresh screenshot. To save credits on repeated requests for the same URL, add cache=true to your request. Cached hits are free and return in under 200ms. See Caching for details.

Authentication

All API requests require an API key passed in the X-API-Key header.

X-API-Key: sk_live_your_key_here

API keys start with sk_live_ and are 40 characters long.

Create and manage keys in the dashboard. Keys are hashed server-side and shown only once at creation.

Missing or invalid keys return 401 UNAUTHORIZED.

Base URL

https://app.snap-render.com

All endpoints below are relative to this base URL. HTTPS is required.

Endpoints

Screenshots

GET /v1/screenshot

Capture a screenshot of any public URL. Returns the image binary by default, or JSON with base64 data when response_type=json. Cache is off by default; pass cache=true to return cached results (free, under 200ms).

Requires API key. Counts against monthly quota (cached hits are free).

See Parameters for all options and Response Format for output details.

POST /v1/screenshot

Capture a screenshot from a URL, raw HTML, or Markdown. Send a JSON body with exactly one source: url, html, or markdown. All screenshot parameters work with all three sources.

Request body (JSON)

Field Type Description
url string Public URL to screenshot
html string Raw HTML to render (max 2MB)
markdown string Markdown to render with styled template (max 500KB)
format, width, ... various All GET parameters as native JSON types (booleans, integers)

Exactly one of url, html, or markdown is required. Returns the image binary by default, or JSON with "response_type": "json".

Example: render HTML

{
  "html": "<h1>Hello World</h1><p>Rendered from raw HTML.</p>",
  "format": "png",
  "width": 800,
  "dark_mode": true
}

Example: render Markdown

{
  "markdown": "# Report Title\n\nSome **bold** text and `code`.",
  "format": "png",
  "width": 1280
}

Example: screenshot a URL via POST

{
  "url": "https://example.com",
  "format": "jpeg",
  "quality": 80,
  "full_page": true
}

See POST cURL examples for complete copy-paste commands.

GET /v1/screenshot/info

Check if a screenshot is cached without taking a new one. Accepts the same query parameters as /v1/screenshot. Does not count against quota.

Response

{
  "url": "https://example.com",
  "cached": true,
  "cache_key": "a1b2c3d4e5...",
  "cached_at": "2026-02-22T10:30:00.000Z",
  "expires_at": "2026-02-23T10:30:00.000Z",
  "content_type": "image/png"
}
POST /v1/screenshot/sign

Generate a pre-signed URL that renders a screenshot when visited. No API key needed to use the signed URL. Ideal for embedding in emails, documents, or sharing with third parties.

Signing is free and does not count against quota. Each render of the signed URL consumes one credit. URLs are tamper-proof (HMAC-SHA256) and expire after the specified duration.

JSON body: url (required), expires_in (seconds, default: 86400, range: 60 to 2,592,000). All screenshot parameters are supported to customize the signed screenshot.

Requires API key. See Signed URL Examples below or the Signed URLs feature guide.

Sample request body

{
  "url": "https://example.com",
  "expires_in": 86400,
  "format": "png",
  "width": 1280
}

Response

{
  "signed_url": "https://app.snap-render.com/v1/screenshot/render?url=...&expires=...&sig=...",
  "expires_at": "2026-04-09T10:00:00.000Z",
  "expires_in": 86400
}
GET /v1/screenshot/render

Render a screenshot using a signed URL. No API key required. The URL must have been generated via POST /v1/screenshot/sign.

Expired URLs return 410 Gone. Tampered URLs return 403 Forbidden. Revoking the API key that created the signed URL invalidates all its signed URLs.

Content Extraction

Feature guide →
GET /v1/extract

Extract content from any web page. Supports 6 extraction types: markdown, text, html, article, links, metadata.

Requires API key. Counts against monthly quota. See Extract Examples below.

Query parameters

Field Type Description
urlstringrequired Public URL to extract content from.
typestringmarkdown, text, html, article, links, or metadata. Default: markdown
selectorstringCSS selector to limit extraction scope. Optional.
block_adsbooleanBlock ads and trackers. Default: true
block_cookie_bannersbooleanRemove cookie consent banners. Default: true
delayintegerWait (ms) after page load (0-10000). Default: 0
max_lengthintegerMax characters to return (1-500000). Default: 100000
cachebooleanUse cached result if available. Default: false
cache_ttlintegerCache TTL in seconds (0-2592000). Default: 86400

Response (markdown type)

{
  "url": "https://example.com",
  "type": "markdown",
  "content": "# Example Domain\n\nThis domain is for use in illustrative examples...",
  "wordCount": 83,
  "processingTimeMs": 1240
}
POST /v1/extract

Same as GET but accepts a JSON body with native types (booleans, numbers). Recommended for programmatic use and SDKs.

Sample request body

{
  "url": "https://example.com",
  "type": "article",
  "block_ads": true,
  "max_length": 50000
}

Batch Screenshots

Feature guide →
POST /v1/screenshot/batch

Create a batch screenshot job for up to 50 URLs. Returns a job ID immediately (202 Accepted). All URLs share the same screenshot options.

Each URL consumes one credit. Failed URLs get credits rolled back automatically.

Request body (JSON)

Field Type Description
urlsstring[]required Array of URLs to capture (1-50).
formatstringpng, jpeg (or jpg), webp, or pdf. Default: png
widthnumberViewport width (320-3840). Default: 1280
heightnumberViewport height (200-10000). Default: 800
qualitynumberJPEG/WebP quality (1-100). Default: 90
full_pagebooleanCapture full scrollable page. Default: false
dark_modebooleanForce dark color scheme. Default: false
devicestringDevice preset (e.g. iphone_15_pro). See presets.
block_adsbooleanBlock ads and trackers. Default: true
block_cookie_bannersbooleanRemove cookie consent banners. Default: true
delaynumberWait (ms) after page load before capture (0-10000). Default: 0
hide_selectorsstringCSS selector(s) to hide before capture. Optional.
click_selectorstringCSS selector to click before capture. Optional.
user_agentstringCustom User-Agent string. Optional.

All screenshot parameters are supported. Options apply to every URL in the batch.

Sample request body

{
  "urls": ["https://example.com", "https://github.com", "https://google.com"],
  "format": "png",
  "width": 1280,
  "dark_mode": true
}

Response (202 Accepted)

{
  "jobId": "c91a6fb2-9feb-49fa-b187-28451d352473",
  "status": "pending",
  "statusUrl": "/v1/screenshot/batch/c91a6fb2-...",
  "total": 3,
  "completed": 0,
  "failed": 0,
  "estimatedSeconds": 20,
  "items": [
    { "url": "https://example.com", "status": "pending" },
    { "url": "https://github.com", "status": "pending" },
    { "url": "https://google.com", "status": "pending" }
  ],
  "createdAt": "2026-04-09T12:00:00.000Z"
}

See Batch Examples below or read the batch optimization guide.

GET /v1/screenshot/batch/:jobId

Poll the status of a batch screenshot job. Returns progress, download URLs for completed items, and error messages for failed items.

Requires API key. Only the job owner can access their batch jobs.

Response when completed

{
  "jobId": "c91a6fb2-...",
  "status": "completed",
  "statusUrl": "/v1/screenshot/batch/c91a6fb2-...",
  "total": 3,
  "completed": 2,
  "failed": 1,
  "items": [
    {
      "url": "https://example.com",
      "status": "completed",
      "downloadUrl": "https://...presigned-url...",
      "format": "png",
      "size": 45231
    },
    {
      "url": "https://github.com",
      "status": "completed",
      "downloadUrl": "https://...presigned-url...",
      "format": "png",
      "size": 87402
    },
    {
      "url": "https://unreachable.test",
      "status": "failed",
      "error": "TARGET_UNREACHABLE"
    }
  ],
  "createdAt": "2026-04-09T12:00:00.000Z",
  "completedAt": "2026-04-09T12:00:18.000Z"
}

Download URLs are presigned and valid for 24 hours. Failed items include an error code. Credits for failed items are refunded automatically.

POST /v1/webhooks

Register a webhook URL to receive event notifications. Max 5 per account. Returns the webhook with its HMAC-SHA256 signing secret.

Events: screenshot.completed, quota.warning (80%), quota.exceeded (100%).

Requires API key. See Webhook Examples below.

Sample request body

{
  "url": "https://your-server.com/webhook",
  "events": ["screenshot.completed", "quota.warning"]
}

Response (201 Created)

{
  "id": "a7f3b2c1-...",
  "url": "https://your-server.com/webhook",
  "events": ["screenshot.completed", "quota.warning"],
  "secret": "whsec_...",
  "isActive": true
}
GET /v1/webhooks

List all registered webhooks for the authenticated account.

Response

[
  {
    "id": "a7f3b2c1-...",
    "url": "https://your-server.com/webhook",
    "events": ["screenshot.completed", "quota.warning"],
    "secret": "whsec_...",
    "isActive": true
  }
]
DELETE /v1/webhooks/:id

Remove a webhook. Future events will no longer be delivered to this URL. Returns 204 No Content.

POST /v1/webhooks/:id/test

Send a test payload to a webhook to verify it's working. Returns delivery status and HTTP response code.

Response

{
  "deliveryId": "d4e5f6a7-...",
  "statusCode": 200,
  "success": true,
  "deliveredAt": "2026-04-09T12:00:00.000Z"
}

Usage

GET /v1/usage

Get your current monthly usage and plan limits.

Response

{
  "plan": "growth",
  "period": {
    "start": "2026-02-01T00:00:00.000Z",
    "end": "2026-02-28T23:59:59.000Z"
  },
  "usage": {
    "screenshots_used": 1423,
    "screenshots_limit": 10000,
    "screenshots_remaining": 8577
  }
}
GET /v1/usage/daily

Get daily usage breakdown. Useful for charts and usage monitoring.

Field Default Description
days30Number of days to look back (1-90)

Response

{
  "days": 30,
  "data": [
    { "date": "2026-02-20", "count": 47 },
    { "date": "2026-02-21", "count": 83 },
    { "date": "2026-02-22", "count": 12 }
  ]
}

Parameters

For GET, pass these as query strings. For POST, pass them as JSON body fields with native types (booleans, integers instead of strings). All features are available on every plan.

Parameter Type Default Description
url string URL to screenshot. Must be a public HTTP/HTTPS URL. required
format string png Output format: png jpeg (jpg also accepted) webp pdf
width integer 1280 Viewport width in pixels. Range: 320–3840.
height integer 800 Viewport height in pixels. Range: 200–10,000.
full_page boolean false Capture the full scrollable page. Max height capped at 32,768px to prevent OOM.
quality integer 90 Output quality for JPEG and WebP. Range: 1–100. Ignored for PNG/PDF.
delay integer 0 Milliseconds to wait after page load before capture. Range: 0–10,000. Useful for pages with animations or lazy-loaded content.
dark_mode boolean false Emulate prefers-color-scheme: dark. Works with sites that have dark mode CSS.
block_ads boolean true Block ads and tracking scripts before capture.
block_cookie_banners boolean true Remove cookie consent banners and GDPR dialogs.
hide_selectors string Comma-separated CSS selectors to hide (sets display:none). Example: .header,.footer,#banner
click_selector string CSS selector of an element to click before capture. Useful for dismissing modals or expanding content.
device string Device preset name. Sets viewport, pixel density, user agent, and mobile flag. See Device Presets.
user_agent string Custom User-Agent string. Overrides the default (or device preset) user agent.
cache boolean false Return a cached screenshot if one exists. Disabled by default: set to true to return cached results (free, no credit charged, but may be up to 24h old).
cache_ttl integer 86400 Cache TTL in seconds. Range: 0–2,592,000 (30 days). Clamped to your plan's max. See Rate Limits.
response_type string binary binary returns the image directly. json returns metadata + base64 data URI. See Response Format.

GET: Boolean parameters accept true or false as string values in the query string.

POST: The JSON body also accepts html (max 2MB) or markdown (max 500KB) as the source instead of url. Use native JSON types: true instead of "true", 1280 instead of "1280". See POST /v1/screenshot for examples.

Device Presets

Use the device parameter to emulate a real device. This sets viewport dimensions, pixel density, mobile mode, and user agent string automatically.

Preset Name Viewport Scale Mobile User Agent
iphone_14 390 × 844 3x Yes iPhone; CPU iPhone OS 16_0 (Safari 604.1)
iphone_15_pro 393 × 852 3x Yes iPhone; CPU iPhone OS 17_0 (Safari 604.1)
pixel_7 412 × 915 2.625x Yes Pixel 7; Android 13 (Chrome 116)
ipad_pro 1024 × 1366 2x Yes iPad; CPU OS 16_0 (Safari 604.1)
macbook_pro 1440 × 900 2x No Mac OS X 10_15_7 (Chrome 120)

When using a device preset, the width, height, and user_agent parameters are overridden by the preset values.

Response Format

Binary response (default)

The screenshot image is returned directly as the response body with the appropriate Content-Type header (image/png, image/jpeg, image/webp, or application/pdf).

JSON response

Set response_type=json to get metadata and the image as a base64 data URI. Recommended for AI agents and workflows that need to process the image inline.

{
  "url": "https://example.com",
  "format": "png",
  "width": 1280,
  "height": 800,
  "image": "data:image/png;base64,iVBORw0KGgo...",
  "size": 248576,
  "cache": "MISS",
  "responseTime": "2847ms",
  "remainingCredits": 8577
}

Response headers

Screenshot responses include these headers. Error responses additionally include X-Error-Code and X-Error-Message.

Header Description
X-CacheHIT or MISS
X-Request-IdUnique request ID for debugging
X-Response-TimeTime to generate the screenshot (e.g. 2847ms)
X-Remaining-CreditsRemaining screenshots for the current billing period
X-RateLimit-LimitMonthly screenshot quota for your plan
X-RateLimit-RemainingScreenshots remaining in the current billing period
X-RateLimit-Burst-LimitMax requests per minute for your plan
Content-Typeimage/png, image/jpeg, image/webp, application/pdf, or application/json
Content-LengthSize in bytes (binary responses only)
X-Error-CodeMachine-readable error code (error responses only)
X-Error-MessageHuman-readable error description (error responses only)

Error Handling

All API errors return JSON with a consistent structure:

{
  "error": {
    "code": "INVALID_URL",
    "message": "The provided URL is not a valid HTTP/HTTPS URL.",
    "status": 400
  }
}

Error codes

Code HTTP Description
UNAUTHORIZED401Missing or invalid API key
VALIDATION_ERROR400Invalid query parameters (Zod validation failed)
INVALID_URL400URL is not a valid HTTP or HTTPS URL
BLOCKED_URL400URL points to a private, local, or blocked address (SSRF protection)
INVALID_DEVICE400Unknown device preset name
RATE_LIMITED429Burst rate limit exceeded. Slow down and retry.
DOMAIN_RATE_LIMITED429Too many requests to the same domain. Limit varies by plan (see Rate Limits).
QUOTA_EXCEEDED429Monthly quota reached. Upgrade your plan.
RENDER_TIMEOUT408Page took longer than 30 seconds to render
TARGET_TIMEOUT408Target website took too long to respond (30s limit)
RENDER_FAILED502Chromium failed to render the page
TARGET_UNREACHABLE502Target website could not be reached (DNS failure, site down, or blocking requests)
TARGET_SSL_ERROR502Target website has an SSL/TLS certificate error
OUTPUT_TOO_LARGE413Screenshot exceeds the 10MB size limit
SERVICE_UNAVAILABLE503Service temporarily unavailable
SERVER_ERROR500Internal server error

For 429 responses, check the Retry-After header for the number of seconds to wait before retrying.

Rate Limits & Quotas

Each plan has a monthly screenshot quota, per-minute burst limit, and per-domain rate limit that scales with your plan.

Plan Monthly Quota Burst Rate Domain Rate Max Cache TTL Price
Free50010/min10/min1 day$0
Starter2,00030/min15/min7 days$9/mo
Growth10,00060/min15/min30 days$29/mo
Business50,000120/min20/min30 days$79/mo
Scale200,000200/min30/min30 days$199/mo

Quotas reset on the 1st of each month (UTC). Cached responses (cache HIT) do not count toward your monthly quota, only fresh captures (cache MISS) do. Use the /v1/usage endpoint to monitor your usage programmatically.

Caching

Screenshots are cached on Cloudflare R2 for fast repeat access. Caching is available on all plans but disabled by default, so every request returns a fresh capture.

Tip: Enable caching with cache=true to save credits on repeated requests. Cached responses are free, instant (under 200ms), and don't count toward your quota. The tradeoff is that the image may be up to cache_ttl seconds old.

How it works

  • cache=false (default) — Always take a fresh screenshot. Consumes one credit. You always get the latest version of the page.
  • cache=true — Return a cached screenshot if one exists. Cached responses are free and instant. The image may be up to cache_ttl seconds old.
  • cache_ttl=SECONDS — How long to store the cached result. Default: 86,400 (24 hours). Range: 0 to 2,592,000 (30 days). Clamped to your plan's max TTL.

When to enable cache

  • • You capture the same pages repeatedly (link previews, thumbnails, OG images)
  • • You want to minimize credit usage
  • • The target page content doesn't change often

When to keep cache off

  • • You are monitoring a page for visual changes and need the latest state
  • • The target page updates frequently (dashboards, live data, news)
  • • You are running visual regression tests and need pixel-accurate results

Cache details

The cache key is based on all screenshot parameters (URL, format, viewport, device, etc.), so changing any parameter generates a fresh screenshot.

Check the X-Cache response header: HIT means the result came from cache, MISS means a fresh capture was performed.

Cache hits are free. Only fresh captures (cache MISS) count toward your monthly quota.

Use the /v1/screenshot/info endpoint to check if a screenshot is cached without taking a new one.

SDKs

Official client libraries for popular languages. The API is also a standard REST endpoint that works with any HTTP client.

Node.js

npm install snaprender
import SnapRender from 'snaprender';
import fs from 'node:fs';

const snap = new SnapRender({ apiKey: 'sk_live_your_key_here' });

// Capture as buffer
const buffer = await snap.capture({ url: 'https://example.com', format: 'png' });
fs.writeFileSync('screenshot.png', buffer);

// Capture with options
const result = await snap.capture({
  url: 'https://github.com',
  format: 'jpeg',
  fullPage: true,
  darkMode: true,
  quality: 85,
  device: 'iphone_15_pro',
});
fs.writeFileSync('github-mobile-dark.jpg', result);

// Check usage
const usage = await snap.usage();
console.log(`${usage.remaining} screenshots remaining`);

View on npm

Python

pip install snaprender
from snaprender import SnapRender

snap = SnapRender("sk_live_your_key_here")

# Capture as bytes
data = snap.capture("https://example.com", format="png")
with open("screenshot.png", "wb") as f:
    f.write(data)

# Capture with options
data = snap.capture(
    "https://github.com",
    format="jpeg",
    full_page=True,
    dark_mode=True,
    quality=85,
    device="iphone_15_pro",
)
with open("github-mobile-dark.jpg", "wb") as f:
    f.write(data)

# Check usage
usage = snap.usage()
print(f"{usage['remaining']} screenshots remaining")

View on PyPI

Go

go get github.com/User0856/snaprender-go
package main

import (
    "context"
    "log"
    "os"

    snaprender "github.com/User0856/snaprender-go"
)

func main() {
    client := snaprender.NewClient("sk_live_your_key_here")

    // Capture a screenshot
    img, err := client.Capture(context.Background(), "https://example.com", nil)
    if err != nil {
        log.Fatal(err)
    }
    os.WriteFile("screenshot.png", img, 0644)

    // With options
    img, _ = client.Capture(context.Background(), "https://github.com", &snaprender.CaptureOptions{
        Format:   "jpeg",
        DarkMode: snaprender.Bool(true),
        Device:   "iphone_15_pro",
    })

    // Generate signed URL
    signed, _ := client.Sign(context.Background(), "https://example.com", &snaprender.SignOptions{
        ExpiresIn: 86400,
    })
    log.Println(signed.SignedURL)
}

View on GitHub

PHP

composer require snaprender/snaprender # coming soon
use SnapRender\SnapRender;

$snap = new SnapRender('sk_live_your_key_here');

// Capture a screenshot
$image = $snap->capture('https://example.com');
file_put_contents('screenshot.png', $image);

// With options
$image = $snap->capture('https://github.com', [
    'format' => 'jpeg',
    'dark_mode' => true,
    'device' => 'iphone_15_pro',
]);

// Generate signed URL
$signed = $snap->sign('https://example.com', ['expires_in' => 86400]);
echo $signed['signed_url'];

View source on GitHub Coming to Packagist

Ruby

gem install snaprender # coming soon
require "snaprender"

snap = SnapRender.new("sk_live_your_key_here")

# Capture a screenshot
image = snap.capture("https://example.com")
File.binwrite("screenshot.png", image)

# With options
image = snap.capture("https://github.com",
  format: "jpeg",
  dark_mode: true,
  device: "iphone_15_pro"
)

# Generate signed URL
signed = snap.sign("https://example.com", expires_in: 86400)
puts signed["signed_url"]

View source on GitHub Coming to RubyGems

MCP Server

Connect SnapRender to Claude Desktop, Claude Code, Cursor, or any MCP-compatible AI tool. Two options:

Remote (no install required)

Add to your MCP client config (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "snaprender": {
      "type": "streamable-http",
      "url": "https://app.snap-render.com/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_your_key_here"
      }
    }
  }
}

Local (npm package)

Runs the MCP server locally via npx:

{
  "mcpServers": {
    "snaprender": {
      "command": "npx",
      "args": ["-y", "snaprender-mcp"],
      "env": {
        "SNAPRENDER_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}

Once connected, ask your AI assistant: "Take a screenshot of github.com" and it will call the SnapRender API through MCP.

View on npm

Code Examples

Basic screenshot

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=png" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output screenshot.png

Full page capture

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://en.wikipedia.org/wiki/Screenshot&format=png&full_page=true" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output full-page.png

Dark mode

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://github.com&format=png&dark_mode=true" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output github-dark.png

Device emulation

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&device=iphone_15_pro&format=png" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output mobile.png

Hide elements

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=png&hide_selectors=.header,.footer,%23sidebar" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output clean.png

PDF generation

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=pdf&full_page=true" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output page.pdf

JSON response for AI agents

cURL
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&format=png&response_type=json" \
  -H "X-API-Key: YOUR_API_KEY"

Returns a JSON object with the screenshot as a base64 data URI in the image field, plus metadata like size, cache status, and remaining credits.

Render HTML (POST)

cURL
curl -X POST "https://app.snap-render.com/v1/screenshot" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html":"<html><body><h1>Hello World</h1></body></html>","format":"png","width":800,"height":600}' \
  --output screenshot.png

Renders raw HTML content and captures it as a screenshot. Max 2MB. No URL needed.

Render Markdown (POST)

cURL
curl -X POST "https://app.snap-render.com/v1/screenshot" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"markdown":"# Hello World\n\nThis is **bold** and this is `code`.","format":"png"}' \
  --output screenshot.png

Renders Markdown content with a clean styled template. Supports headings, code blocks, tables, lists, and more. Max 500KB.

Generate Signed URL

cURL
curl -X POST "https://app.snap-render.com/v1/screenshot/sign" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","expires_in":86400}'

Returns a signed URL that anyone can use to render the screenshot without an API key. Signing is free. The URL expires after expires_in seconds (default: 1 day, max: 30 days).

Use Signed URL

cURL
# No API key needed, just use the signed_url from the sign response
curl "https://app.snap-render.com/v1/screenshot/render?url=...&expires=...&key_prefix=...&sig=..." \
  --output screenshot.png

The signed URL can be used in <img> tags, emails, documents, or shared directly. Tampered URLs return 403, expired URLs return 410.

Extract Content (Markdown)

cURL
curl "https://app.snap-render.com/v1/extract?url=https://example.com&type=markdown" \
  -H "X-API-Key: YOUR_API_KEY"

Extracts readable content from any web page as clean Markdown. Uses Mozilla Readability to strip navigation, ads, and clutter. Also supports: text, html, article, links, metadata.

Extract Article (POST)

cURL
curl -X POST "https://app.snap-render.com/v1/extract" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","type":"article"}'

Returns structured article data: title, author, excerpt, content (as Markdown), and word count. The POST endpoint accepts native JSON types.

Extract Metadata

cURL
curl "https://app.snap-render.com/v1/extract?url=https://example.com&type=metadata" \
  -H "X-API-Key: YOUR_API_KEY"

Extracts page title, description, canonical URL, Open Graph tags (og:title, og:description, og:image, og:url, og:type), and Twitter Card tags.

Batch Screenshots (Create Job)

Submit up to 50 URLs in one request. Returns a job ID for polling.

cURL
curl -X POST "https://app.snap-render.com/v1/screenshot/batch" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com", "https://github.com", "https://google.com"],
    "format": "png",
    "width": 1280,
    "dark_mode": true
  }'

Returns 202 with jobId and statusUrl. Each URL consumes one credit; failed URLs get credits rolled back.

Batch Screenshots (Poll Status)

cURL
curl "https://app.snap-render.com/v1/screenshot/batch/JOB_ID" \
  -H "X-API-Key: YOUR_API_KEY"

Poll until status is completed or failed. Completed items include downloadUrl (presigned, valid 24h). Jobs expire after 24 hours.

Webhooks (Create)

Register a webhook URL to receive event notifications. Payloads are signed with HMAC-SHA256.

cURL
curl -X POST "https://app.snap-render.com/v1/webhooks" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["screenshot.completed", "quota.warning"]
  }'

Save the returned secret (starts with whsec_) to verify incoming webhook signatures.

Verify Webhook Signature (Node.js)

JavaScript
import { createHmac } from 'crypto';

function verifySignature(payload, signature, secret) {
  const expected = createHmac('sha256', secret).update(payload).digest('hex');
  const sig = signature.replace('sha256=', '');
  return sig === expected;
}

// In your webhook handler:
const isValid = verifySignature(
  req.body,                                    // raw body string
  req.headers['x-snaprender-signature'],       // sha256=...
  'whsec_your_webhook_secret'
);

Events: screenshot.completed (batch job done), quota.warning (80% used), quota.exceeded (100% used). Failed deliveries retry up to 5 times with exponential backoff.

Node.js (fetch, no SDK)

JavaScript
const url = new URL('https://app.snap-render.com/v1/screenshot');
url.searchParams.set('url', 'https://example.com');
url.searchParams.set('format', 'png');
url.searchParams.set('dark_mode', 'true');

const res = await fetch(url, {
  headers: { 'X-API-Key': 'YOUR_API_KEY' },
});

const buffer = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('screenshot.png', buffer);
console.log(`Cache: ${res.headers.get('x-cache')}, Remaining: ${res.headers.get('x-remaining-credits')}`);

Python (requests, no SDK)

Python
import requests

response = requests.get(
    "https://app.snap-render.com/v1/screenshot",
    params={"url": "https://example.com", "format": "png", "dark_mode": "true"},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
response.raise_for_status()

with open("screenshot.png", "wb") as f:
    f.write(response.content)
print(f"Cache: {response.headers['X-Cache']}, Remaining: {response.headers['X-Remaining-Credits']}")

Check usage

cURL
curl "https://app.snap-render.com/v1/usage" \
  -H "X-API-Key: YOUR_API_KEY"

Ready to start?

Create a free account and get your API key in 30 seconds.