When someone pastes a link into a chat, a comment box, a CMS or a notes app, users expect a card with the title, a description, an icon and an image. Building that yourself means fetching untrusted URLs from your servers, parsing HTML, running JavaScript for single-page apps and handling the many pages without an og:image. One request returns all of it.
Everything for the card in one request
Ask for JSON with the metadata you need. The screenshot doubles as the fallback image:
curl -X POST "https://shotkit.net/api/take" \
-H "X-Access-Key: YOUR_ACCESS_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://github.com","response_type":"json","metadata_page_title":true,"metadata_open_graph":true,"metadata_icon":true,"viewport_width":1200,"viewport_height":630,"image_width":600,"format":"jpg","block_cookie_banners":true,"cache":true,"cache_ttl":604800}' \
--fail-with-body -o shot.json{
"screenshot_url": "https://shotkit.net/cdn/files/…/….jpg",
"metadata": {
"page_title": "GitHub · Build and ship software on a single, collaborative platform",
"icon": { "url": "https://github.githubassets.com/favicons/favicon.svg", "type": "image/svg+xml" },
"open_graph": { "title": "GitHub", "description": "Join the world's most widely adopted…", "image": "https://github.githubassets.com/…", "type": "website" }
}
}const res = await fetch("https://shotkit.net/api/take", {
method: "POST",
headers: { "X-Access-Key": process.env.SHOTKIT_ACCESS_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ url, response_type: "json", metadata_page_title: true, metadata_open_graph: true, metadata_icon: true, image_width: 600, cache: true }),
});
const { screenshot_url, metadata } = await res.json();
const og = metadata.open_graph ?? {};
const preview = {
title: og.title ?? metadata.page_title,
description: og.description ?? null,
image: og.image ?? screenshot_url, // a real picture of the page when there's no og:image
icon: metadata.icon?.url ?? null,
};Copy the screenshot to your own storage if previews must last longer than cache_ttl.
Why render instead of fetch
- Single-page apps. Many sites set their title and meta tags with JavaScript. A plain HTTP fetch sees an empty shell; a browser sees what users see.
- Safety. Fetching user-submitted URLs from your own servers invites server-side request forgery against your internal network. Here the page is loaded on shotkit's infrastructure, not yours.
- A guaranteed image. Plenty of pages have no
og:image, or a broken one. A screenshot always shows something real.
Frequently asked questions
Does it follow redirects and short links?
Yes. The page is loaded like a browser loads it, so redirects and URL shorteners resolve to the final page.
How fast is it?
Usually a few seconds for the first request, depending on the site. With cache=true, repeat previews of the same URL return immediately and are free.
Can I get the page text too, for a longer summary?
Yes. Add metadata_content: true with metadata_content_format: "markdown" and you get a URL to the page's content as Markdown, ready to summarize. See website to Markdown.