Back to blog
Screenshot APIWeb DevelopmentAutomation

What to Look for in a Website Screenshot API (With Examples)

Choosing a website screenshot API? Here are the specific features, edge cases, and code patterns that separate a usable service from a frustrating one.

2026-05-215 min read

Every screenshot API looks the same on the landing page: send a URL, get an image back. The differences only show up once you start hitting real websites — pages with lazy-loaded images, cookie banners, paywalls, login walls, and JavaScript that takes four seconds to settle. This post walks through what actually matters when you pick a website screenshot API, with code you can paste into a project today.

The baseline: what a screenshot API should do

Before comparing services, here's the minimum feature set you should expect from any modern website screenshot API:

  • Multiple output formats — PNG for transparency, JPEG for size, WebP for bandwidth, PDF for documents.
  • Full-page and viewport capture — scroll-to-bottom rendering, not just the fold.
  • Custom viewport sizes — mobile, tablet, retina, and arbitrary widths.
  • Wait conditions — wait for a selector, a network idle state, or a fixed delay.
  • Element-level capture — screenshot a single CSS selector instead of the whole page.
  • Authentication support — cookies, basic auth, custom headers for logged-in views.
  • Caching — don't re-render the same URL on every request.

If a service is missing more than one of these, you'll outgrow it within a month.

The edge cases that break cheap screenshot services

Cookie banners and consent overlays

Half the web hides behind a GDPR overlay. A good screenshot API lets you inject CSS or run a script before capture:

GET https://api.pxshot.dev/v1/screenshot
  ?url=https://example.com
  &css=.cookie-banner{display:none!important}
  &format=png

Without this, every screenshot of a European site shows a giant consent modal.

Lazy-loaded images

Most image-heavy pages defer loading until the user scrolls. If your API captures immediately, you get a page full of placeholder boxes. Look for:

  • An option to scroll the page before capture (triggers IntersectionObserver-based loaders).
  • A wait_until=networkidle setting.
  • A configurable delay (200–3000ms) for stubborn pages.

Fonts that haven't loaded

Custom fonts render asynchronously. If you capture too early, headings flash in Times New Roman. Waiting for document.fonts.ready fixes this — verify your API does it by default.

Animations and video posters

Hero animations that loop forever will produce inconsistent screenshots. The fix is either pausing CSS animations with injected styles or using a deterministic wait selector.

Three real workflows worth automating

1. Dynamic Open Graph images

Instead of hand-designing OG images for every blog post, build a templated HTML page and screenshot it on demand. A typical pattern:

  1. Create a route like /og/[slug] that renders a 1200×630 HTML card with the post title, author, and cover.
  2. In your post metadata, point og:image at a screenshot API call against that route.
  3. Cache the result by URL so the image is generated once and served from a CDN afterwards.

With PxShot, that looks like:

<meta property="og:image"
  content="https://api.pxshot.dev/v1/screenshot?url=https://yoursite.com/og/my-post&width=1200&height=630&format=png" />

2. Visual regression monitoring

Run a daily cron that captures your marketing pages and diffs them against yesterday's image. You'll catch:

  • Broken hero images after a CMS migration.
  • Tracking pixels that crash layout.
  • Third-party scripts pushing your CTA below the fold.

Pair the screenshot API with pixelmatch or odiff for the diff step, and you have visual monitoring for under $20/month.

3. PDF invoices and reports

Render an HTML invoice template, then call the API with format=pdf. You skip the entire headless Chrome maintenance problem — no Puppeteer version pinning, no Docker images bloating to 1.5GB, no random font rendering bugs in serverless environments.

Self-hosted Puppeteer vs. a hosted API

The honest comparison:

Self-hosted (Puppeteer / Playwright)

  • Pros: zero per-request cost, full control over the browser, can run inside a private network.
  • Cons: Chrome updates break things, memory leaks under load, cold starts in Lambda are brutal (3–6s), proxy management is your problem.

Hosted website screenshot API

  • Pros: no infra, instant scaling, built-in caching and CDN delivery, predictable per-image cost.
  • Cons: third-party dependency, rate limits, monthly bill.

Rule of thumb: if you're doing fewer than 100,000 screenshots a month, hosted is almost always cheaper than the engineering hours spent maintaining your own pipeline.

What to test before committing to a provider

Sign up for free tiers and run the same five URLs through each candidate:

  1. A SPA with client-side routing (try a Next.js or React app) — does it wait for hydration?
  2. A page with a large hero video — does the poster render, or do you get a black box?
  3. A site with a cookie wall — can you dismiss it via CSS or script injection?
  4. A logged-in dashboard — does cookie or header auth work end-to-end?
  5. A long article — does full-page capture produce a clean image, or does it crop at 8000px?

Time the response and inspect the output at 100% zoom. You'll quickly see which providers are running outdated Chromium versions or skipping font waits.

Pricing patterns to watch for

  • Per-screenshot pricing — simplest to model, best when traffic is bursty.
  • Credit-based pricing — PDF and full-page often cost more credits than a viewport PNG; read the fine print.
  • Cached vs. fresh — cached hits should be free or near-free. If a provider charges full price for repeat URLs, look elsewhere.
  • Concurrency limits — a $29 plan with 1 concurrent request is useless for batch jobs.

A minimal integration you can ship today

Here's a Node example that generates a screenshot, falls back gracefully, and caches the result URL in your database:

async function getScreenshot(targetUrl) {
  const api = new URL('https://api.pxshot.dev/v1/screenshot');
  api.searchParams.set('url', targetUrl);
  api.searchParams.set('width', '1280');
  api.searchParams.set('format', 'webp');
  api.searchParams.set('full_page', 'true');
  api.searchParams.set('wait_until', 'networkidle');

  const res = await fetch(api, {
    headers: { Authorization: `Bearer ${process.env.PXSHOT_KEY}` }
  });
  if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
  return res.url;
}

That's the whole integration. No browser binaries, no Dockerfile, no memory tuning.

If you want to try the patterns in this post against real pages, PxShot has a free tier at pxshot.dev that covers enough monthly screenshots to prototype OG image generation, visual monitoring, or PDF rendering without a credit card.