Framepine

API reference

Base URL https://framepine.com/v1. All control endpoints take Authorization: Bearer <key> and speak JSON. Delivery endpoints take no headers and return an image.

POST/v1/assets

Registers a source and starts indexing.

FieldTypeNotes
sourcestringRequired. HTTPS URL we can fetch. Max 2 GB.
labelstringOptional. Your own name for the asset, echoed back.
ttl_daysintegerOptional, 1–365. Deletes the asset automatically. Default: no expiry.
curl -X POST https://framepine.com/v1/assets \
  -H "Authorization: Bearer fp_live_9c2ad41f7b6e" \
  -H "Content-Type: application/json" \
  -d '{"source":"https://cdn.example.com/reel.mp4","label":"launch-reel"}'

# 201 Created
{
  "id": "as_7f31c0",
  "label": "launch-reel",
  "status": "ready",
  "kind": "video",
  "duration": 34.2,
  "frames": 821,
  "width": 1920,
  "height": 1080,
  "token": "k3d9xq2m",
  "delivery": "https://framepine.com/v1/f/k3d9xq2m/",
  "created_at": "2026-07-14T09:21:04Z"
}

status is indexing, ready or failed. For PDF sources kind is document, duration is absent and pages takes the place of frames.

GET/v1/assets/{id}

Returns the same object. Poll this while status is indexing.

curl https://framepine.com/v1/assets/as_7f31c0 \
  -H "Authorization: Bearer fp_live_9c2ad41f7b6e"

A failed asset carries the reason:

{
  "id": "as_7f31c0",
  "status": "failed",
  "error": { "code": "source_unreachable", "message": "Origin returned 403." }
}

POST/v1/assets/{id}/rotate

Issues a new delivery token and retires the old one. Existing URLs keep working for a 60 second grace period, then return 404. Cached derivatives are preserved — rotation changes the address, not the images.

curl -X POST https://framepine.com/v1/assets/as_7f31c0/rotate \
  -H "Authorization: Bearer fp_live_9c2ad41f7b6e"

# 200 OK
{
  "id": "as_7f31c0",
  "token": "w82hc6nv",
  "delivery": "https://framepine.com/v1/f/w82hc6nv/",
  "previous_token_expires_at": "2026-07-14T09:22:04Z"
}

DELETE/v1/assets/{id}

Removes the asset and every cached derivative. Returns 204 No Content. Deleting is immediate and cannot be undone.

curl -X DELETE https://framepine.com/v1/assets/as_7f31c0 \
  -H "Authorization: Bearer fp_live_9c2ad41f7b6e"

GET/v1/f/{token}/{spec}.{ext}

Returns the rendered frame. No authentication header — the token is the credential. Spec grammar is documented in the guide.

GET /v1/f/k3d9xq2m/t12.5s-w640.jpg

# 200 OK
Content-Type: image/jpeg
Content-Length: 48213
ETag: "9f2c11a4"
Cache-Control: public, max-age=86400, stale-while-revalidate=604800
X-Framepine-Cache: MISS
X-Framepine-Render-Ms: 214

X-Framepine-Cache is HIT or MISS. Only a MISS counts against your extraction quota; a HIT counts as delivery bandwidth only. Requests with a matching If-None-Match return 304 and are not billed.

GET/v1/usage

Current billing period, as the dashboard sees it.

{
  "period_start": "2026-07-01T00:00:00Z",
  "period_end": "2026-08-01T00:00:00Z",
  "plan": "studio",
  "extractions": { "used": 6412, "included": 25000 },
  "delivery_bytes": { "used": 41028374528, "included": 268435456000 }
}

Error codes

Every error body has the same shape: an error object with code and message. Match on code — messages are written for humans and change without notice.

HTTPCodeWhat happened
400invalid_specThe delivery spec did not parse, or a value was out of range.
400timecode_out_of_rangeThe timecode is past the end of the source.
401invalid_keyMissing, malformed or revoked API key.
402quota_exceededPlan quota spent. Delivery of cached frames continues; new renders stop.
404asset_not_foundUnknown id, retired token, or the asset was deleted.
413source_too_largeSource exceeds 2 GB.
415unsupported_sourceContainer or codec cannot be indexed.
429rate_limitedControl API rate limit. Honour Retry-After.
502source_unreachableYour origin refused the fetch or returned an error status.
504source_timeoutYour origin did not respond within 30 seconds.

502 and 504 are worth retrying with backoff. The rest are not: they will fail the same way until you change the request.