Screenshot API for Next.js: Complete Integration Guide

Published April 2026 • 10 min read • Next.js, React, Integration

Next.js powers a significant share of modern web applications, from marketing sites and SaaS dashboards to full-stack platforms with complex server-side rendering requirements. If your Next.js application needs screenshot, scraping, or PDF generation capabilities, SnapAPI integrates cleanly with both the App Router and Pages Router paradigms. This guide covers Route Handlers, Server Actions, background queue integration, and testing patterns for Next.js applications.

Basic Integration with Route Handlers (App Router)

// app/api/screenshot/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const { url, fullPage = false } = await request.json();
  
  const response = await fetch("https://snapapi.pics/api/screenshot", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.SNAPAPI_KEY!,
    },
    body: JSON.stringify({ url, full_page: fullPage, format: "png" }),
  });
  
  if (!response.ok) {
    return NextResponse.json({ error: "Screenshot failed" }, { status: 500 });
  }
  
  const data = await response.json();
  return NextResponse.json({ screenshotUrl: data.url, capturedAt: data.captured_at });
}

Server Actions for Form-Based Captures

// app/actions/screenshot.ts
"use server";
import { revalidatePath } from "next/cache";

export async function capturePageScreenshot(formData: FormData) {
  const url = formData.get("url") as string;
  
  const response = await fetch("https://snapapi.pics/api/screenshot", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.SNAPAPI_KEY!,
    },
    body: JSON.stringify({ url, format: "png", full_page: true }),
    cache: "no-store",
  });
  
  const { url: screenshotUrl } = await response.json();
  
  // Update database record
  await db.pages.update({ where: { url }, data: { screenshotUrl } });
  revalidatePath("/dashboard");
  
  return { screenshotUrl };
}

Service Class with Caching

// lib/snapapi.ts
import { unstable_cache } from "next/cache";

const SNAPAPI_URL = "https://snapapi.pics";

async function fetchScreenshot(url: string, options: Record = {}) {
  const response = await fetch(`${SNAPAPI_URL}/api/screenshot`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.SNAPAPI_KEY!,
    },
    body: JSON.stringify({ url, format: "png", ...options }),
  });
  
  if (!response.ok) throw new Error(`SnapAPI error: ${response.status}`);
  return response.json();
}

// Cached version using Next.js unstable_cache (15-minute TTL)
export const getCachedScreenshot = unstable_cache(
  async (url: string) => fetchScreenshot(url),
  ["screenshot"],
  { revalidate: 900 } // 15 minutes
);

export async function generatePdf(url: string): Promise {
  const data = await fetchScreenshot(url, { format: "pdf", full_page: true });
  return data.url;
}

export async function extractData(url: string, fields: string[]): Promise> {
  const response = await fetch(`${SNAPAPI_URL}/api/extract`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.SNAPAPI_KEY!,
    },
    body: JSON.stringify({ url, fields }),
  });
  const data = await response.json();
  return data.data || {};
}

Pages Router API Route

// pages/api/screenshot.ts
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST") return res.status(405).end();
  
  const { url } = req.body;
  if (!url) return res.status(400).json({ error: "url required" });
  
  const response = await fetch("https://snapapi.pics/api/screenshot", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.SNAPAPI_KEY!,
    },
    body: JSON.stringify({ url, format: "png" }),
  });
  
  const data = await response.json();
  return res.status(200).json({ screenshotUrl: data.url });
}

OG Image Generation with Next.js

One of the best applications of SnapAPI in Next.js is automated OG image generation for dynamic routes. When a blog post, product page, or profile page is published, trigger a SnapAPI call from your CMS webhook handler to capture the page and store the screenshot as the Open Graph image:

// app/api/webhooks/cms/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const { pageUrl, slug } = await request.json();
  
  // Capture OG image at 1200x630
  const res = await fetch("https://snapapi.pics/api/screenshot", {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-API-Key": process.env.SNAPAPI_KEY! },
    body: JSON.stringify({
      url: pageUrl,
      viewport_width: 1200,
      viewport_height: 630,
      format: "jpeg",
      quality: 90
    }),
  });
  
  const { url: ogImageUrl } = await res.json();
  
  // Store in your CMS or database
  await cms.updatePage({ slug, ogImage: ogImageUrl });
  
  return NextResponse.json({ ogImageUrl });
}

Environment Configuration

# .env.local
SNAPAPI_KEY=your_api_key_here

# .env.example (commit this, not .env.local)
SNAPAPI_KEY=your_snapapi_key_from_dashboard

Testing with Jest and MSW

// __tests__/screenshot.test.ts
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";

const server = setupServer(
  http.post("https://snapapi.pics/api/screenshot", () => {
    return HttpResponse.json({
      url: "https://cdn.snapapi.pics/test/screenshot.png",
      captured_at: "2026-04-04T10:00:00Z",
    });
  })
);

beforeAll(() => server.listen());
afterAll(() => server.close());

test("screenshot route returns url", async () => {
  const res = await fetch("/api/screenshot", {
    method: "POST",
    body: JSON.stringify({ url: "https://example.com" }),
    headers: { "Content-Type": "application/json" },
  });
  const data = await res.json();
  expect(data.screenshotUrl).toContain("snapapi.pics");
});

Getting Started

Create your free SnapAPI account at snapapi.pics/dashboard. Add your API key to your Next.js environment variables and you are ready to integrate. The free plan provides 200 monthly screenshots for development. The TypeScript SDK is available via npm: npm install snapapi-js. Starter plan ($19/month) for 5,000 captures; Growth plan ($79/month) for 50,000 with full extract and scrape access.

Advanced Next.js Patterns for Screenshot APIs

Next.js applications integrating SnapAPI at production scale benefit from several framework-specific optimizations that improve reliability, reduce API quota consumption, and keep screenshot generation costs predictable as traffic grows.

Incremental Static Regeneration provides a natural caching layer for screenshot outputs attached to static or semi-static pages. When a product page, blog post, or listing page is revalidated through ISR, trigger a SnapAPI call to update the cached OG image for that page as part of the revalidation workflow. Store the updated OG image URL in your CMS or database alongside the revalidated page data. This ensures that OG images stay current with page content changes without requiring real-time screenshot generation on every social share event.

Edge Runtime compatibility is worth considering for Next.js applications that run Route Handlers in the Vercel Edge Runtime. SnapAPI calls use the standard fetch API, which is compatible with the Edge Runtime. However, Edge Route Handlers have a 25-second execution timeout, and full-page screenshot captures of complex pages can occasionally approach this limit. For long-running captures, use Node.js runtime for the Route Handler rather than Edge Runtime to benefit from the longer timeout allowance.

Parallel screenshot generation using Promise.all is appropriate for batch capture scenarios, such as generating thumbnails for all products in a category after a bulk import. Implement a concurrency limiter using a library like p-limit to cap simultaneous SnapAPI requests to a reasonable level (three to five concurrent requests) to avoid overwhelming your API quota or triggering rate limiting. The p-limit pattern also makes it straightforward to add retry logic for failed captures without affecting the parallel execution of successful ones.

Middleware integration allows you to intercept requests for screenshot-dependent resources and serve cached versions while triggering background refreshes. A Next.js middleware that detects requests for OG image URLs, checks whether the cached version is still valid, and dispatches a background revalidation task if needed can significantly reduce the latency impact of screenshot generation on page response times. The middleware itself responds immediately from cache while the background task updates the screenshot asynchronously.

The SnapAPI npm package is available as snapapi-js via npm or yarn, providing TypeScript-typed method signatures for all endpoints, automatic retry logic, configurable timeout handling, and response validation. The package works in both Node.js and Edge Runtime contexts. Install with npm install snapapi-js and configure with your API key from snapapi.pics/dashboard. Free plan: 200 captures/month. Starter: $19/month. Growth: $79/month.

SnapAPI at a Glance

SnapAPI provides three core endpoints — screenshot for visual captures and PDF generation, scrape for rendered HTML retrieval, and extract for structured DOM data extraction — accessible via a single REST API key. All endpoints accept a URL and optional configuration parameters including viewport dimensions, custom request headers, cookie values for authenticated captures, wait conditions for JavaScript-heavy pages, and output format specifications. The screenshot endpoint returns a hosted image URL with a capture timestamp. The scrape endpoint returns the fully-rendered HTML after all JavaScript has executed. The extract endpoint accepts CSS selectors or field descriptions and returns matched content as structured JSON. Authentication uses the X-API-Key request header. Rate limits scale with your plan tier. Response times average under three seconds for standard captures, under eight seconds for full-page renders of complex single-page applications. Start free at snapapi.pics — no credit card required for the 200 monthly capture free plan.

Caching Strategies and CDN Integration with SnapAPI

Screenshot freshness is a critical product decision. For marketing pages, screenshots taken once per day are perfectly adequate — users want to see a representative view, not a real-time render. For compliance and audit tooling, you might need hourly or even per-request freshness guarantees. SnapAPI gives you both modes through its caching controls.

By default, SnapAPI caches screenshot results for 24 hours at the CDN edge. Subsequent requests for the same URL return the cached PNG or JPEG instantly, slashing response times from 2–4 seconds to under 50ms. For Next.js ISR pages that themselves update every hour, you can override the cache TTL:

const res = await fetch(
  `https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(target)}&cache_ttl=3600&access_key=${process.env.SNAP_KEY}`
);

Setting cache_ttl=0 forces a fresh render every time — useful for real-time dashboards or pages with personalized content. Setting it to 86400 maximises CDN hit rate and minimises your API quota consumption.

Deploying to Vercel Edge Functions

Vercel Edge Functions run at the CDN edge with sub-millisecond cold starts — but they have a 1MB payload limit and no Node.js native modules. SnapAPI returns binary image data, so streaming the response directly to the client is the correct pattern:

// app/api/snap/route.ts  (Edge Runtime)
export const runtime = 'edge';

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const target = searchParams.get('url') ?? '';
  const snap = await fetch(
    `https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(target)}&format=webp&access_key=${process.env.SNAP_KEY}`
  );
  return new Response(snap.body, {
    headers: { 'Content-Type': 'image/webp', 'Cache-Control': 'public, max-age=3600' }
  });
}

WebP format is recommended for Edge deployments — it's 30–40% smaller than JPEG at equivalent quality, meaning fewer bytes cross the network and your Vercel bandwidth bill shrinks accordingly.

Monitoring and Alerting

Build a lightweight health check that screenshots your own marketing pages nightly and diffs them against a stored baseline. Unexpected layout shifts — caused by a broken CSS import, a missing environment variable, or a third-party widget going dark — show up as pixel-diff anomalies before your users notice. Pair SnapAPI with an image comparison library like pixelmatch and a Slack webhook to get instant alerts when your homepage looks wrong at 3 AM.

This monitoring pattern costs almost nothing: 30 screenshots per night well within the free tier, and the operational confidence it provides is enormous. Start with your most important conversion pages — homepage, pricing, signup — and expand from there as you build confidence in the pipeline.