API Reference

OG Screenshot API

Generate a PNG screenshot of any URL using a saved template. The image is returned directly — drop the endpoint URL into any og:image meta tag and you're done.

Endpoint#

GET/api/og/v1/:templateId?url=<targetUrl>

Returns a PNG image of the target URL rendered with the template's configuration.

Authentication#

This endpoint requires no authentication header. The URL itself is the credential — it embeds your template ID, and Linkshot validates the request by checking:

  1. The templateId references an existing template owned by an active subscriber.
  2. The domain of the url query parameter is in your registered domain list (subdomains are allowed automatically).

Keep your template IDs private

Anyone who knows your template ID and a domain you've registered can generate screenshots. Treat template IDs like API keys — don't expose them in public repositories or client-side JavaScript.

Parameters#

ParameterTypeInDescription
templateId*
string (UUID)pathThe UUID of the template to use. Find this in Dashboard → Templates.
url*
string (URL)queryThe fully-qualified URL of the page to screenshot. Must be on a domain you have registered.

Template Configuration#

Each template stores a set of capture settings that control how the screenshot is taken. You configure these in the dashboard — they are not passed as query parameters.

SettingTypeDefaultDescription
viewportWidthinteger1200Browser viewport width in pixels (320–3840).
viewportHeightinteger630Browser viewport height in pixels (200–2160). 1200×630 is the OG standard.
deviceScaleFactorinteger2Pixel density multiplier (1–3). 2 produces retina-quality output.
scrollPositioninteger0Vertical scroll offset in pixels before capturing.
enableTailwindInjectionbooleantrueInject TailwindCSS into the page before capturing. Enables Tailwind classes to render correctly.
waitTimeoutinteger0Additional wait time in milliseconds after page load before capturing (0–3000).
blockPopupsbooleantrueBlock cookie banners and popup overlays before capturing.

Response#

A successful request returns the screenshot directly as a PNG image.

200 OKPNG image of the target page
Response Headers
Content-Type: image/png
Cache-Control: public, s-maxage=600, stale-while-revalidate=600
X-Cache: HIT | MISS

X-Cache header

X-Cache: HIT — the image was served from cache. A background job is regenerating it so the next request gets a fresh copy.

X-Cache: MISS — the image was freshly generated and is now cached.

Caching#

Screenshots are cached keyed by templateId + sha256(url).

  • Cache hit: The cached image is returned immediately. A background job regenerates it so the next request gets a fresh copy.
  • Cache miss: The screenshot is generated synchronously, stored to R2, and returned.
  • TTL: s-maxage=600, stale-while-revalidate=600 (10 minutes).

Example#

Requestbash
curl -X GET \
  "https://uselinkshot.com/api/og/v1/84ed4d78-7ad7-40cb-8abc-dbd122d4da79?url=https%3A%2F%2Fexample.com%2Fblog%2Fmy-post"
Usage in HTMLhtml
<meta property="og:image"
  content="https://uselinkshot.com/api/og/v1/TEMPLATE_ID?url=https://example.com/blog/my-post" />
<meta name="twitter:image"
  content="https://uselinkshot.com/api/og/v1/TEMPLATE_ID?url=https://example.com/blog/my-post" />
<meta name="twitter:card" content="summary_large_image" />

URL-encode the target URL

The url parameter must be URL-encoded (percent-encoded). Use encodeURIComponent() in JavaScript or urllib.parse.quote() in Python. Most OG image meta tag contexts encode automatically.

Error Responses#

All error responses have Content-Type: application/json and include an error field with a machine-readable code.

400 Bad RequestMissing url parameter
Response Bodyjson
{
  "error": "Missing url parameter",
  "message": "Please provide a url query parameter"
}
400 Bad RequestInvalid URL
Response Bodyjson
{
  "error": "Invalid URL",
  "message": "The provided url is not a valid URL"
}
404 Not FoundTemplate not found
Response Bodyjson
{
  "error": "Template not found",
  "message": "The specified template does not exist"
}
403 ForbiddenNo active subscription
Response Bodyjson
{
  "error": "No active subscription",
  "message": "The template owner does not have an active subscription"
}
403 ForbiddenDomain not authorized
Response Bodyjson
{
  "error": "Domain not authorized",
  "message": "The domain \"example.com\" is not in the authorized domain list"
}
500 Internal Server ErrorNavigation timeout — message includes the target URL
Response Bodyjson
{
  "error": "navigation_timeout",
  "message": "Page failed to load within timeout: https://example.com/page"
}
500 Internal Server ErrorScreenshot failed — message includes the error detail
Response Bodyjson
{
  "error": "screenshot_failed",
  "message": "Screenshot generation failed: <error detail>"
}