API docs · Reference
API reference
Every endpoint of the FrameQuery API v1 with its parameters, a copy-pasteable curl command and the shape it returns. The OpenAPI 3.1 document is the machine-readable version.
Base URL https://api.framequery.com/v1/api. The machine-readable contract is the OpenAPI 3.1 document at https://api.framequery.com/v1/api/openapi.json; anchors on this page equal its operationIds.
All examples assume export FRAMEQUERY_API_KEY=fq_live_... and, where a job is involved, JOB_ID holding a job id. Successful responses are wrapped as {"data": …, "requestId": "…"} (except GET /openapi.json); errors follow the error envelope.
| Method | Path | Auth | Operation |
|---|---|---|---|
POST | /jobs | key | createJob |
POST | /jobs/from-url | key | createJobFromUrl |
POST | /jobs/batch | key | createBatch |
GET | /jobs | key | listJobs |
GET | /jobs/{jobId} | key | getJob |
POST | /jobs/{jobId}/cancel | key | cancelJob |
DELETE | /jobs/{jobId} | key | cancelJobDeprecated |
GET | /jobs/{jobId}/transcript | key | getTranscript |
GET | /jobs/{jobId}/scenes | key | getScenes |
GET | /jobs/{jobId}/audioTracks | key | listAudioTracks |
GET | /jobs/{jobId}/audioTracks/{trackIndex} | key | getAudioTrack |
GET | /quota | key | getQuota |
GET | /webhooks/deliveries | key | listWebhookDeliveries |
POST | /webhooks/test | key | sendTestWebhook |
GET | /openapi.json | none | getOpenApi |
GET | /pricing | none | getPricing |
GET | /pricing/estimate | none | estimatePrice |
GET | /formats | none | listFormats |
GET | /keys | session | listKeys |
GET | /keys/portal | session | getKeysPortal |
POST | /keys/{id}/rotate | session | rotateKey |
GET | /webhooks/secret | session | getWebhookSecret |
POST | /webhooks/secret/rotate | session | rotateWebhookSecret |
key: an fq_live_ API key in Authorization: Bearer or X-API-Key. none: no key, rate-limited per IP. session: the FrameQuery dashboard's signed-in session, used by the API keys page; an API key cannot call these.
Jobs
Create a job with an upload
POST /jobs. Creates a job in state awaiting_upload and returns the Job object plus uploadUrl, resumableUploadUrl, uploadMethod (PUT), uploadHeaders, expiresInSeconds (3600) and maxUploadBytes; for separate audio also audioUploadUrl or audioTrackUploadUrls, and audioUploadHeaders. Accepts Idempotency-Key. Details: Upload a local file.
Body: fileName (required), fileSizeBytes, processingMode, callbackUrl, idempotencyKey, metadata, audioFileName, audioSyncMode, audioOffsetMs, audioTracks[].
curl -X POST https://api.framequery.com/v1/api/jobs \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"fileName": "interview.mp4", "fileSizeBytes": 734003200, "processingMode": "all"}'Errors: 400, 401, 402, 403, 409, 413, 415, 422, 429, 500, 503.
Create a job from a URL
POST /jobs/from-url. Creates a job that fetches its media from a public http(s) URL; returns the Job object in state queued. Accepts Idempotency-Key. Details: Create a job from a URL.
Body: url (required), fileName, downloadToken, provider (gdrive | dropbox), processingMode, callbackUrl, idempotencyKey, metadata, audioUrl, audioDownloadToken, audioSyncMode, audioOffsetMs, audioTracks[].
curl -X POST https://api.framequery.com/v1/api/jobs/from-url \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"url": "https://example.com/interview.mp4", "processingMode": "all", "metadata": {"assetId": "A003_C008"}}'Errors: 400, 401, 402, 403, 409, 415, 422, 429, 500, 503.
Create a batch
POST /jobs/batch. Creates jobs for up to 100 URLs. Returns {batchId, mode, jobs[]}, each item a Job object plus its clip index. Accepts Idempotency-Key. Details: Batch jobs.
Body: mode (independent | continuous, required), clips[] (1 to 100, each {sourceUrl, fileName?, downloadToken?, provider?, metadata?}, required), processingMode, callbackUrl, idempotencyKey, metadata.
curl -X POST https://api.framequery.com/v1/api/jobs/batch \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode": "independent", "clips": [{"sourceUrl": "https://example.com/a.mp4"}, {"sourceUrl": "https://example.com/b.mp4"}]}'Errors: 400, 401, 402, 403, 409, 415, 422, 429, 500, 503.
List jobs
GET /jobs. Your API jobs, newest first. Returns {data: [Job summary], hasMore, nextCursor} and a Link: <…>; rel="next" header while more pages exist. Continue while hasMore is true; pages can be short.
Query: limit (1 to 100, default 20), cursor (the previous nextCursor), state (a job state; uploading is accepted as an alias of awaiting_upload), status (deprecated legacy filter, not combinable with state).
curl "https://api.framequery.com/v1/api/jobs?state=completed&limit=20" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 400, 401, 429, 500.
Get a job
GET /jobs/{jobId}. The Job object.
Query: include, a comma list of scenes and transcript, or none. Default: both, each present only once its stage is completed. Use include=none when polling.
curl "https://api.framequery.com/v1/api/jobs/$JOB_ID?include=none" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 400, 401, 404, 429, 500.
Cancel a job
POST /jobs/{jobId}/cancel. No body. Allowed while awaiting_upload, queued or processing; returns the Job object (without processedData) and emits job.cancelled. Completed stages stay charged and readable. Details: Cancel a job.
curl -X POST "https://api.framequery.com/v1/api/jobs/$JOB_ID/cancel" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 401, 404, 409 (JOB_NOT_CANCELLABLE), 429, 500.
Cancel a job (deprecated)
DELETE /jobs/{jobId}. Deprecated alias of POST /jobs/{jobId}/cancel with identical behaviour. Responses carry Deprecation: true and Link: </v1/api/jobs/{jobId}/cancel>; rel="successor-version". The DELETE verb is reserved for a future "delete job and results" operation.
curl -X DELETE "https://api.framequery.com/v1/api/jobs/$JOB_ID" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Results
Get the transcript
GET /jobs/{jobId}/transcript. JSON {jobId, language, speakers, text, segments[], tracks?, trackIndex?, trackName?}, or a caption file. Details: Results.
Query: format (json default, srt, vtt, txt), track (audio track index; required for srt/vtt/txt on multi-track jobs).
curl "https://api.framequery.com/v1/api/jobs/$JOB_ID/transcript?format=vtt" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY" -o transcript.vttErrors: 400, 401, 404 (NOT_FOUND, RESULTS_UNAVAILABLE), 409 (RESULTS_NOT_READY), 500.
Get scenes
GET /jobs/{jobId}/scenes. {jobId, scenes[]}, each scene {index, startTime, endTime, description, objects, colorPalette, shotType, shotAngle}. Empty when the video has no scene cuts. Details: Results.
curl "https://api.framequery.com/v1/api/jobs/$JOB_ID/scenes" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 401, 404 (NOT_FOUND, RESULTS_UNAVAILABLE), 409 (RESULTS_NOT_READY), 500.
List audio tracks
GET /jobs/{jobId}/audioTracks. {jobId, tracks[]} for a multi-track job, ordered by index; each {trackIndex, trackName, status, transcript, speakers, language, errorCode?} with the stored transcript entries.
curl "https://api.framequery.com/v1/api/jobs/$JOB_ID/audioTracks" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 401, 404, 500.
Get one audio track
GET /jobs/{jobId}/audioTracks/{trackIndex}. One track, same shape as a tracks[] item above.
curl "https://api.framequery.com/v1/api/jobs/$JOB_ID/audioTracks/0" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 400, 401, 404, 500.
Account
Credits and usage
GET /quota. data.api = {available, balanceCents, heldCents, availableCents, payAsYouGo, monthUsageSeconds, tier, volumeThresholdSeconds}. When available is false the balance could not be read right now and the other fields are omitted. The remaining top-level fields describe desktop-app plans and can be ignored. Details: Credits and billing.
curl https://api.framequery.com/v1/api/quota \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 401, 429.
Webhooks
Delivery log
GET /webhooks/deliveries. The last 100 deliveries, newest first: {id, jobId, eventType, urlHost, status, attempts, lastStatus, lastError, disabled, deliveredAt, createdAt}. Query: jobId. Details: Delivery log.
curl "https://api.framequery.com/v1/api/webhooks/deliveries?jobId=$JOB_ID" \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY"Errors: 401, 429.
Send a test event
POST /webhooks/test. Sends a signed webhook.test event to url (HTTPS, port 443, public address; 5 per minute). Returns {delivered, statusCode, latencyMs, error?}.
curl -X POST https://api.framequery.com/v1/api/webhooks/test \
-H "Authorization: Bearer $FRAMEQUERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-app.example.com/webhooks/framequery"}'Errors: 400, 401, 429.
Discovery (no key)
All four are rate-limited to 120 requests per minute per client IP and cacheable for five minutes (Cache-Control: public, max-age=300). A request that carries a key is served the same way.
OpenAPI document
GET /openapi.json. The OpenAPI 3.1 document as JSON, not wrapped in data.
curl https://api.framequery.com/v1/api/openapi.json -o framequery-openapi.jsonPrice table
GET /pricing. Per-stage rates in US cents per footage-hour for the standard and raw tiers, plus volumeThresholdSeconds, minBillableSeconds, currency, unit and rounding. Details: Pricing.
curl https://api.framequery.com/v1/api/pricingPrice estimate
GET /pricing/estimate. The exact charge for one job if every requested stage completes: {cents, usd, currency, billableSeconds, durationSeconds, tier, mode, audioTracks, priorUsageSeconds}.
Query: durationSeconds (required, 0 to 2 678 400), tier (standard | raw), mode (all | transcript | vision), audioTracks (1 to 16), priorUsageSeconds (billable seconds already used this UTC month).
curl "https://api.framequery.com/v1/api/pricing/estimate?durationSeconds=3600&tier=raw&mode=all"Errors: 400 (with param), 429.
Formats and limits
GET /formats. {standard, audioOnly, raw: {r3d, braw, arri}, rawExtensions, blocked, unknownExtensions, limits: {standardMaxBytes, rawMaxBytes, maxDurationSec, uploadUrlExpirySeconds}}. Details: Formats and limits.
curl https://api.framequery.com/v1/api/formatsDashboard (session)
These power the API keys page. They take the dashboard's signed-in session token, not an API key, and are listed here because they are part of the contract. $FRAMEQUERY_SESSION_TOKEN below stands for that token.
List keys
GET /keys. Your Polar license keys, never the full secret: [{id, displayKey, status, expiresAt, benefitId, createdAt}].
curl https://api.framequery.com/v1/api/keys -H "Authorization: Bearer $FRAMEQUERY_SESSION_TOKEN"Customer portal
GET /keys/portal. {url}: a short-lived Polar customer-portal link to view keys, receipts and credits. 404 if you have never bought anything.
curl https://api.framequery.com/v1/api/keys/portal -H "Authorization: Bearer $FRAMEQUERY_SESSION_TOKEN"Rotate a key
POST /keys/{id}/rotate. Rotates one key. The response is the key object plus key, the new full secret, shown once.
curl -X POST "https://api.framequery.com/v1/api/keys/$KEY_ID/rotate" -H "Authorization: Bearer $FRAMEQUERY_SESSION_TOKEN"Reveal the webhook secret
GET /webhooks/secret. {secret, createdAt, rotatedAt}; the whsec_ secret is created on the first call.
curl https://api.framequery.com/v1/api/webhooks/secret -H "Authorization: Bearer $FRAMEQUERY_SESSION_TOKEN"Rotate the webhook secret
POST /webhooks/secret/rotate. Returns the new secret. The previous one keeps signing, as a second v1, signature, for 24 hours.
curl -X POST https://api.framequery.com/v1/api/webhooks/secret/rotate -H "Authorization: Bearer $FRAMEQUERY_SESSION_TOKEN"