API Reference

The SnapAPI REST API lets you capture screenshots, generate Open Graph images, and export PDFs from any URL or custom HTML. All endpoints accept JSON and return binary image/PDF data or JSON metadata.

Base URL
https://api.snapapi.dev

All API requests must include a valid API key in the Authorization header. Responses include rate limit headers so you can track your usage in real time.

Authentication

Authenticate every request by including your API key as a Bearer token in the Authorization header. Keys are scoped to your account and can be rotated from the dashboard at any time.

HeaderValue
AuthorizationBearer sk_live_your_api_key
Content-Typeapplication/json

API keys follow the format sk_live_ for production and sk_test_ for sandbox environments. Test keys have the same rate limits but watermark output images.

POST Example authenticated request
curl -X POST https://api.snapapi.dev/v1/screenshot \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
import requests

resp = requests.post(
    "https://api.snapapi.dev/v1/screenshot",
    headers={"Authorization": "Bearer sk_live_abc123"},
    json={"url": "https://example.com"},
)
print(resp.status_code)
const resp = await fetch("https://api.snapapi.dev/v1/screenshot", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_abc123",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com" }),
});
console.log(resp.status);
$ch = curl_init('https://api.snapapi.dev/v1/screenshot');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer sk_live_abc123',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['url' => 'https://example.com']),
    CURLOPT_RETURNTRANSFER => true,
]);
$result = curl_exec($ch);

Base URL

All API endpoints are served over HTTPS from a single base URL. HTTP requests are rejected.

https://api.snapapi.dev

API versioning is handled via the URL path. The current version is v1. When breaking changes are introduced, a new version will be released and the previous version will remain available for at least 12 months.

Rate Limits

Rate limits are applied per API key based on your plan. Limits reset at the start of each calendar month (UTC). Every response includes headers so you can track usage programmatically.

PlanRequests / monthBurst rate
Free1002 req/s
Starter1,0005 req/s
Growth5,00010 req/s
Business25,00025 req/s

Rate limit headers

HeaderDescription
X-RateLimit-LimitTotal requests allowed this billing period
X-RateLimit-RemainingRequests remaining in the current period
X-RateLimit-ResetUnix timestamp when the limit resets
Retry-AfterSeconds to wait before retrying (only on 429 responses)

Error Codes

Errors return a JSON body with a machine-readable code, a human-readable message, and an HTTP status code. Your integration should handle these gracefully.

ERROR Error response format
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "You have exceeded your monthly quota. Upgrade your plan or wait until the next billing period.",
    "status": 429
  }
}
StatusCodeDescription
400invalid_requestMissing required parameter or malformed JSON body
401unauthorizedMissing or invalid API key
403forbiddenAPI key does not have permission for this action
404not_foundThe requested endpoint does not exist
422unprocessable_entityThe target URL could not be loaded or rendered
429rate_limit_exceededMonthly quota or burst rate exceeded
500internal_errorUnexpected server error — retry with exponential backoff
503service_unavailableRenderer temporarily unavailable — retry after delay
POST /v1/screenshot

Capture a screenshot of any publicly accessible URL. Returns the image binary directly (PNG, JPEG, or WebP). Supports full-page capture, custom viewports, device emulation, CSS injection, and dark mode rendering.

Request body

ParameterTypeRequiredDescription
url string Required The URL to capture. Must be publicly accessible and start with http:// or https://.
format string Optional Output format. One of png, jpeg, webp. Default: png.
quality integer Optional Image quality for JPEG/WebP (1-100). Default: 80. Ignored for PNG.
viewport object Optional Viewport dimensions. {"width": 1280, "height": 720}. Default: 1280x720.
full_page boolean Optional Capture the full scrollable page instead of just the viewport. Default: false.
device_scale_factor number Optional Device pixel ratio (1-3). Use 2 for retina. Default: 1.
dark_mode boolean Optional Emulate prefers-color-scheme: dark. Default: false.
css string Optional Custom CSS to inject before capture. Useful for hiding elements or restyling.
wait_for string Optional CSS selector to wait for before capturing. The capture begins once this element is visible.
delay integer Optional Additional delay in milliseconds after page load before capturing. Max: 10000.
cache_ttl integer Optional Cache TTL in seconds. Set to 0 to bypass cache. Default: 3600.
selector string Optional CSS selector of a specific element to capture. Only that element's bounding box is returned.

Response

On success, returns 200 OK with the image binary. The Content-Type header matches the requested format (image/png, image/jpeg, or image/webp).

HeaderValue
Content-Typeimage/png, image/jpeg, or image/webp
Content-LengthSize in bytes
X-CacheHIT or MISS
X-Render-TimeRender duration in milliseconds

Try it

POST /v1/screenshot
curl -X POST https://api.snapapi.dev/v1/screenshot \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://github.com",
    "viewport": {"width": 1440, "height": 900},
    "format": "png",
    "full_page": false,
    "dark_mode": true,
    "css": ".header-banner { display: none; }",
    "cache_ttl": 3600
  }' \
  --output github.png
import requests

response = requests.post(
    "https://api.snapapi.dev/v1/screenshot",
    headers={"Authorization": "Bearer sk_live_abc123"},
    json={
        "url": "https://github.com",
        "viewport": {"width": 1440, "height": 900},
        "format": "png",
        "full_page": False,
        "dark_mode": True,
        "css": ".header-banner { display: none; }",
        "cache_ttl": 3600,
    },
)

with open("github.png", "wb") as f:
    f.write(response.content)

print(f"Status: {response.status_code}")
print(f"Cache: {response.headers.get('X-Cache')}")
print(f"Render time: {response.headers.get('X-Render-Time')}ms")
import { writeFileSync } from "node:fs";

const response = await fetch("https://api.snapapi.dev/v1/screenshot", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_abc123",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://github.com",
    viewport: { width: 1440, height: 900 },
    format: "png",
    full_page: false,
    dark_mode: true,
    css: ".header-banner { display: none; }",
    cache_ttl: 3600,
  }),
});

const buffer = Buffer.from(await response.arrayBuffer());
writeFileSync("github.png", buffer);
console.log(`Cache: ${response.headers.get("X-Cache")}`);
$ch = curl_init('https://api.snapapi.dev/v1/screenshot');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer sk_live_abc123',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'url'       => 'https://github.com',
        'viewport'  => ['width' => 1440, 'height' => 900],
        'format'    => 'png',
        'full_page' => false,
        'dark_mode' => true,
        'css'       => '.header-banner { display: none; }',
        'cache_ttl' => 3600,
    ]),
]);

$image = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

file_put_contents('github.png', $image);
echo "Status: $status\n";
POST /v1/og-image

Generate dynamic Open Graph images for social media previews. Choose from built-in templates or provide custom HTML. Returns a 1200x630 image optimized for social sharing.

Request body

ParameterTypeRequiredDescription
template string Conditional Built-in template name. One of basic, article, product, social, gradient, minimal. Required if html is not provided.
html string Conditional Custom HTML to render as the OG image. Required if template is not provided. Full CSS support.
title string Optional Primary heading text. Used by built-in templates.
subtitle string Optional Secondary text below the title. Used by built-in templates.
logo_url string Optional URL to a logo image to include in the OG image.
background_color string Optional Background color as hex (#1a1a2e) or CSS color. Default varies by template.
text_color string Optional Text color as hex or CSS color. Default: #ffffff.
format string Optional Output format. One of png, jpeg, webp. Default: png.
width integer Optional Image width. Default: 1200.
height integer Optional Image height. Default: 630.
cache_ttl integer Optional Cache TTL in seconds. Default: 86400 (24 hours).

Templates

TemplateDescriptionSupports
basicClean text on solid backgroundtitle, subtitle, background_color, text_color, logo_url
articleBlog/article style with author, date, reading timetitle, subtitle, logo_url, author, date, reading_time
productProduct card with image, price, CTAtitle, subtitle, image_url, price, cta_text
socialProfile card for social sharingtitle, subtitle, avatar_url, handle
gradientTitle text over an animated gradient backgroundtitle, subtitle, gradient_from, gradient_to
minimalCentered text with generous whitespacetitle, subtitle, logo_url

Try it

POST /v1/og-image
curl -X POST https://api.snapapi.dev/v1/og-image \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "article",
    "title": "Building a Screenshot API from Scratch",
    "subtitle": "A deep dive into headless browsers, rendering pipelines, and edge caching.",
    "logo_url": "https://example.com/logo.png",
    "author": "Jane Developer",
    "date": "2026-05-26",
    "reading_time": "8 min read",
    "format": "png"
  }' \
  --output og-image.png
import requests

response = requests.post(
    "https://api.snapapi.dev/v1/og-image",
    headers={"Authorization": "Bearer sk_live_abc123"},
    json={
        "template": "article",
        "title": "Building a Screenshot API from Scratch",
        "subtitle": "A deep dive into headless browsers, rendering pipelines, and edge caching.",
        "logo_url": "https://example.com/logo.png",
        "author": "Jane Developer",
        "date": "2026-05-26",
        "reading_time": "8 min read",
        "format": "png",
    },
)

with open("og-image.png", "wb") as f:
    f.write(response.content)
import { writeFileSync } from "node:fs";

const response = await fetch("https://api.snapapi.dev/v1/og-image", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_abc123",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    template: "article",
    title: "Building a Screenshot API from Scratch",
    subtitle: "A deep dive into headless browsers, rendering pipelines, and edge caching.",
    logo_url: "https://example.com/logo.png",
    author: "Jane Developer",
    date: "2026-05-26",
    reading_time: "8 min read",
    format: "png",
  }),
});

writeFileSync("og-image.png", Buffer.from(await response.arrayBuffer()));
$ch = curl_init('https://api.snapapi.dev/v1/og-image');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer sk_live_abc123',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'template'     => 'article',
        'title'        => 'Building a Screenshot API from Scratch',
        'subtitle'     => 'A deep dive into headless browsers and edge caching.',
        'logo_url'     => 'https://example.com/logo.png',
        'author'       => 'Jane Developer',
        'date'         => '2026-05-26',
        'reading_time' => '8 min read',
        'format'       => 'png',
    ]),
]);

file_put_contents('og-image.png', curl_exec($ch));
curl_close($ch);
POST /v1/pdf

Export any webpage as a PDF document. Control page size, margins, headers, footers, and whether background graphics are included. Returns the PDF binary directly.

Request body

ParameterTypeRequiredDescription
url string Required The URL to convert to PDF. Must start with http:// or https://.
page_size string Optional Paper size. One of A4, Letter, Legal, Tabloid, A3. Default: A4.
orientation string Optional Page orientation. portrait or landscape. Default: portrait.
margins object Optional Page margins in CSS units. {"top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm"}.
print_background boolean Optional Include background colors and images. Default: true.
header_html string Optional Custom HTML for the page header. Supports {{pageNumber}} and {{totalPages}} placeholders.
footer_html string Optional Custom HTML for the page footer. Same placeholders as header.
page_ranges string Optional Pages to include, e.g. "1-5, 8". Default: all pages.
css string Optional Custom CSS to inject before generating the PDF.
wait_for string Optional CSS selector to wait for before generating. Same behavior as screenshot endpoint.
cache_ttl integer Optional Cache TTL in seconds. Default: 3600.

Response

Returns 200 OK with Content-Type: application/pdf.

Try it

POST /v1/pdf
curl -X POST https://api.snapapi.dev/v1/pdf \
  -H "Authorization: Bearer sk_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/invoice/12345",
    "page_size": "A4",
    "orientation": "portrait",
    "margins": {
      "top": "20mm",
      "right": "15mm",
      "bottom": "20mm",
      "left": "15mm"
    },
    "print_background": true,
    "footer_html": "
Page {{pageNumber}} of {{totalPages}}
" }' \ --output invoice.pdf
import requests

response = requests.post(
    "https://api.snapapi.dev/v1/pdf",
    headers={"Authorization": "Bearer sk_live_abc123"},
    json={
        "url": "https://example.com/invoice/12345",
        "page_size": "A4",
        "orientation": "portrait",
        "margins": {
            "top": "20mm",
            "right": "15mm",
            "bottom": "20mm",
            "left": "15mm",
        },
        "print_background": True,
        "footer_html": '<div style="font-size:10px;text-align:center;width:100%">Page {{pageNumber}} of {{totalPages}}</div>',
    },
)

with open("invoice.pdf", "wb") as f:
    f.write(response.content)
import { writeFileSync } from "node:fs";

const response = await fetch("https://api.snapapi.dev/v1/pdf", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_abc123",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/invoice/12345",
    page_size: "A4",
    orientation: "portrait",
    margins: { top: "20mm", right: "15mm", bottom: "20mm", left: "15mm" },
    print_background: true,
    footer_html: `<div style="font-size:10px;text-align:center;width:100%">Page {{pageNumber}} of {{totalPages}}</div>`,
  }),
});

writeFileSync("invoice.pdf", Buffer.from(await response.arrayBuffer()));
$ch = curl_init('https://api.snapapi.dev/v1/pdf');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer sk_live_abc123',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'url'              => 'https://example.com/invoice/12345',
        'page_size'        => 'A4',
        'orientation'      => 'portrait',
        'margins'          => ['top' => '20mm', 'right' => '15mm', 'bottom' => '20mm', 'left' => '15mm'],
        'print_background' => true,
        'footer_html'      => '<div style="font-size:10px;text-align:center;width:100%">Page {{pageNumber}} of {{totalPages}}</div>',
    ]),
]);

file_put_contents('invoice.pdf', curl_exec($ch));
curl_close($ch);
GET /v1/usage

Retrieve your current billing period usage, quota, and plan details. Useful for building dashboards or monitoring consumption.

Request

No request body. Send a GET request with your API key in the Authorization header.

Response

GET Response body
{
  "plan": "growth",
  "billing_period": {
    "start": "2026-05-01T00:00:00Z",
    "end": "2026-05-31T23:59:59Z"
  },
  "usage": {
    "screenshots": 1847,
    "og_images": 312,
    "pdfs": 89,
    "total": 2248
  },
  "quota": {
    "limit": 5000,
    "remaining": 2752,
    "reset_at": "2026-06-01T00:00:00Z"
  },
  "rate_limit": {
    "burst": 10,
    "unit": "requests_per_second"
  }
}

Try it

GET /v1/usage
curl https://api.snapapi.dev/v1/usage \
  -H "Authorization: Bearer sk_live_abc123"
import requests

response = requests.get(
    "https://api.snapapi.dev/v1/usage",
    headers={"Authorization": "Bearer sk_live_abc123"},
)

data = response.json()
print(f"Used: {data['usage']['total']} / {data['quota']['limit']}")
print(f"Remaining: {data['quota']['remaining']}")
const response = await fetch("https://api.snapapi.dev/v1/usage", {
  headers: { "Authorization": "Bearer sk_live_abc123" },
});

const data = await response.json();
console.log(`Used: ${data.usage.total} / ${data.quota.limit}`);
console.log(`Remaining: ${data.quota.remaining}`);
$ch = curl_init('https://api.snapapi.dev/v1/usage');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_abc123'],
]);

$data = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Used: {$data['usage']['total']} / {$data['quota']['limit']}\n";
echo "Remaining: {$data['quota']['remaining']}\n";

SDKs & Libraries

Official client libraries are coming soon. In the meantime, the API works with any HTTP client in any language. The examples throughout this documentation cover the most common integrations.

LanguagePackageStatus
Pythonpip install snapapiComing soon
Node.jsnpm install @snapapi/sdkComing soon
PHPcomposer require snapapi/sdkComing soon
Gogo get github.com/snapapi/go-sdkComing soon
Rubygem install snapapiComing soon

Webhooks

Webhooks are available on the Business plan. When a capture completes (useful for long-running renders), SnapAPI sends a POST request to your configured endpoint with the result metadata.

POST Webhook payload
{
  "event": "capture.completed",
  "timestamp": "2026-05-26T14:30:00Z",
  "data": {
    "id": "cap_abc123def456",
    "type": "screenshot",
    "url": "https://example.com",
    "format": "png",
    "size_bytes": 245890,
    "render_time_ms": 1230,
    "download_url": "https://cdn.snapapi.dev/captures/cap_abc123def456.png",
    "expires_at": "2026-05-27T14:30:00Z"
  }
}

Changelog

2026-05-20
New

Added selector parameter to the screenshot endpoint for element-level capture.

2026-05-12
New

Launched /v1/og-image endpoint with six built-in templates.

2026-05-01
Improved

Reduced average render time by 40% with a new rendering pipeline.

2026-04-15
New

Public beta launch with screenshot, PDF, and usage endpoints.