February 14, 2026 ยท 11 min read

Website Thumbnail Generator API: Generate Website Previews at Scale

Browser mockup showing a website screenshot being captured

Website thumbnails are everywhere โ€” link previews in chat apps, directory listings, portfolio showcases, search results, and bookmarking tools. Generating these website preview images at scale requires a reliable thumbnail generator API that can handle thousands of URLs without breaking a sweat.

In this guide, we'll show you how to build a website thumbnail generation system using SnapAPI, from basic captures to advanced caching and optimization strategies.

๐Ÿ–ผ๏ธ Generate Thumbnails Instantly

200 free thumbnails/month. Multiple sizes and formats. No credit card required.

Get Free API Key โ†’

What Are Website Thumbnails?

A website thumbnail is a small preview image of a webpage. Think of the preview cards you see when sharing a link on Slack, Twitter, or LinkedIn. These visual previews help users decide whether to click through, dramatically improving engagement rates.

Common use cases include:

Quick Start: Generate a Website Thumbnail

Generate a thumbnail of any website with a single API call:

curl "https://api.snapapi.pics/v1/screenshot?\
url=https://github.com&\
width=1280&\
height=800&\
format=webp&\
quality=80" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o github-thumbnail.webp

This captures a 1280x800 viewport screenshot in WebP format. For thumbnails, WebP offers the best quality-to-size ratio, typically 30-50% smaller than JPEG at equivalent quality.

Generating Smaller Thumbnails

For directory listings and grids, you often need smaller images. Use the width parameter to control the output size:

# Small thumbnail (400px wide)
curl "https://api.snapapi.pics/v1/screenshot?\
url=https://github.com&\
width=400&\
height=300&\
format=webp&\
quality=75" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o thumbnail-small.webp

# Medium thumbnail (800px wide)
curl "https://api.snapapi.pics/v1/screenshot?\
url=https://github.com&\
width=800&\
height=600&\
format=webp&\
quality=80" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o thumbnail-medium.webp

Building a Thumbnail Generation Service

Let's build a complete thumbnail service that generates, caches, and serves website previews. This is the pattern used by apps like Notion, Raindrop.io, and Pocket.

Node.js Thumbnail Service

const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

const app = express();
const CACHE_DIR = './thumbnails';
const SNAPAPI_KEY = process.env.SNAPAPI_KEY;

// Ensure cache directory exists
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });

// Generate a cache key from URL and size
function cacheKey(url, width, height) {
  const hash = crypto.createHash('md5').update(`${url}-${width}-${height}`).digest('hex');
  return path.join(CACHE_DIR, `${hash}.webp`);
}

// Thumbnail endpoint
app.get('/thumbnail', async (req, res) => {
  const { url, width = 800, height = 600 } = req.query;
  
  if (!url) return res.status(400).json({ error: 'URL required' });
  
  const cachePath = cacheKey(url, width, height);
  
  // Serve from cache if available
  if (fs.existsSync(cachePath)) {
    const stat = fs.statSync(cachePath);
    const age = Date.now() - stat.mtimeMs;
    
    // Cache valid for 24 hours
    if (age < 24 * 60 * 60 * 1000) {
      res.setHeader('Content-Type', 'image/webp');
      res.setHeader('Cache-Control', 'public, max-age=86400');
      res.setHeader('X-Cache', 'HIT');
      return fs.createReadStream(cachePath).pipe(res);
    }
  }
  
  // Generate new thumbnail
  try {
    const response = await fetch(
      `https://api.snapapi.pics/v1/screenshot?` +
      `url=${encodeURIComponent(url)}&width=${width}&height=${height}` +
      `&format=webp&quality=80&block_ads=true`,
      { headers: { 'Authorization': `Bearer ${SNAPAPI_KEY}` } }
    );
    
    if (!response.ok) throw new Error(`API returned ${response.status}`);
    
    const buffer = Buffer.from(await response.arrayBuffer());
    
    // Save to cache
    fs.writeFileSync(cachePath, buffer);
    
    res.setHeader('Content-Type', 'image/webp');
    res.setHeader('Cache-Control', 'public, max-age=86400');
    res.setHeader('X-Cache', 'MISS');
    res.send(buffer);
  } catch (error) {
    console.error('Thumbnail generation failed:', error.message);
    res.status(500).json({ error: 'Failed to generate thumbnail' });
  }
});

app.listen(3000, () => console.log('Thumbnail service running on :3000'));

This service generates thumbnails on-demand, caches them for 24 hours, and serves cached versions for repeat requests. In production, you'd want to replace the filesystem cache with S3 or a CDN.

Python Thumbnail Service

import hashlib
import os
import time
from flask import Flask, request, send_file, jsonify
import requests
import io

app = Flask(__name__)
CACHE_DIR = './thumbnails'
SNAPAPI_KEY = os.environ['SNAPAPI_KEY']
CACHE_TTL = 86400  # 24 hours

os.makedirs(CACHE_DIR, exist_ok=True)

def cache_path(url, width, height):
    key = hashlib.md5(f"{url}-{width}-{height}".encode()).hexdigest()
    return os.path.join(CACHE_DIR, f"{key}.webp")

@app.route('/thumbnail')
def thumbnail():
    url = request.args.get('url')
    width = request.args.get('width', 800, type=int)
    height = request.args.get('height', 600, type=int)
    
    if not url:
        return jsonify(error='URL required'), 400
    
    path = cache_path(url, width, height)
    
    # Check cache
    if os.path.exists(path):
        age = time.time() - os.path.getmtime(path)
        if age < CACHE_TTL:
            return send_file(path, mimetype='image/webp')
    
    # Generate thumbnail
    response = requests.get(
        'https://api.snapapi.pics/v1/screenshot',
        params={
            'url': url, 'width': width, 'height': height,
            'format': 'webp', 'quality': 80, 'block_ads': True
        },
        headers={'Authorization': f'Bearer {SNAPAPI_KEY}'}
    )
    
    if response.status_code != 200:
        return jsonify(error='Generation failed'), 500
    
    # Cache and serve
    with open(path, 'wb') as f:
        f.write(response.content)
    
    return send_file(io.BytesIO(response.content), mimetype='image/webp')

Optimization Strategies

1. Choose the Right Format

SnapAPI supports PNG, JPEG, WebP, and AVIF formats. For thumbnails, the choice matters:

2. Viewport Size Strategy

The viewport you capture at affects both quality and cost. Here's a recommended strategy:

3. Block Ads and Cookie Banners

Nothing ruins a thumbnail like a cookie consent popup covering half the page. Use SnapAPI's built-in ad and cookie banner blocking:

curl "https://api.snapapi.pics/v1/screenshot?\
url=https://example.com&\
block_ads=true&\
block_cookie_banners=true&\
width=1280&height=800&format=webp" \
  -H "Authorization: Bearer YOUR_API_KEY" -o clean-thumbnail.webp

4. Dark Mode Thumbnails

Many websites support dark mode. Generate dark mode thumbnails for consistency with your own dark-themed UI:

curl "https://api.snapapi.pics/v1/screenshot?\
url=https://github.com&\
dark_mode=true&\
width=1280&height=800&format=webp" \
  -H "Authorization: Bearer YOUR_API_KEY" -o dark-thumbnail.webp

5. Caching with CDN

For high-traffic applications, put your thumbnails behind a CDN. Generate once, serve millions of times:

// Upload to S3 after generation
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({ region: 'us-east-1' });

async function generateAndCache(url) {
  const key = `thumbnails/${md5(url)}.webp`;
  
  // Check if already cached in S3
  try {
    await s3.send(new HeadObjectCommand({ Bucket: 'my-thumbnails', Key: key }));
    return `https://cdn.example.com/${key}`;  // Already cached
  } catch {}
  
  // Generate via SnapAPI
  const response = await fetch(
    `https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(url)}&format=webp&width=800&height=600`,
    { headers: { 'Authorization': `Bearer ${SNAPAPI_KEY}` } }
  );
  
  // Upload to S3
  await s3.send(new PutObjectCommand({
    Bucket: 'my-thumbnails',
    Key: key,
    Body: Buffer.from(await response.arrayBuffer()),
    ContentType: 'image/webp',
    CacheControl: 'public, max-age=604800'  // 7 days
  }));
  
  return `https://cdn.example.com/${key}`;
}

Batch Thumbnail Generation

Need to generate thumbnails for hundreds of URLs at once? Here's a batch processing pattern:

// Batch generate with concurrency control
const pLimit = require('p-limit');
const limit = pLimit(10);  // 10 concurrent requests

const urls = [
  'https://github.com',
  'https://stackoverflow.com',
  'https://dev.to',
  // ... hundreds more
];

const results = await Promise.allSettled(
  urls.map(url => limit(async () => {
    const response = await fetch(
      `https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(url)}&format=webp&width=800&height=600&quality=80`,
      { headers: { 'Authorization': `Bearer ${SNAPAPI_KEY}` } }
    );
    
    if (!response.ok) throw new Error(`Failed: ${url}`);
    
    const filename = `thumbnails/${url.replace(/[^a-z0-9]/gi, '_')}.webp`;
    fs.writeFileSync(filename, Buffer.from(await response.arrayBuffer()));
    
    return { url, filename, status: 'success' };
  }))
);

const succeeded = results.filter(r => r.status === 'fulfilled').length;
console.log(`Generated ${succeeded}/${urls.length} thumbnails`);

SnapAPI supports unlimited concurrent requests on all plans, so you can parallelize as aggressively as your application needs.

Building a link preview feature like Slack or Notion? Here's the full flow:

// 1. User pastes a URL in your app
// 2. Your backend generates a preview

async function generateLinkPreview(url) {
  // Fetch metadata (title, description) via SnapAPI extract
  const metaResponse = await fetch(
    `https://api.snapapi.pics/v1/extract?url=${encodeURIComponent(url)}`,
    { headers: { 'Authorization': `Bearer ${SNAPAPI_KEY}` } }
  );
  const metadata = await metaResponse.json();
  
  // Generate thumbnail
  const imgResponse = await fetch(
    `https://api.snapapi.pics/v1/screenshot?url=${encodeURIComponent(url)}&format=webp&width=800&height=600&quality=80&block_ads=true`,
    { headers: { 'Authorization': `Bearer ${SNAPAPI_KEY}` } }
  );
  const thumbnail = Buffer.from(await imgResponse.arrayBuffer());
  
  return {
    url,
    title: metadata.title,
    description: metadata.description,
    thumbnail: thumbnail,  // Save to storage and return URL
    favicon: metadata.favicon
  };
}

This combines SnapAPI's content extraction with screenshot generation for a complete link preview solution.

Comparison: SnapAPI vs. Other Thumbnail Services

Why choose SnapAPI for thumbnail generation? See our full screenshot API comparison for details, but here are the highlights for thumbnail-specific use:

๐Ÿš€ Start Generating Website Thumbnails

Fast, reliable, and affordable. 200 free thumbnails/month. Multiple formats including AVIF.

Get Free API Key โ†’

Frequently Asked Questions

How do I generate a website thumbnail?

Send a GET request to SnapAPI with the target URL and desired dimensions. The API returns a screenshot image you can use as a thumbnail. No server setup or browser installation required.

What size should website thumbnails be?

For most use cases, capture at 1280x800 and display at 640x400 (retina-ready). For smaller grid thumbnails, 400x300 works well. For social sharing cards, use the OG standard of 1200x630.

Can I generate thumbnails in bulk?

Yes. SnapAPI supports unlimited concurrent requests on all plans. Use a concurrency limiter (like p-limit) to process hundreds of URLs in parallel.

How often should I refresh thumbnails?

It depends on your use case. For directories and bookmarks, weekly refreshes are typically sufficient. For monitoring or news sites, daily or even hourly updates may be appropriate.

What's the best image format for thumbnails?

WebP offers the best balance of quality and file size for most applications. AVIF provides even smaller files but is slightly slower to encode. Both are supported by SnapAPI.

Last updated: February 14, 2026

Related Reading