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.
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.
| Header | Value |
|---|---|
Authorization | Bearer sk_live_your_api_key |
Content-Type | application/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.
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.
| Plan | Requests / month | Burst rate |
|---|---|---|
| Free | 100 | 2 req/s |
| Starter | 1,000 | 5 req/s |
| Growth | 5,000 | 10 req/s |
| Business | 25,000 | 25 req/s |
Rate limit headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Total requests allowed this billing period |
X-RateLimit-Remaining | Requests remaining in the current period |
X-RateLimit-Reset | Unix timestamp when the limit resets |
Retry-After | Seconds 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": {
"code": "rate_limit_exceeded",
"message": "You have exceeded your monthly quota. Upgrade your plan or wait until the next billing period.",
"status": 429
}
}
| Status | Code | Description |
|---|---|---|
400 | invalid_request | Missing required parameter or malformed JSON body |
401 | unauthorized | Missing or invalid API key |
403 | forbidden | API key does not have permission for this action |
404 | not_found | The requested endpoint does not exist |
422 | unprocessable_entity | The target URL could not be loaded or rendered |
429 | rate_limit_exceeded | Monthly quota or burst rate exceeded |
500 | internal_error | Unexpected server error — retry with exponential backoff |
503 | service_unavailable | Renderer temporarily unavailable — retry after delay |
/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
| Parameter | Type | Required | Description |
|---|---|---|---|
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).
| Header | Value |
|---|---|
Content-Type | image/png, image/jpeg, or image/webp |
Content-Length | Size in bytes |
X-Cache | HIT or MISS |
X-Render-Time | Render duration in milliseconds |
Try it
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";
/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
| Parameter | Type | Required | Description |
|---|---|---|---|
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
| Template | Description | Supports |
|---|---|---|
basic | Clean text on solid background | title, subtitle, background_color, text_color, logo_url |
article | Blog/article style with author, date, reading time | title, subtitle, logo_url, author, date, reading_time |
product | Product card with image, price, CTA | title, subtitle, image_url, price, cta_text |
social | Profile card for social sharing | title, subtitle, avatar_url, handle |
gradient | Title text over an animated gradient background | title, subtitle, gradient_from, gradient_to |
minimal | Centered text with generous whitespace | title, subtitle, logo_url |
Try it
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);
/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
| Parameter | Type | Required | Description |
|---|---|---|---|
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
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);
/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
{
"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
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.
| Language | Package | Status |
|---|---|---|
| Python | pip install snapapi | Coming soon |
| Node.js | npm install @snapapi/sdk | Coming soon |
| PHP | composer require snapapi/sdk | Coming soon |
| Go | go get github.com/snapapi/go-sdk | Coming soon |
| Ruby | gem install snapapi | Coming 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.
{
"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
Added selector parameter to the screenshot endpoint for element-level capture.
Launched /v1/og-image endpoint with six built-in templates.
Reduced average render time by 40% with a new rendering pipeline.
Public beta launch with screenshot, PDF, and usage endpoints.