Menu
typescriptnodejsscreenshot-apitutorial

Screenshot API in TypeScript: A Complete Developer Guide

SnapSharp Team·April 16, 2026·9 min read

Taking screenshots from TypeScript is straightforward with the right API client. This guide covers everything from installation to production-ready patterns: type-safe requests, error handling, caching, batch processing, and async jobs.

Why use an API instead of Playwright?

Playwright is the right tool when you own the browser and need full control. It's wrong for most screenshot use cases because:

  • Infrastructure overhead: running headless Chromium in production needs memory (1–2GB per browser), CPU, and careful process management
  • Scaling is hard: handling concurrent requests without browser pool exhaustion requires non-trivial code
  • Cold starts: each new Playwright instance takes 2–5 seconds to initialize
  • Maintenance: browser security patches, OS compatibility, Docker image updates

A screenshot API outsources all of this. Your TypeScript code makes an HTTP call and gets back a Buffer. The API handles the Chromium cluster.

Installation

npm install @snapsharp/sdk
# or
pnpm add @snapsharp/sdk

Basic screenshot

import SnapSharp from '@snapsharp/sdk';
import { writeFile } from 'fs/promises';

const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);

const image = await client.screenshot({
  url: 'https://example.com',
  width: 1280,
  height: 720,
  format: 'png',
});

await writeFile('screenshot.png', image);

image is a Buffer. Write it to disk, upload to S3, serve as an HTTP response, or pass it directly to an image processing pipeline.

Type-safe parameters

The SDK is fully typed. All parameters map to the TypeScript interface:

import SnapSharp, { ScreenshotParams } from '@snapsharp/sdk';

const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);

const params: ScreenshotParams = {
  url: 'https://example.com',
  width: 1280,              // 320–3840
  height: 720,              // 240–2160
  format: 'png',            // 'png' | 'jpeg' | 'webp' | 'pdf'
  quality: 90,              // 1–100 (jpeg/webp only)
  fullPage: true,           // capture full scroll height
  retina: true,             // 2x device pixel ratio
  darkMode: false,
  delay: 1000,              // wait N ms after page load
  waitFor: '#app-loaded',   // CSS selector to wait for
  blockAds: true,
  blockCookieBanners: true,
  cache: true,              // Redis cache (TTL 1 hour)
};

const image = await client.screenshot(params);

Raw HTTP (no SDK)

If you prefer plain fetch, the API is a simple GET request:

const apiKey = process.env.SNAPSHARP_API_KEY!;

async function screenshot(url: string): Promise<Buffer> {
  const params = new URLSearchParams({
    url,
    width: '1280',
    format: 'png',
    cache: 'true',
  });

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

  if (!res.ok) {
    const error = await res.json() as { error: string; message: string };
    throw new Error(`SnapSharp error ${res.status}: ${error.message}`);
  }

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

Error handling

The API returns structured error responses. Handle them specifically:

import SnapSharp, { SnapSharpError } from '@snapsharp/sdk';

const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);

async function safeScreenshot(url: string): Promise<Buffer | null> {
  try {
    return await client.screenshot({ url, width: 1280 });
  } catch (err) {
    if (err instanceof SnapSharpError) {
      switch (err.code) {
        case 'rate_limit_exceeded':
          // Back off and retry after err.retryAfter seconds
          await new Promise(r => setTimeout(r, (err.retryAfter ?? 60) * 1000));
          return safeScreenshot(url);

        case 'ssrf_blocked':
          // URL resolved to an internal/private IP — don't retry
          console.warn(`SSRF blocked: ${url}`);
          return null;

        case 'plan_required':
          console.error(`Feature requires ${err.requiredPlan} plan`);
          return null;

        case 'screenshot_timeout':
          console.warn(`Page timed out: ${url}`);
          return null;

        default:
          console.error(`Screenshot failed: ${err.message} (${err.code})`);
          return null;
      }
    }
    throw err; // Re-throw unexpected errors
  }
}

Serving screenshots as HTTP responses

In a Next.js API route or Express handler:

// app/api/screenshot/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server';
import SnapSharp from '@snapsharp/sdk';

const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);

export async function GET(req: NextRequest) {
  const url = req.nextUrl.searchParams.get('url');
  if (!url) {
    return NextResponse.json({ error: 'Missing url parameter' }, { status: 400 });
  }

  try {
    const image = await client.screenshot({
      url,
      width: 1280,
      format: 'png',
      cache: true,
    });

    return new NextResponse(image, {
      headers: {
        'Content-Type': 'image/png',
        'Cache-Control': 'public, max-age=3600, s-maxage=3600',
      },
    });
  } catch {
    return NextResponse.json({ error: 'Screenshot failed' }, { status: 500 });
  }
}
// Express
import express from 'express';
import SnapSharp from '@snapsharp/sdk';

const app = express();
const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);

app.get('/screenshot', async (req, res) => {
  const { url } = req.query as { url: string };
  if (!url) return res.status(400).json({ error: 'Missing url' });

  const image = await client.screenshot({ url, width: 1280, format: 'png', cache: true });
  res.set('Content-Type', 'image/png');
  res.set('Cache-Control', 'public, max-age=3600');
  res.send(image);
});

Batch screenshots

Screenshot multiple URLs in one call (up to 100):

const results = await client.batch([
  { url: 'https://example.com/page-1', format: 'png', width: 1280 },
  { url: 'https://example.com/page-2', format: 'png', width: 1280 },
  { url: 'https://example.com/page-3', format: 'png', width: 1280 },
]);

for (const result of results) {
  if (result.status === 'success') {
    await writeFile(`screenshots/${result.filename}`, result.image);
  } else {
    console.error(`Failed: ${result.url} — ${result.error}`);
  }
}

Async screenshots

For large batches or long-running pages, submit an async job:

const job = await client.asyncScreenshot({
  url: 'https://heavy-page.com',
  fullPage: true,
  format: 'pdf',
  callbackUrl: 'https://yourapi.com/webhooks/screenshot',
});

console.log(`Job submitted: ${job.id}`);

// Option 1: poll for completion
let result = await client.getJob(job.id);
while (result.status === 'pending' || result.status === 'processing') {
  await new Promise(r => setTimeout(r, 2000));
  result = await client.getJob(job.id);
}

if (result.status === 'completed') {
  const image = Buffer.from(result.result.data, 'base64');
  await writeFile('output.pdf', image);
}

// Option 2: receive webhook when done (preferred for production)
// POST to your callbackUrl with {jobId, status, result}

Direct S3 upload

Skip the round-trip — upload directly from SnapSharp to your S3 bucket:

// Configure S3 credentials in dashboard (Settings → Storage)
// Then pass upload_to_s3: true

const result = await client.screenshot({
  url: 'https://example.com',
  width: 1280,
  uploadToS3: true,
});

// result.s3Url → 'https://your-bucket.s3.amazonaws.com/screenshots/...'
console.log('Uploaded:', result.s3Url);

Caching

Screenshots are cached in Redis. Identical requests return in ~50ms:

// First request: ~1–3 seconds (real browser render)
const image1 = await client.screenshot({
  url: 'https://example.com',
  cache: true,
  cacheTtl: 3600, // cache for 1 hour
});

// Second request: ~50ms (Redis cache hit)
const image2 = await client.screenshot({
  url: 'https://example.com',
  cache: true,
});

Check the X-Cache header to see if a request was cached:

// With raw fetch, check headers
const res = await fetch('https://api.snapsharp.dev/v1/screenshot?url=...', {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const cached = res.headers.get('x-cache'); // 'HIT' or 'MISS'
const responseTime = res.headers.get('x-response-time'); // e.g. '47ms'

TypeScript config

The SDK requires "moduleResolution": "bundler" or "node16" in your tsconfig:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true
  }
}

For ESM projects with "type": "module" in package.json, imports work as-is. For CommonJS:

// CommonJS
const SnapSharp = require('@snapsharp/sdk').default;
const client = new SnapSharp(process.env.SNAPSHARP_API_KEY);

Getting started

# 1. Get a free API key
# Sign up at snapsharp.dev/sign-up

# 2. Install the SDK
npm install @snapsharp/sdk

# 3. Set your API key
export SNAPSHARP_API_KEY=sk_live_your_key_here

# 4. Take your first screenshot
npx ts-node -e "
import SnapSharp from '@snapsharp/sdk';
import { writeFile } from 'fs/promises';
const client = new SnapSharp(process.env.SNAPSHARP_API_KEY!);
client.screenshot({ url: 'https://example.com', width: 1280 })
  .then(img => writeFile('test.png', img))
  .then(() => console.log('Done!'));
"

Free tier: 100 screenshots/month, no credit card. See the full API reference for all parameters.

Screenshot API in TypeScript: A Complete Developer Guide — SnapSharp Blog