API reference

Two endpoints and one idea: give it a URL, get back an image of the page rather than of a consent wall.

Quickstart

# No key needed. Paste this as it is.
curl -X POST https://shots.rendershed.com/v1/render \
  -H "Content-Type: application/json" \
  -d '{"url": "theguardian.com/international"}' \
  -o shot.png

# A free key raises the limit from 20 renders an hour to 100 a month
# and keeps working from any address:
#   -H "X-API-Key: $RENDERSHED_KEY"

The scheme is optional — example.com works, and https is assumed, falling back to http if nothing answers.

Authentication

A key is optional. A call without one runs on the anonymous tier — 20 renders an hour, metered against your address — so every example here works pasted. A key raises the limit and detaches it from where you are calling from. Note the difference: no key is anonymous, a wrong key is rejected.

TierLimitExtras
anonymous20 / hour per IPviewport capture only
free key100 / monthsame features, any address
paid3,000 – 75,000 / monthbase64, retina, longer cache, full page

Server to server, send your key as a header:

X-API-Key: rsk_live_...
Authorization: Bearer rsk_live_...   # either works

For an <img> tag, mint a signed URL instead so no key reaches the browser. The signature covers every parameter, so the URL cannot be edited into a different render, and signed GETs are the only responses a CDN will cache.

curl -X POST https://shots.rendershed.com/v1/sign \
  -H "X-API-Key: $RENDERSHED_KEY" -H "Content-Type: application/json" \
  -d '{"url": "stripe.com", "width": 1200}'

# {"url": "/v1/render?url=...&width=1200&kid=...&sig=...", "plan": "growth"}

Endpoints

MethodPathPurpose
POST/v1/renderRender a page. Key in a header.
GET/v1/renderRender a page from a signed query string. CDN-cacheable.
POST/v1/signMint a signed URL.
GET/healthzService and cache status.

Parameters

NameTypeDefaultDescription
url stringrequiredThe page to render. http and https only.
width int1440Viewport width, 200–3840.
height int900Viewport height, 200–4320.
dpr float1Device pixel ratio, up to 3.
full_page boolfalseCapture the whole document, not just the viewport.
format enumpngpng, jpeg or webp.
quality int1–100, for jpeg and webp.
overlay_mode enumdismissoff, hide or dismiss.
block_ads booltrueDrop known ad and tracker requests.
dark_mode boolfalseRender with prefers-color-scheme: dark.
wait_for_selector stringWait for a CSS selector before capture.
delay_ms int0Extra settle time, up to 10000.
locale stringen-USBrowser locale.
timezone stringUTCIANA timezone.
cache_ttlint86400 Seconds a render may be served from cache. Capped by your plan.
freshboolfalse Bypass the cache for this call. The result is still stored.
responseenumbinary json returns base64 in a JSON envelope. Paid plans.

Overlay removal

overlay_mode decides how hard the renderer works to give you the page instead of what is covering it.

ModeBehaviour
offLeaves the page exactly as it loads.
hideHides known consent containers with CSS. Consents to nothing.
dismissDefault. Hides, plus verified reject clicks, plus a generic modal sweep.

It never clicks “accept”. Where a reject control is really a paid upsell — the Guardian's is labelled “Reject all and subscribe” — it is detected by its own label and skipped in favour of hiding, because clicking it navigates away from the page you asked for.

Responses

By default the body is the image. Ask for JSON when your pipeline needs base64 rather than bytes:

curl -X POST https://shots.rendershed.com/v1/render \
  -H "X-API-Key: $RENDERSHED_KEY" -H "Content-Type: application/json" \
  -d '{"url": "example.com", "response": "json"}'

{
  "image_b64": "iVBORw0KGgoAAAANSUhEUg...",
  "format": "png",
  "bytes": 17883,
  "cached": false,
  "width": 1440,
  "height": 900,
  "ms": 1194
}
HeaderMeaning
X-Rendershed-Cache HIT or MISS. A HIT costs nothing and is not billed.
X-Rendershed-Ms Render time in milliseconds, from the original render.
X-Rendershed-Suppressed Which overlays were removed, or how many were hidden.
Cache-Control Set on signed GET responses so a CDN can store them.

Caching

Renders are cached on the parameters that change pixels. Asking for the same image as JSON instead of binary, or with a different TTL, still hits. A cached render is free and does not count against your quota — not the monthly allowance and not the hourly limit either. It costs us a disk read, so charging for it would be charging for nothing.

Pass fresh: true to bypass the cache for one call. The result is still stored for the next one.

Errors

StatusMeaning
400 Invalid parameters, or a URL that will not be fetched (bad scheme, blocked port, non-public host)
401 No API key, or a signed URL with no signature
402 The plan does not include the requested feature
403 Invalid API key, or a signature that does not match
429 Quota exceeded for the hour
502 The page could not be rendered
503 The render fleet is unreachable
504 The render exceeded its deadline

Errors are JSON with an error field. A page behind bot protection returns a clear failure rather than an image of a challenge screen.

Plans and limits

PlanRendersNotes
free 100 / monthPNG, JPEG, WebP, Overlay removal, Signed URLs, 24-hour cache
starter 3,000 / monthbase64 / JSON response, 7-day cache, Retina (2x) output, Email support
growth 20,000 / month30-day cache, Higher concurrency, Priority queue, MCP server access
scale 75,000 / monthFull-page capture, 4K and 3x output, Overage at $0.001 / render, Self-hosted option

Full-page capture is Scale only: an unbounded document height is an unbounded surface to rasterise, and a long page can cost ten times a viewport capture.

MCP

An agent can call rendershed directly through the Model Context Protocol. The render_url tool returns the image plus a note about what was removed, so the agent knows whether it is looking at the page or at a wall.

https://mcp.rendershed.com/mcp    # streamable HTTP