API reference · v1

Grabbit Screenshot API

POST a URL, get back a hosted image. JSON in, JSON out, one Bearer key. Every live grab costs $0.002; test keys are free.

Authentication

Base URL: https://api.grabbit.live/v1. Send Authorization: Bearer <token> on every request except /pricing.

Quickstart

One request captures a page. The response holds a hosted image_url.

Request
curl https://api.grabbit.live/v1/grabs \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "full_page": true}'
200 OK
{
  "id": "5f0c6c3e-8a51-4d0b-9d0e-2b7a1c9e4f10",
  "status": "done",
  "target_url": "https://example.com",
  "image_url": "https://grabbit.live/g/5f0c6c3e-8a51-4d0b-9d0e-2b7a1c9e4f10.png",
  "width": 1280,
  "height": 720,
  "format": "png",
  "bytes": 188231,
  "execution_ms": 812,
  "created_at": "2026-09-28T00:00:00Z"
}
post/v1/grabs

Capture a screenshot of a URL

Renders a public web page in a real Chromium browser and returns a hosted image URL. Synchronous by default (the render times out after 25 seconds). Send `Prefer: respond-async` or `?async=true` to get a 202 with a grab id, then poll GET /grabs/{id}. Live keys spend one credit per successful grab; sk_test_ keys return a free placeholder image.

operationId: createGrab · Bearer token required

ParameterInTypeDescription
Idempotency-KeyheaderstringOptional. Retries with the same key return the original response.
PreferheaderstringOptional. `respond-async` queues the grab and returns 202.
asyncquerybooleanOptional. Same as `Prefer: respond-async`.
Body fieldTypeDescription
urlstring (required)Absolute http(s) URL to capture.
widthintegerViewport width, 320 to 1920. Default 1280.
heightintegerViewport height, 240 to 1080. Default 720.
formatpng | jpeg | webpImage format. Default png.
full_pagebooleanCapture the whole scrollable page instead of the viewport. Default false.
delay_msintegerWait this long after load before capturing, 0 to 10000. Default 0.
selectorstringCSS selector; capture only that element. Sync requests only.
Example
curl https://api.grabbit.live/v1/grabs \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "full_page": true}'
get/v1/grabs

List recent grabs

Lists the calling key's grabs, newest first, scoped to the key's team and environment.

operationId: listGrabs · Bearer token required

ParameterInTypeDescription
limitqueryinteger1 to 100. Default 25.
statusquerypending | processing | done | failedOptional status filter.
Example
curl "https://api.grabbit.live/v1/grabs?limit=10" -H "Authorization: Bearer sk_test_..."
get/v1/grabs/{id}

Fetch one grab

Returns a single grab. Poll this after an async create until status is done or failed.

operationId: getGrab · Bearer token required

ParameterInTypeDescription
idpathuuidThe grab id.
Example
curl https://api.grabbit.live/v1/grabs/GRAB_ID -H "Authorization: Bearer sk_test_..."
get/v1/usage

Get credit balance and usage

Returns remaining credits (subscription + prepaid), the plan, and 30-day grab counts. Check this before a large batch so you can top up first.

operationId: getUsage · Bearer token required

Example
curl https://api.grabbit.live/v1/usage -H "Authorization: Bearer sk_live_..."
get/v1/pricing

Get pricing and competitor comparison

Public, no auth. Flat per-grab price, prepaid packages, and a dated comparison against other screenshot APIs.

operationId: getPricing · No auth

Example
curl https://api.grabbit.live/v1/pricing

Errors

Every error, including a 404 on an unknown API path, uses the same JSON envelope. Branch on error.code; the message is for humans and may change.

402 Payment Required
{
  "error": {
    "code": "credits_exhausted",
    "message": "Your team has 0 credits remaining. Please top up your balance in the Developer Console."
  }
}
CodeHTTPWhat to do
unauthorized401Missing or invalid Bearer token. Send an sk_live_/sk_test_ key or an OAuth token.
bad_request400A parameter is missing or malformed. The message names the field.
invalid_viewport400width must be 320 to 1920 and height 240 to 1080.
ssrf_detected400The URL resolves to a private or internal address. Only public URLs are captured.
credits_exhausted402No live credits left. Top up at https://grabbit.live/app/billing.
not_found404No grab with that id exists for this key, or the path is not an API route.
idempotency_conflict409A request with this Idempotency-Key is still running. Retry after Retry-After.
idempotency_mismatch400This Idempotency-Key was already used with a different request body.
rate_limit_exceeded42960 requests per minute per team. Wait until X-RateLimit-Reset.
queue_limit_exceeded429At most 10 async grabs may be pending at once. Poll existing ones first.
render_blocked422The target site blocked the capture (bot wall). No credit is charged.
render_failed500The page could not be captured (timeout, DNS failure). No credit is charged.
internal_error500Unexpected server error. Safe to retry with the same Idempotency-Key.

Limits and retries

  • 60 grab requests per minute per team. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix seconds).
  • Send an Idempotency-Key header on POST /grabs. A retry with the same key and body returns the original response instead of capturing twice.
  • Async mode (Prefer: respond-async) allows 10 pending grabs per team. Poll GET /grabs/{id} or configure a grab.succeeded / grab.failed webhook in the console.
  • Only public URLs are captured. Private, loopback, and link-local addresses are refused with ssrf_detected.

MCP server and CLI

Agents can skip HTTP entirely. The hosted MCP server at https://mcp.grabbit.live exposes grab, get_grab, list_grabs, get_usage, and get_pricing, with OAuth for claude.ai or an API key header for everything else. The CLI runs with npx grabbit.live <url>.

Claude Code
claude mcp add --transport http grabbit https://mcp.grabbit.live \
  --header "Authorization: Bearer sk_live_..."

Setup guides: Claude Code, Cursor, Codex, all agents. Questions: contact us.