Screenshot API for Node.js

Add website screenshot capture to your Node.js application with just a few lines of code. Works with fetch, axios, or any HTTP client.

Quick Start with Fetch

Node.js 18+ includes a built-in fetch API, making SnapAPI integration trivial with zero dependencies:

const API_KEY = process.env.SNAPAPI_KEY;

async function screenshot(url, opts = {}) {
  const params = new URLSearchParams({
    url,
    format: opts.format || 'png',
    width: String(opts.width || 1280),
    height: String(opts.height || 800),
  });
  if (opts.fullPage) params.set('full_page', 'true');
  if (opts.darkMode) params.set('dark_mode', 'true');

  const res = await fetch(
    `https://apisnap.dev/api/screenshot?${params}`,
    { headers: { 'Authorization': `Bearer ${API_KEY}` } }
  );

  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.error || 'Screenshot failed');
  }

  return Buffer.from(await res.arrayBuffer());
}

// Usage
const image = await screenshot('https://github.com');
fs.writeFileSync('github.png', image);

Using Axios

If your project already uses axios, the integration is equally straightforward:

import axios from 'axios';
import fs from 'fs';

const snap = axios.create({
  baseURL: 'https://apisnap.dev/api',
  headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` },
  responseType: 'arraybuffer',
});

const { data } = await snap.get('/screenshot', {
  params: { url: 'https://stripe.com', format: 'jpeg', quality: 85 }
});

fs.writeFileSync('stripe.jpg', data);
console.log('Remaining:', res.headers['x-usage-remaining']);

The x-usage-remaining header tells you how many screenshots remain in your monthly quota.

Start Capturing Screenshots Today

100 free screenshots per month. No credit card required. API key delivered instantly.

Get Started Free

Express Integration

Build a screenshot proxy endpoint in your Express app to generate screenshots on demand:

import express from 'express';

const app = express();

app.get('/preview', async (req, res) => {
  const { url } = req.query;
  if (!url) return res.status(400).json({ error: 'URL required' });

  try {
    const ssRes = await fetch(
      `https://apisnap.dev/api/screenshot?url=${encodeURIComponent(url)}&format=png&width=1200&height=630`,
      { headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
    );

    if (!ssRes.ok) throw new Error('Screenshot failed');

    res.set('Content-Type', 'image/png');
    res.set('Cache-Control', 'public, max-age=86400');
    res.send(Buffer.from(await ssRes.arrayBuffer()));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(3000);

This creates a /preview?url=... endpoint that caches screenshots for 24 hours via Cache-Control headers.

Error Handling Best Practices

Robust error handling is essential for production screenshot integrations:

async function screenshotWithRetry(url, retries = 2) {
  for (let i = 0; i <= retries; i++) {
    try {
      const res = await fetch(
        `https://apisnap.dev/api/screenshot?url=${encodeURIComponent(url)}`,
        { headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
      );

      if (res.status === 429) {
        console.warn('Quota exceeded');
        return null; // Handle gracefully
      }

      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return Buffer.from(await res.arrayBuffer());
    } catch (err) {
      if (i === retries) throw err;
      await new Promise(r => setTimeout(r, 1000 * (i + 1)));
    }
  }
}

Always check for 429 (quota exceeded) separately from other errors. Failed requests (4xx, 5xx) don't count against your quota.

Frequently Asked Questions

Node.js 18+ for built-in fetch. For older versions, use node-fetch or axios.
Absolutely. The API returns binary data (Buffer/ArrayBuffer), which TypeScript handles natively. No SDK or type definitions needed.
For full-page screenshots of long pages, use streams instead of buffering the entire response. Pipe directly to file or S3 upload.
Not yet. The REST API is simple enough that a plain fetch/axios call works perfectly. An SDK is on the roadmap.