DEV Community

clause-netizen
clause-netizen

Posted on

Add Website Screenshots to Your App with a Single GET Request

You need a thumbnail of a website. Maybe you're building a bookmarking tool, a link-in-bio dashboard, or a portfolio page that shows previews of client sites. Running your own headless Chromium for this means Docker images north of a gigabyte, memory spikes, and a browser process that wedges itself at 3am.

I ship SiteIntel for exactly this. One GET request in, one PNG out. It also does site enrichment and SEO audits from the same base URL, which turns out to be handy once you have screenshots working.

The screenshot endpoint

Auth is standard RapidAPI headers. The url param must be a full https:// URL, not a bare domain.

curl -o shot.png \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: siteintel.p.rapidapi.com" \
  "https://siteintel.p.rapidapi.com/v1/screenshot?url=https://news.ycombinator.com"
Enter fullscreen mode Exit fullscreen mode

That's it. shot.png is a rendered PNG from headless Chromium. Add &full=true if you want the whole page instead of the viewport.

Same thing in Node with global fetch, no dependencies:

const res = await fetch(
  "https://siteintel.p.rapidapi.com/v1/screenshot?url=" +
    encodeURIComponent("https://news.ycombinator.com"),
  {
    headers: {
      "X-RapidAPI-Key": process.env.RAPIDAPI_KEY,
      "X-RapidAPI-Host": "siteintel.p.rapidapi.com",
    },
  }
);

if (!res.ok) throw new Error(`screenshot failed: ${res.status}`);

const buf = Buffer.from(await res.arrayBuffer());
await import("node:fs/promises").then((fs) => fs.writeFile("shot.png", buf));
Enter fullscreen mode Exit fullscreen mode

Note the encodeURIComponent on the target URL. If you skip it, query strings inside the target URL will get eaten by the outer request and you'll spend twenty minutes wondering why example.com/page?id=5 screenshots the homepage.

Pair it with /v1/analyze for real link previews

A screenshot alone gets you a thumbnail. Combine it with the /v1/analyze endpoint and you get everything a rich link preview needs: title, description, favicon, Open Graph data, even detected tech.

curl -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: siteintel.p.rapidapi.com" \
  "https://siteintel.p.rapidapi.com/v1/analyze?url=https://vercel.com"
Enter fullscreen mode Exit fullscreen mode

Response (trimmed):

{
  "query": "https://vercel.com",
  "final_url": "https://vercel.com/",
  "status": 200,
  "title": "Vercel: Build and deploy the best web experiences...",
  "description": "Vercel gives developers the frameworks...",
  "favicon": "https://vercel.com/favicon.ico",
  "open_graph": {
    "title": "Vercel",
    "image": "https://vercel.com/og.png",
    "site_name": "Vercel",
    "type": "website"
  },
  "detected_tech": ["React", "Next.js"],
  "social_links": ["https://twitter.com/vercel"],
  "emails": [],
  "server": "Vercel"
}
Enter fullscreen mode Exit fullscreen mode

The practical build: a bookmark card component. When a user saves a URL, hit /v1/analyze for the title, description, and open_graph.image. If open_graph.image is missing (plenty of sites skip OG tags), fall back to /v1/screenshot and store the PNG yourself. Your cards never render as a bare blue link again, and you wrote zero browser automation.

const meta = await analyze(url); // GET /v1/analyze
const thumb = meta.open_graph?.image ?? (await screenshot(url)); // GET /v1/screenshot fallback
Enter fullscreen mode Exit fullscreen mode

There's also /v1/seo-audit?url=... if you want an on-page SEO report from the same key, same header setup.

Two things worth knowing

Cache the PNGs. Screenshots of the same URL rarely change minute to minute, so store them in S3 or your own disk keyed by URL and re-fetch on a schedule, not on every page load.

Handle failures. A screenshot of a dead or hanging site returns a non-200 with a JSON error body instead of image bytes, so check res.ok before you treat the response as a PNG. The Node example above does this.


Code and more examples: github.com/clause-netizen/siteintel-api — or grab a managed key on RapidAPI.

Top comments (0)