HTTP API Reference

This is the public HTTP API for Snap.

Base URL

https://snap.i2dev.com

Authentication

Pass your API key using either header:

http
Authorization: Bearer YOUR_API_KEY

or

http
x-api-key: YOUR_API_KEY

POST /v1/screenshots

Captures a screenshot of a URL (synchronous). Returns an imageUrl plus metadata.

Example request (copy/paste)

bash
curl -sS -X POST https://snap.i2dev.com/v1/screenshots   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
    "url": "https://example.com",
    "format": "png",
    "fullPage": false,
    "viewport": { "width": 1280, "height": 720 },
    "waitUntil": "load"
  }'

Example response

json
{
  "jobId": "...",
  "request_id": "...",
  "imageUrl": "https://snap-i2dev-com.s3.amazonaws.com/...?X-Amz-...",
  "contentType": "image/png",
  "bytes": 12345,
  "durationMs": 987,
  "logs": [],
  "page_errors": []
}

Result URLs and retention

Use imageUrl to retrieve the finished screenshot. Authenticated result URLs are signed and expire. Synchronous results default to seven days; async results default to one hour. Both are deployment-configurable. Download or copy an artifact that must outlive its URL. logs and page_errors contain capture diagnostics and may be empty.

Async mode, when enabled

Set "async": true in the POST /v1/screenshots body to queue the render and return immediately. Then poll GET /v1/jobs/:id for status. If async mode is unavailable on the deployment, the API returns a 400 bad_request; synchronous capture remains available.

Async submit example

bash
curl -sS -X POST https://snap.i2dev.com/v1/screenshots   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
    "url": "https://example.com",
    "format": "png",
    "async": true
  }'

Async submit response

json
{
  "jobId": "...",
  "request_id": "...",
  "status": "queued",
  "statusUrl": "https://snap.i2dev.com/v1/jobs/..."
}

Poll job status

bash
curl -sS -H "Authorization: Bearer YOUR_API_KEY"   https://snap.i2dev.com/v1/jobs/JOB_ID

Job status response (done)

json
{
  "jobId": "...",
  "status": "succeeded",
  "result": {
    "imageUrl": "https://snap-i2dev-com.s3.amazonaws.com/...?X-Amz-...",
    "contentType": "image/png",
    "bytes": 12345,
    "durationMs": 987
  }
}

Batch: many URLs in one request

Send an items array instead of a single url to capture up to 50 pages in one request. Options at the top level apply to every item, and each item may override any of them, field by field — an item that sets only viewport.width keeps the shared viewport.height. Every item needs its own url. A batch is always asynchronous: it returns a batchId immediately, which you poll at GET /v1/screenshots/:batchId. Because it is asynchronous, it needs the same deployment switch as async mode and returns 400 bad_request where that is unavailable.

Each item costs one render from your monthly allowance, charged up front and all-or-nothing: a batch larger than your remaining allowance is refused whole with 429 quota_exceeded and costs you nothing. A batch containing a URL we will not fetch — a private address, a non-HTTP scheme — is likewise rejected before anything is charged, with the offending index in the error details.

A URL that simply does not resolve is treated differently: it is accepted and fails as its own item, so one dead link in a sitemap does not throw away the rest of the batch. Items render independently, so a batch can finish with a mix of succeeded and failed items — check counts rather than assuming completed means every item worked.

Batch submit example

bash
curl -sS -X POST https://snap.i2dev.com/v1/screenshots   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
    "format": "jpeg",
    "viewport": { "width": 1280, "height": 720 },
    "items": [
      { "url": "https://example.com/" },
      { "url": "https://example.com/pricing", "fullPage": true },
      { "url": "https://example.com/docs", "viewport": { "width": 390, "height": 844 } }
    ]
  }'

Batch submit response

json
{
  "batchId": "...",
  "request_id": "...",
  "count": 3,
  "status": "queued",
  "statusUrl": "https://snap.i2dev.com/v1/screenshots/..."
}

Poll batch status

bash
curl -sS -H "Authorization: Bearer YOUR_API_KEY"   https://snap.i2dev.com/v1/screenshots/BATCH_ID

Batch status response

status is running until every item has finished, then completed — and it stays there. Items come back in the order you submitted them. Results expire (see retention above), so a batch you poll much later can be completed with fewer entries in items than itemCount; fetch what you need before then.

json
{
  "batchId": "...",
  "status": "completed",
  "itemCount": 3,
  "counts": { "queued": 0, "running": 0, "succeeded": 2, "failed": 1 },
  "items": [
    {
      "jobId": "...",
      "url": "https://example.com/",
      "status": "succeeded",
      "imageUrl": "https://snap-i2dev-com.s3.amazonaws.com/...?X-Amz-...",
      "contentType": "image/jpeg",
      "bytes": 12345,
      "durationMs": 987
    },
    {
      "jobId": "...",
      "url": "https://example.com/pricing",
      "status": "failed",
      "error": { "error": "Navigation timeout", "code": "nav_timeout" }
    }
  ]
}

Request body

FieldTypeRequiredDescription
urlstringYesThe URL to screenshot (public http/https only; SSRF protections enabled).
format"png" | "jpeg"NoDefault: png. The anonymous GET endpoint defaults to jpeg instead.
fullPagebooleanNoCapture full scrollable page. Default: false
viewport{ width, height }NoDefault: {"width":1280,"height":720}
waitUntil"load" | "domcontentloaded" | "networkidle"NoDefault: load. After this condition fires, the renderer always runs a best-effort settle pass (network quiescence up to 5s, font loading, brief animation delay) before capturing, so SPAs and lazy content typically render fully on the default setting.
waitForSelectorstringNoWait for a CSS selector before capturing.
timeoutMsnumberNoOverall render timeout (bounded by server max).
navTimeoutMsnumberNoNavigation timeout (bounded by server max).
timezoneIdstringNoExample: America/Toronto
localestringNoExample: en-US
extraHTTPHeadersobjectNoExtra request headers (string → string).
cookiesarrayNoCookies to set before navigation.
baselineTagstringNoOptional label included in the result's storage identity. It does not publish a Visual CI baseline.
compareWithTagstringNoOptional label included in the result's storage identity. It does not retrieve or diff a previous capture.
asyncbooleanNoQueue render and return immediately (requires async mode enabled).
itemsarrayNoCapture many URLs in one request (max 50). Replaces url; every other field becomes a shared default that each item may override. See Batch.

timezoneId and locale are checked by Chromium when rendering starts, rather than against a server-side allowlist. Values Chromium cannot use return400 invalid_render_option for synchronous requests. Async submissions still return 202, then expose invalid_render_option on the failed job. Unconventional values that Chromium supports remain accepted.

Capturing authenticated pages safely

Supply headers and cookies from CI or repository secrets—never commit authorization values. Cookies must include the target domain. Use the least privilege and shortest lifetime possible, and avoid reusing long-lived personal sessions. Snap forwards these credentials while loading the target page. Header and cookie values are not part of the result's storage identity, so do not use one API key with identical URL/render options for multiple user sessions; a later capture can replace the earlier object.

Rate limits and errors

Errors are returned as JSON and include a request_id. Include that ID when reporting issues.

Authenticated rate-limit responses use HTTP 429 with rate_limited, a Retry-After header, and X-Snap-Rate-Limit-Scope set to ip or api_key. The JSON details object repeats the delay as retry_after_seconds and the scope as scope. too_many_inflight is also a 429, but is a concurrency response and does not include token-refill retry metadata.

json
{
  "error": "Invalid or missing API key",
  "code": "unauthorized",
  "request_id": "..."
}

Common HTTP status codes:

  • 400 bad_request / invalid_render_option / unsafe_url
  • 401 unauthorized
  • 413 payload_too_large
  • 415 unsupported_media_type — send Content-Type: application/json. Worth checking first if a hand-written request fails immediately: some shells drop the header, and curl removes a header given an empty value.
  • 429 rate_limited / too_many_inflight / quota_exceeded
  • 500 internal_server_error
  • 502 automation_refused / upstream_unreachable — the target site failed, not Snap