snaprender
v0.7.3
Published
Official Node.js SDK for SnapRender Screenshot API
Maintainers
Readme
snaprender
Official Node.js SDK for the SnapRender Screenshot API. Capture pixel-perfect screenshots, extract web content, run batch jobs, and manage webhooks with a single import.
200 free screenshots/month. Get your API key
Features
- Screenshot from URL, HTML, or Markdown in PNG, JPEG, WebP, or PDF
- Signed URLs for keyless sharing and embedding
- Content extraction as markdown, text, HTML, article, links, or metadata
- Batch screenshots for up to 50 URLs in one job
- Webhooks for screenshot completion and quota alerts
- Device emulation with iPhone, iPad, Pixel, and more presets
- Dark mode, ad blocking, and cookie banner removal
- Smart caching with configurable TTL
- Full-page captures of entire scrollable pages
- Usage tracking with monthly and daily breakdowns
Install
npm install snaprenderQuick Start
import SnapRender from 'snaprender';
import fs from 'node:fs';
const snap = new SnapRender({ apiKey: 'sk_live_...' });
// Capture a screenshot
const buffer = await snap.capture({ url: 'https://example.com' });
fs.writeFileSync('screenshot.png', buffer);Cache is OFF by default. Every request captures a fresh screenshot. To save credits on repeated requests, pass
cache: true. Cached hits are free and return in under 200ms:const buffer = await snap.capture({ url: 'https://example.com', cache: true });
API
new SnapRender(options)
| Option | Type | Required | Default |
|--------|------|----------|---------|
| apiKey | string | Yes | |
| baseUrl | string | No | https://app.snap-render.com |
snap.capture(options) → Promise<Buffer>
Capture a screenshot from a URL, raw HTML, or Markdown. Provide exactly one of url, html, or markdown.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| url | string | | URL to capture |
| html | string | | Raw HTML to render and capture |
| markdown | string | | Markdown to render and capture |
| format | 'png' \| 'jpeg' \| 'webp' \| 'pdf' | 'png' | Output format |
| width | number | 1280 | Viewport width |
| height | number | 800 | Viewport height |
| fullPage | boolean | false | Capture full scrollable page |
| quality | number | 90 | JPEG/WebP quality (1-100) |
| delay | number | 0 | Wait ms after page load |
| darkMode | boolean | false | Emulate dark mode |
| blockAds | boolean | true | Block ad networks |
| blockCookieBanners | boolean | true | Remove cookie banners |
| device | string | | Device preset ('iphone_14', 'iphone_15_pro', 'pixel_7', 'ipad_pro', 'macbook_pro') |
| hideSelectors | string | | Comma-separated CSS selectors to hide |
| clickSelector | string | | CSS selector to click before capture |
| userAgent | string | | Custom user agent string |
| cache | boolean | false | Return cached result if available. Off by default: set true to enable caching. Cached hits are free but may be up to 24h old. |
| cacheTtl | number | 86400 | Cache lifetime in seconds (default 24h). Cached screenshots older than this are recaptured. |
| responseType | 'binary' \| 'json' | 'binary' | 'json' returns metadata + base64 data URI |
Capture by URL:
const buffer = await snap.capture({
url: 'https://example.com',
format: 'jpeg',
width: 1920,
height: 1080,
fullPage: true,
darkMode: true,
quality: 95,
});
fs.writeFileSync('screenshot.jpg', buffer);Capture from raw HTML:
const buffer = await snap.capture({
html: '<h1>Hello World</h1><p>Rendered server-side.</p>',
width: 800,
height: 600,
});
fs.writeFileSync('html-capture.png', buffer);Capture from Markdown:
const buffer = await snap.capture({
markdown: '# Invoice\n\n| Item | Price |\n|------|-------|\n| Widget | $9.99 |',
format: 'pdf',
});
fs.writeFileSync('invoice.pdf', buffer);JSON response (metadata + base64):
const result = await snap.capture({
url: 'https://example.com',
responseType: 'json',
});
console.log(result.format); // 'png'
console.log(result.size); // bytes
console.log(result.remainingCredits); // 487
// result.image is a base64 data URIsnap.sign(options) → Promise<SignedUrlResponse>
Generate a signed URL that anyone can use without an API key. Signing is free and does not count against your quota. The signed URL consumes one credit when rendered.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| url | string | | URL to capture (required) |
| expiresIn | number | 3600 | Signature validity in seconds |
| format | 'png' \| 'jpeg' \| 'webp' \| 'pdf' | 'png' | Output format |
| width | number | 1280 | Viewport width |
| height | number | 800 | Viewport height |
| fullPage | boolean | false | Capture full scrollable page |
| quality | number | 90 | JPEG/WebP quality (1-100) |
| delay | number | 0 | Wait ms after page load |
| darkMode | boolean | false | Emulate dark mode |
| blockAds | boolean | true | Block ad networks |
| blockCookieBanners | boolean | true | Remove cookie banners |
| hideSelectors | string | | CSS selectors to hide |
| clickSelector | string | | CSS selector to click before capture |
| device | string | | Device preset |
| userAgent | string | | Custom user agent string |
const signed = await snap.sign({
url: 'https://example.com',
expiresIn: 7200, // 2 hours
format: 'jpeg',
width: 1200,
});
console.log(signed.signedUrl); // Use in <img> tags, share publicly
console.log(signed.expiresAt); // ISO date string
console.log(signed.expiresIn); // seconds remainingUse signed URLs in HTML without exposing your API key:
<img src="https://app.snap-render.com/v1/screenshot/signed?token=..." alt="Preview" />snap.extract(options) → Promise<ExtractResult>
Extract content from a web page. Returns the content in the requested format along with metadata.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| url | string | | URL to extract from (required) |
| type | 'markdown' \| 'text' \| 'html' \| 'article' \| 'links' \| 'metadata' | 'markdown' | Extraction type |
| selector | string | | CSS selector to target a specific element |
| blockAds | boolean | true | Block ads during extraction |
| blockCookieBanners | boolean | true | Remove cookie banners |
| delay | number | 0 | Wait ms after page load |
| maxLength | number | | Truncate content to this many characters |
| cache | boolean | false | Return cached extraction if available. Off by default: set true to enable caching. Cached hits are free but may be stale. |
| cacheTtl | number | 86400 | Cache lifetime in seconds (default 24h). |
// Extract as Markdown
const result = await snap.extract({
url: 'https://example.com/blog/post',
type: 'markdown',
});
console.log(result.content); // Markdown string
console.log(result.wordCount); // 1523
// Extract only links
const links = await snap.extract({
url: 'https://example.com',
type: 'links',
});
console.log(links.content); // Array of { href, text }
// Extract metadata
const meta = await snap.extract({
url: 'https://example.com',
type: 'metadata',
});
console.log(meta.content); // { title, description, ogImage, ... }
// Extract a specific element
const pricing = await snap.extract({
url: 'https://example.com/pricing',
type: 'text',
selector: '.pricing-table',
});snap.batch(options) → Promise<BatchJobResponse>
Submit a batch screenshot job for 1 to 50 URLs. Returns immediately with a job ID. Each URL consumes one credit. Credits for failed URLs are rolled back.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| urls | string[] | | Array of URLs to capture (1-50, required) |
| format | 'png' \| 'jpeg' \| 'webp' \| 'pdf' | 'png' | Output format |
| width | number | 1280 | Viewport width |
| height | number | 800 | Viewport height |
| fullPage | boolean | false | Capture full scrollable page |
| quality | number | 90 | JPEG/WebP quality (1-100) |
| delay | number | 0 | Wait ms after page load |
| darkMode | boolean | false | Emulate dark mode |
| blockAds | boolean | true | Block ad networks |
| blockCookieBanners | boolean | true | Remove cookie banners |
| hideSelectors | string | | CSS selectors to hide |
| clickSelector | string | | CSS selector to click before capture |
| device | string | | Device preset |
| userAgent | string | | Custom user agent string |
const job = await snap.batch({
urls: [
'https://example.com',
'https://example.com/about',
'https://example.com/pricing',
],
format: 'jpeg',
fullPage: true,
});
console.log(job.jobId); // 'batch_abc123'
console.log(job.total); // 3snap.getBatchStatus(jobId) → Promise<BatchJobResponse>
Poll the status of a batch job until it completes.
// Poll until done
let status = await snap.getBatchStatus(job.jobId);
while (status.status === 'pending' || status.status === 'processing') {
await new Promise((r) => setTimeout(r, 2000));
status = await snap.getBatchStatus(job.jobId);
}
console.log(status.completed); // 3
console.log(status.failed); // 0
// Download results
for (const item of status.items) {
if (item.status === 'completed' && item.downloadUrl) {
console.log(`${item.url} -> ${item.downloadUrl}`);
}
}snap.createWebhook(options) → Promise<WebhookResponse>
Create a webhook to receive event notifications. Maximum 5 webhooks per account. Returns the webhook details including its signing secret.
| Option | Type | Description |
|--------|------|-------------|
| url | string | HTTPS endpoint to receive payloads (required) |
| events | string[] | Events to subscribe to (required) |
Available events: screenshot.completed, quota.warning, quota.exceeded
const webhook = await snap.createWebhook({
url: 'https://yourapp.com/webhooks/snaprender',
events: ['screenshot.completed', 'quota.warning'],
});
console.log(webhook.id); // 'wh_abc123'
console.log(webhook.secret); // Save this for signature verificationsnap.listWebhooks() → Promise<WebhookResponse[]>
List all webhooks for your account.
const webhooks = await snap.listWebhooks();
for (const wh of webhooks) {
console.log(`${wh.id}: ${wh.url} (active: ${wh.isActive})`);
}snap.deleteWebhook(webhookId) → Promise<void>
Delete a webhook by ID.
await snap.deleteWebhook('wh_abc123');snap.testWebhook(webhookId) → Promise<WebhookTestResult>
Send a test payload to a webhook endpoint to verify it is receiving deliveries correctly.
const result = await snap.testWebhook('wh_abc123');
console.log(result.success); // true
console.log(result.statusCode); // 200
console.log(result.deliveryId); // 'del_xyz789'SnapRender.verifyWebhookSignature(payload, signature, secret) → boolean
Static method. Verify that a webhook payload was sent by SnapRender using its HMAC-SHA256 signature. Use this in your webhook handler to reject forged requests.
import SnapRender from 'snaprender';
import express from 'express';
const app = express();
app.post('/webhooks/snaprender', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-snaprender-signature'] as string;
const payload = req.body.toString();
const secret = 'whsec_...'; // from createWebhook response
const valid = SnapRender.verifyWebhookSignature(payload, signature, secret);
if (!valid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
console.log('Received event:', event.type);
res.status(200).send('OK');
});snap.info(options) → Promise<CacheInfo>
Check if a screenshot is cached without capturing. Does not consume credits.
| Option | Type | Description |
|--------|------|-------------|
| url | string | URL to check (required) |
| format | string | Output format (must match the cached format) |
| width | number | Viewport width (must match the cached width) |
| height | number | Viewport height (must match the cached height) |
const info = await snap.info({ url: 'https://example.com' });
console.log(info.cached); // true
console.log(info.cachedAt); // '2026-04-09T12:00:00Z'
console.log(info.expiresAt); // '2026-04-10T12:00:00Z'
console.log(info.contentType); // 'image/png'snap.usage() → Promise<UsageResponse>
Get your current month's usage.
const usage = await snap.usage();
console.log(usage.plan); // 'starter'
console.log(usage.used); // 342
console.log(usage.limit); // 2000
console.log(usage.remaining); // 1658
console.log(usage.period); // { start: '2026-04-01', end: '2026-04-30' }snap.usageDaily(days?) → Promise<DailyUsage>
Get daily usage breakdown. Defaults to the last 30 days.
const daily = await snap.usageDaily(7);
for (const day of daily.data) {
console.log(`${day.date}: ${day.count} screenshots`);
}
// 2026-04-03: 48 screenshots
// 2026-04-04: 52 screenshots
// ...Error Handling
All methods throw SnapRenderError on failure with code, status, and message properties.
import SnapRender, { SnapRenderError } from 'snaprender';
try {
await snap.capture({ url: 'https://example.com' });
} catch (err) {
if (err instanceof SnapRenderError) {
console.log(err.code); // 'QUOTA_EXCEEDED'
console.log(err.status); // 429
console.log(err.message); // 'Monthly quota exceeded'
}
}Common error codes:
| Code | Status | Meaning |
|------|--------|---------|
| INVALID_URL | 400 | The URL is malformed or blocked |
| UNAUTHORIZED | 401 | Missing or invalid API key |
| QUOTA_EXCEEDED | 429 | Monthly screenshot limit reached |
| CAPTURE_FAILED | 502 | Screenshot could not be taken |
Links
License
MIT
