Documentation

The comprehensive guide to integrating the len.sh screenshot API into your development workflow.


Quick Start Guide

1

Get your API Key

Create an account at the signup page. Your unique API key is shown once — store it securely.

2

Make your first request

Call the /v1/screenshot endpoint with the url parameter.

3

Get your screenshot

The API returns the raw image binary. Use the endpoint URL directly as an img src or save to a file.


Authentication

All API requests (except /v1/health) require an API key. Two methods:

HTTP Header (Recommended)

Authorization: Bearer <key>

Query Parameter

?access_key=YOUR_API_KEY

Signed URLs

Signed URLs let you embed screenshots in client-side contexts — img tags, emails, social cards — without exposing your API key. Instead of authenticating with an API key, requests are verified using an HMAC-SHA256 signature computed with a separate signing secret.

How It Works

1

Collect all query parameters except signature.

2

Sort parameter names alphabetically.

3

Build the canonical string: key1=value1&key2=value2&... (use raw values, not URL-encoded).

4

Compute HMAC-SHA256(signing_secret, canonical_string) and output as hex.

5

Append &signature={hex} to the URL.

Example Signed URL

https://api.len.sh/v1/screenshot?key_id=abc123&url=https://example.com&signature=a1b2c3d4...

Security Notes

  • check_circle The signing secret is separate from your API key
  • check_circle The signing secret can be rotated independently
  • check_circle Signed URLs do not expire (designed for static img tags)
  • check_circle Rate limiting and monthly quotas still apply

All official SDKs include a signUrl helper that implements this algorithm for you.


Parameter Reference

All parameters work on both GET (query string) and POST (JSON body).

Parameter Type Default Description
url string Target URL to capture. Required*
html string Raw HTML to render (use POST). Required*
format enum png Output format: png, jpeg, webp
width int 1280 Viewport width in pixels (1–3840).
height int 720 Viewport height in pixels (1–2160).
quality int 80 JPEG/WebP quality (1–100, ignored for PNG).
full_page bool false Capture the entire scrollable page.
selector string CSS selector to capture a specific element.
delay int 0 Wait N ms before capture (0–10000).
wait_until enum load load, domcontentloaded, networkidle0, networkidle2
timeout int 15000 Max wait time in ms (1–30000).
transparent bool false Transparent background (PNG/WebP only).
user_agent string Custom user agent string.
device_scale number 1 Device scale factor (1–3) for retina.
device string Device emulation preset (e.g. iphone-15, desktop-1080p). Sets width, height, scale, and user agent.
block_ads bool false Hide common ad elements.
block_cookie_banners bool false Hide cookie consent banners.
block_popups bool false Hide marketing popups (newsletters, spin wheels, exit-intent overlays).
js string Custom JavaScript to inject after page load (max 10KB).
css string Custom CSS to inject (max 10KB).
cache_ttl int 86400 Cache duration in seconds (0 = bypass, max 2592000).
response_type enum image image (raw bytes) or json (metadata).

*One of url or html is required.

Device Emulation Presets

Use the device parameter to emulate a specific device. This sets width, height, device scale, and user agent automatically. Explicit parameters override preset values.

Device Value Width Height Scale
iPhone 15 iphone-15 393 852 3x
iPhone 15 Pro Max iphone-15-pro-max 430 932 3x
iPad Pro ipad-pro 1024 1366 2x
Pixel 8 pixel-8 412 915 2.625x
Samsung Galaxy S24 samsung-s24 360 780 3x
MacBook Pro 16" macbook-pro-16 1728 1117 2x
Desktop 1080p desktop-1080p 1920 1080 1x
Desktop 4K desktop-4k 3840 2160 1x

OG Image Generation

Generate branded Open Graph images (1200×630) from structured parameters. Uses the same authentication as the screenshot endpoint.

POST /v1/og

Parameters

Parameter Type Default Description
title string Main heading (max 200 chars, clamped to 3 lines). Required
subtitle string Secondary text (max 300 chars).
badge string Pill badge text e.g. "12 items" (max 50 chars).
url string URL displayed in footer (max 200 chars).
brand_name string Brand name in footer (max 100 chars).
brand_color string #4850e5 Accent color, hex format.
theme enum dark dark or light.
format enum png Output format: png, jpeg, webp.
quality int 90 Image quality (1–100).
cache_ttl int 86400 Cache duration in seconds (0 = bypass, max 2592000).

Response

Returns raw image bytes with the following headers:

Content-Type: image/png
X-Len-Cache: MISS
X-Len-Render-Time: 234
X-Len-Request-Id: req_abc123
Cache-Control: public, max-age=86400

Rate limit and quota headers are also included, identical to the screenshot endpoint.


PDF Generation

Generate PDFs from any URL or HTML content. Uses the same authentication, caching, and rate limiting as the screenshot endpoint.

POST GET /v1/pdf

Parameters

Parameter Type Default Description
url string Target URL to render as PDF. Required*
html string Raw HTML to render (use POST). Required*
paper_size enum A4 Page size: A4, Letter, Legal, Tabloid, Ledger.
landscape bool false Use landscape orientation.
scale float 1.0 Scale factor (0.1–2.0).
margin_top string 0.4in Top margin (CSS unit).
margin_right string 0.4in Right margin (CSS unit).
margin_bottom string 0.4in Bottom margin (CSS unit).
margin_left string 0.4in Left margin (CSS unit).
print_background bool true Include background colors/images.
page_ranges string Page ranges to include, e.g. "1-3, 5".
header_template string HTML template for page headers.
footer_template string HTML template for page footers.
wait_until enum load load, domcontentloaded, networkidle0, networkidle2
timeout int 15000 Max wait time in ms (1–30000).
delay int 0 Wait N ms before rendering (0–10000).
user_agent string Custom user agent string.
device string Device emulation preset for user agent (e.g. iphone-15).
block_ads bool false Hide common ad elements.
block_cookie_banners bool false Hide cookie consent banners.
block_popups bool false Hide marketing popups (newsletters, spin wheels, exit-intent overlays).
js string Custom JavaScript to inject after page load (max 10KB).
css string Custom CSS to inject (max 10KB).
cache_ttl int 86400 Cache duration in seconds (0 = bypass, max 2592000).
response_type enum pdf pdf (raw bytes) or json (metadata).

*One of url or html is required.

Response

Returns raw PDF bytes with the following headers:

Content-Type: application/pdf
X-Len-Cache: MISS
X-Len-Render-Time: 1234
X-Len-Request-Id: req_abc123
Cache-Control: public, max-age=86400

Use response_type=json to receive metadata (file URL, size, cache status) instead of the raw PDF.


Error Codes

HTTP Code Description
400 MISSING_URL Neither url nor html provided
400 INVALID_URL URL is malformed or uses a blocked protocol
400 INVALID_PARAMETER Parameter value out of range
401 UNAUTHORIZED Missing or invalid API key
429 RATE_LIMITED Too many requests per hour
429 QUOTA_EXCEEDED Monthly screenshot quota exceeded
502 RENDER_FAILED Browser Rendering returned an error
504 RENDER_TIMEOUT Screenshot exceeded timeout

Rate Limits

Rate limits are enforced per API key on a rolling hourly window. The default limit is 100 requests per hour.

Response Headers

Every API response includes rate limit headers so you can track your usage:

// Hourly rate limit
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1709254800
// Monthly quota
X-Quota-Limit: 25000
X-Quota-Remaining: 24913
X-Quota-Reset: 1711929600
Header Description
X-RateLimit-LimitTotal requests allowed per hour
X-RateLimit-RemainingRequests remaining in the current hour
X-RateLimit-ResetUnix timestamp when the hourly window resets
X-Quota-LimitTotal screenshots allowed per month
X-Quota-RemainingScreenshots remaining this billing month
X-Quota-ResetUnix timestamp when the monthly quota resets (1st of next month)

When the hourly limit is exceeded, you'll receive a 429 RATE_LIMITED response. Wait until the X-RateLimit-Reset timestamp and retry.


Monthly Quotas

Each plan includes a monthly screenshot quota. When the quota is exceeded, requests return a 429 QUOTA_EXCEEDED error until the next billing month.

Plan Screenshots / Month Rate Limit / Hour Price
Free 100 100 €0
Pro 25,000 100 Coming soon
Scale 500,000 100 €99/mo
Enterprise 100,000+ Custom Custom

Quota Exceeded Response

When your monthly quota is exceeded, the API returns:

HTTP/1.1 429 Too Many Requests
{
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Monthly quota exceeded (100 screenshots). Resets 2026-03-01T00:00:00.000Z.",
"request_id": "req_abc123"
}
}

Use the X-Quota-Remaining header to track usage and the X-Quota-Reset header to know when the quota resets. If you need more volume, upgrade your plan.


Handling Rate Limits

Best practices for building reliable integrations that respect rate limits.

1. Check Headers Proactively

Monitor the remaining count on every response instead of waiting for a 429 error.

const res = await fetch(url, { headers });
const remaining = res.headers.get("X-Quota-Remaining");

if (remaining !== null && parseInt(remaining) < 10) {
  console.warn(`Low quota: ${remaining} screenshots remaining`);
}

2. Retry with Exponential Backoff

When you receive a 429, wait and retry with increasing delays.

async function captureWithRetry(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(
      "https://api.len.sh/v1/screenshot?url=" + encodeURIComponent(url),
      { headers: { Authorization: "Bearer YOUR_API_KEY" } }
    );

    if (res.status !== 429) return res;

    const reset = res.headers.get("X-RateLimit-Reset");
    const waitMs = reset
      ? Math.max(0, parseInt(reset) * 1000 - Date.now())
      : Math.pow(2, attempt) * 1000;

    await new Promise((r) => setTimeout(r, waitMs));
  }
  throw new Error("Rate limit retries exhausted");
}

3. Use Caching

Avoid redundant captures by using the built-in cache. Cached responses (cache HIT) still count against hourly rate limits, but they don't re-render the page.

// Cache for 24 hours (default)
/v1/screenshot?url=https://example.com&cache_ttl=86400

// Cache for 7 days
/v1/screenshot?url=https://example.com&cache_ttl=604800

4. Distinguish Error Types

Handle hourly rate limits and monthly quotas differently:

  • schedule RATE_LIMITED — Wait until X-RateLimit-Reset (usually minutes). Retry the request.
  • block QUOTA_EXCEEDED — Monthly limit reached. Either upgrade your plan or wait until the next billing month (X-Quota-Reset).

Caching

Screenshots are cached in Cloudflare R2. The cache key is a SHA-256 hash of all rendering-affecting parameters.

  • check_circle Default TTL: 24 hours (86400 seconds)
  • check_circle Bypass cache: Set cache_ttl=0
  • check_circle Max TTL: 30 days (2592000 seconds)
  • check_circle Cache HIT: ~50-100ms response time

Check the X-Len-Cache response header to see if a response was served from cache.