ScreenshotNeo

BlogHow-to

How to Make Website Thumbnails for a Directory of Indian SaaS Companies

Build consistent website thumbnails for an Indian SaaS directory with Playwright or ScreenshotNeo, plus practical storage, refresh, image-format, and security guidance.

By the ScreenshotNeo team4 October 20269 min read

A website thumbnail is a browser-rendered image of a listing’s URL. For a directory of Indian SaaS companies, generate each thumbnail from the company’s public listing URL, capture the same viewport for every listing, store the result under a stable listing ID, and display all cards at a consistent ratio. Use a full-page capture only when the entire page is useful to readers. You can run the browser yourself with Playwright or use a managed screenshot API such as ScreenshotNeo.

1. Choose the thumbnail framing

Start with the directory card design, then select one capture viewport and one display ratio for every listing. A consistent frame makes entries easier to compare. There is no evidence-backed universal pixel size: derive it from your layout and check the result at the actual card size.

  • Viewport screenshot: a good default for compact directory cards; it gives each company the same visible browser area.
  • Full-page screenshot: useful when visitors need to inspect more than the first screen, but it produces taller images and can make cards harder to scan.
  • Element screenshot: useful when a stable CSS selector identifies a specific part of the page, such as a product preview. Selectors vary across sites and may break when a site changes.

Use the public listing URL as the capture input. Keep the capture dimensions separate from the CSS dimensions used to display it. Save by an immutable listing identifier, not a company name that may be edited later.

2. Generate thumbnails with Playwright

Playwright is a self-hosted option: your application controls navigation and capture, and you operate the browser workers, retries, storage, and refresh schedule. The example below uses Node.js and saves a WebP viewport capture. Install Playwright and its Chromium browser first:

npm install playwright
npx playwright install chromium

Save as capture-thumbnail.mjs. Pass the listing ID and URL as arguments. The script validates basic URL shape, uses a fixed viewport, waits for the page load event and a short settling period, and writes the image to a stable-ID filename.

import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';

const [listingId, rawUrl] = process.argv.slice(2);
if (!listingId || !rawUrl) {
  throw new Error('Usage: node capture-thumbnail.mjs <listing-id> <https-url>');
}
const target = new URL(rawUrl);
if (target.protocol !== 'https:' || !target.hostname) {
  throw new Error('Only valid HTTPS URLs are accepted by this example.');
}

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 1
  });
  page.setDefaultNavigationTimeout(30000);
  await page.goto(target.href, { waitUntil: 'load', timeout: 30000 });
  // Give late layout changes a brief chance to settle. Tune for your sites.
  await page.waitForTimeout(750);
  await mkdir('thumbnails', { recursive: true });
  await page.screenshot({
    path: `thumbnails/${listingId}.webp`,
    type: 'webp',
    fullPage: false
  });
} finally {
  await browser.close();
}

Run it with a stable listing identifier:

node capture-thumbnail.mjs listing_104 https://example.com

This is a starting point, not a complete production security boundary. Do not pass arbitrary submitter URLs directly to a renderer in a privileged network environment; see OWASP’s SSRF Prevention Cheat Sheet.

Playwright capture choices

Need Playwright setting or approach Trade-off
Consistent card image Fixed viewport, fullPage: false May omit content below the fold
Whole page preview fullPage: true Tall output and potentially longer captures
Specific page region Locate an element and call its screenshot method Selector can be absent or unstable across sites
Image bytes for upload Call screenshot without a path and pass the returned buffer to storage code You must handle upload errors and cleanup
Format PNG, JPEG, or WebP screenshot output Compare quality and file size at the card’s real display dimensions

For a selector capture, replace the screenshot call with a locator capture. Check that the selector exists and is visible before taking the screenshot:

const preview = page.locator('main');
await preview.waitFor({ state: 'visible', timeout: 10000 });
await preview.screenshot({ path: `thumbnails/${listingId}.webp`, type: 'webp' });

For full-page output, use fullPage: true in the page screenshot call. A full-page capture can be less comparable when companies have very different page lengths.

3. Store and serve the images

Store images in object storage or another media store suited to your application. Dirstarter documents a pattern that uses a managed screenshot integration and uploads directory media to S3; this is an example integration, not an independent comparison of providers. Keep a database record mapping the listing ID to the current image key and capture metadata.

  • Use a stable key such as thumbnails/{listing_id}.webp, or versioned keys plus a database pointer if you need rollback.
  • Store the source URL, capture time, output format, dimensions, and capture status alongside the image reference.
  • Serve images through your existing media delivery path and set an appropriate cache policy for the URL strategy you choose.
  • Inspect images at the size users actually see; a file that looks good at full resolution may be illegible in a small card.

WebP and AVIF may compress better than PNG or JPEG, but the result depends on the image and delivery context. Check visual quality and browser support for your audience before choosing a default. See web.dev’s guidance on image formats.

4. Schedule captures and refreshes

Capture on listing creation and provide an operator-triggered refresh. Add scheduled refreshes if your directory needs them; choose the interval based on how current the previews must be and the resources you can spend. Websites can change, disappear, redirect, or become parked pages, so a successful browser capture does not prove that it depicts the intended product.

  1. Create a capture job with a listing ID and an approved destination.
  2. Run it in a worker with a strict timeout and resource limits.
  3. Record the outcome, timestamp, final URL if available, and image key.
  4. Review failed captures and suspicious or irrelevant pages rather than blindly replacing a useful existing thumbnail.
  5. Retry transient failures through a controlled queue with a limit, rather than retrying continuously.

Shotpipe describes cached rendering, a fresh=1 option, metadata retrieval, and checks for parked, 404, or unreachable listings. These are vendor-described capabilities; verify the current behavior and terms directly before choosing that provider. Provider features, cache behavior, and quotas can change.

5. Protect the capture service from unsafe URLs

If people can submit company URLs, server-side screenshot generation becomes a security boundary. A renderer that accepts attacker-controlled destinations may be induced to request internal services or other unintended destinations. OWASP advises validating destinations, preferring allowlists where feasible, and constraining network access. It also warns that accepting complete user-supplied URLs is difficult to validate safely.

  • Prefer an allowlist of domains when the expected set of company sites is known.
  • Parse and validate the hostname and scheme; reject unexpected schemes, credentials in URLs, and malformed input.
  • Resolve and check addresses, and account for redirects so a permitted hostname cannot redirect the browser to a forbidden destination.
  • Run capture workers with network egress restrictions so they cannot reach internal services or metadata endpoints.
  • Use strict navigation timeouts, CPU and memory limits, and bounded concurrency.
  • Keep capture jobs separate from the web request that accepts a listing, so a slow or broken company site does not block directory use.

These controls are implementation recommendations based on the SSRF risk and the resource demands of browser navigation. The basic URL check in the sample is not sufficient protection for a public production service.

6. Managed screenshot API option

A managed service handles browser rendering behind an API, while you still decide how to validate listing URLs, store images, display them, and schedule refreshes. Dirstarter documents ScreenshotOne as an integration example for directory screenshots and S3 media handling. The research does not establish comparative provider cost, uptime, or screenshot quality, so check current service terms and controls before selecting one.

Or skip the browser setup

ScreenshotNeo documentation covers its screenshot API and MCP server. One GET request returns an image or PDF; this example requests a WebP capture of a listing URL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Replace the example target with a listing URL and keep your access key on the server. ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, retrieve page information, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan. See ScreenshotNeo for the service and the API documentation for options such as full-page and element capture, viewport and device settings, format, cache TTL, custom CSS, and async jobs.

Sign up for 1,000 free screenshots a month with no card.

7. Troubleshooting

Symptom Likely cause What to do
Navigation times out The site is slow, never settles, or blocks automated browsing Keep a finite timeout, inspect the target separately, and decide whether a controlled retry is warranted. Do not let one listing hold a worker indefinitely.
Image is blank or mostly empty Content may render after the chosen load event, depend on scripts, or be blocked Inspect the page and adjust the wait strategy for that site; record the failure instead of treating every output as valid.
Cookie banner or popup covers the page The target displays an overlay For self-hosted Playwright, handle the page’s consent flow or hide a known overlay only where appropriate. A managed ScreenshotNeo capture removes supported consent platforms, newsletter popups, and chat widgets.
Selector capture fails Selector does not exist, is hidden, or changed Wait for visibility, handle missing selectors, and fall back to a viewport shot when that is acceptable.
Capture looks different from other cards Viewport, device scale, wait timing, or page state differs Use the same viewport and device scale, keep capture policy consistent, and compare at the same rendered card size.
Thumbnail shows a parked or unrelated page Listing URL changed ownership, redirected, expired, or is unavailable Review the final destination and page content, flag the listing for review, and avoid replacing a known-good image automatically.
Worker cannot start Chromium Browser binary is not installed or runtime dependencies are missing Install Playwright’s Chromium browser for the deployed environment and verify that the worker image contains the required browser dependencies.
Unsafe destination is reachable URL validation or network egress controls are inadequate Stop processing the URL, tighten destination checks and redirects handling, and restrict worker network access.

8. Performance, reliability, and cost

Browser navigation is variable because each destination controls its own page size, scripts, and response time. A fixed timeout, bounded worker concurrency, and a queue help isolate slow sites. Full-page screenshots can require more work than viewport shots, while retries add load and may repeat the same persistent failure. Capture only when needed, reuse stored images between refreshes, and avoid regenerating all thumbnails on every directory page request.

There is no source-backed benchmark or total-cost comparison for self-hosted Playwright versus managed APIs in the research. For self-hosting, account for browser-worker compute, operations, storage, image delivery, and engineering time. For a provider, verify current pricing, quotas, retention, cache behavior, failure reporting, and data handling. ScreenshotNeo’s listed plans are Free: 1,000 shots/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free. Only clean shots are billed under the stated product rules; check the documentation for request behavior and billing headers.

9. Practical launch checklist

  • Choose a standard viewport and card ratio based on the directory design.
  • Use listing IDs for storage keys and maintain capture metadata.
  • Choose viewport, element, or full-page capture intentionally.
  • Inspect format and quality at the size readers see.
  • Define capture, refresh, failure review, and bounded retry behavior.
  • Validate untrusted destinations and restrict worker network access.
  • Keep browser work off the directory’s request path.
  • Confirm provider pricing, quotas, cache and data-handling terms before adopting a managed service.

10. FAQ

Should every directory entry use a full-page screenshot?

No. A viewport capture is usually simpler for comparable cards. Use full-page output when the complete page preview adds value to the directory.

Can I save screenshots directly to cloud storage?

Yes. Playwright can return screenshot bytes for an upload step, or write a local file that a worker later uploads. Handle upload failures separately from capture failures.

Is there a single ideal thumbnail size?

The reviewed sources do not establish one. Set dimensions from your directory layout and keep them consistent across entries.

Do screenshots tell me whether a SaaS company is legitimate?

No. A screenshot is a visual snapshot, not verification of ownership, product quality, or business status.