Automate Website Thumbnails for Your Link Aggregator or Directory

Published: (February 21, 2026 at 05:45 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

If you’re building a link aggregator, directory, or bookmark manager, you probably wonder how to show a preview thumbnail for every link without manually screenshotting them. The solution is to automate the process with a screenshot API.

Screenshot API Example

async function captureThumb(url) {
  const response = await fetch('https://api.rendly.dev/v1/screenshot', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url,
      width: 1280,
      height: 720,
      format: 'webp',
      quality: 80
    })
  });

  if (!response.ok) throw new Error(`Screenshot failed: ${response.status}`);
  return response.json();
}

Queue Processing

When a new URL enters your system, store it in the database and add a job to a thumbnail queue.

async function onNewLink(link) {
  await db.links.create({ url: link.url, title: link.title, thumb: null });
  await thumbnailQueue.add({ linkId: link.id, url: link.url });
}

thumbnailQueue.process(async (job) => {
  const { linkId, url } = job.data;
  const result = await captureThumb(url);
  await db.links.update(linkId, { thumb: result.url });
});

Periodic Refresh

Websites change, so thumbnails can become outdated. Refresh stale thumbnails on a schedule.

async function refreshStaleThumbs() {
  const staleLinks = await db.links.where(
    'thumb_updated_at < ?',
    new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // 30 days
  );

  for (const link of staleLinks) {
    await thumbnailQueue.add({ linkId: link.id, url: link.url, refresh: true });
  }
}

Lazy Loading & Placeholders

Lazy Loading

[Image: ${link.title}]

Fallback Placeholders

function thumbUrl(link) {
  return link.thumb || `/placeholders/${getDomainColor(link.url)}.svg`;
}

Responsive Sizes

Serve different thumbnail sizes for different contexts.

const listThumb = await captureThumb(url, { width: 640, height: 360 });
const detailThumb = await captureThumb(url, { width: 1280, height: 720 });

Use Cases

  • Link aggregators (e.g., Hacker News with previews)
  • Bookmark managers (Raindrop.io, Pocket-style)
  • Web directories (curated lists of tools, resources)
  • Portfolio sites (showing project thumbnails)
  • Competitive analysis dashboards

Scaling Considerations

LinksMonthly RefreshScreenshots / Month
1,0001,0001,000
10,00010,00010,000

Most screenshot APIs, including Rendly, offer tiered pricing that makes this affordable at scale.

Conclusion

Automating thumbnails turns a “nice to have” feature into a reliable part of your product. The pattern is simple:

  1. Capture on ingest.
  2. Queue jobs for scalability.
  3. Refresh periodically.

Your users enjoy richer previews, and you never have to screenshot anything manually again.

0 views
Back to Blog

Related posts

Read more »

Assertions vs pre and post scripts

Simple Assertions vs Pre & Post Requests Many developers already use Assertions in Voiden to validate responses—checking status codes, verifying fields, or ens...