FrameQuery API
Programmatically upload videos and trigger processing through the FrameQuery cloud pipeline. Integrate FrameQuery into your workflow, automate bulk uploads, or build custom tooling on top of your video library.
Base URL
https://api.framequery.com/v1All requests must be made over HTTPS.
API key via header
100 req/min per key
$2.00 / hour processed
Quick start
Process a video via URL in two API calls:
# Submit a video from URL
curl -X POST https://api.framequery.com/v1/api/jobs/from-url \
-H "X-API-Key: fq_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/video.mp4"}'Response envelope
All successful responses are wrapped in a data key. Errors return an error string.
{
"data": { ... }
}Authentication
All API requests require authentication via an API key. Include your key in the request headers using either format:
X-API-Key: fq_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6API keys use the fq_live_ prefix followed by 32 hex characters. Create and manage keys from your dashboard — see the section below.
API Keys
Create and manage API keys directly from here. Each key is identified by its keyPrefix — the first 8 hex characters after fq_live_. Keys support scopes: jobs:read, jobs:write, and quota:read.
API reference
You can also manage keys programmatically. These endpoints are authenticated via your session token (not an API key).
/v1/api/keysGenerate a new API key. The full key is only returned once — store it securely.
Body parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | A human-readable label for this key (e.g. "CI Pipeline") |
| scopes | string[] | Permissions for this key. Defaults to ["jobs:read", "jobs:write", "quota:read"] |
curl -X POST https://api.framequery.com/v1/api/keys \
-H "Authorization: Bearer <your_session_token>" \
-H "Content-Type: application/json" \
-d '{"name": "CI Pipeline"}'Response
200{
"data": {
"key": "fq_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"keyPrefix": "a1b2c3d4",
"name": "CI Pipeline"
}
}/v1/api/keysList all API keys associated with your account. Returns the key prefix, name, scopes, and revocation status — never the full key.
curl https://api.framequery.com/v1/api/keys \
-H "Authorization: Bearer <your_session_token>"Response
200{
"data": [
{
"keyPrefix": "a1b2c3d4",
"name": "CI Pipeline",
"scopes": ["jobs:read", "jobs:write", "quota:read"],
"createdAt": "2026-02-21T14:30:00Z",
"revoked": false
},
{
"keyPrefix": "e5f6a7b8",
"name": "Staging",
"scopes": ["jobs:read", "quota:read"],
"createdAt": "2026-02-19T09:00:00Z",
"revoked": false
}
]
}/v1/api/keys/:keyPrefixPermanently revoke an API key. Any requests using this key will immediately start failing with 401.
Path parameters
| Parameter | Type | Description |
|---|---|---|
| keyPrefix | string | The 8-character key prefix (e.g. a1b2c3d4) |
curl -X DELETE https://api.framequery.com/v1/api/keys/a1b2c3d4 \
-H "Authorization: Bearer <your_session_token>"Response
200{
"data": {
"revoked": true
}
}Jobs
A job represents a video being uploaded and processed through the FrameQuery pipeline. Once complete, the job contains scenes, transcripts, and detected objects. Jobs progress through these statuses:
| Status | Meaning |
|---|---|
| PENDING_UPLOAD | Job created via presigned URL — waiting for you to PUT the file |
| PENDING_FETCH | Job created from URL — FrameQuery is downloading the file |
| FAILED_FETCH | URL download failed (unreachable, private IP, etc.) |
| COMPLETED | Processing finished with scenes and transcript |
| COMPLETED_NO_SCENES | Processing finished but no scenes were detected |
/v1/api/jobsCreate a job and get a presigned upload URL. You then PUT your file directly to that URL.
Body parameters
| Parameter | Type | Description |
|---|---|---|
| fileName | string | The filename including extension (e.g. "video.mp4") |
# Step 1: Create the job and get an upload URL
curl -X POST https://api.framequery.com/v1/api/jobs \
-H "X-API-Key: fq_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"fileName": "video.mp4"}'
# Step 2: Upload the file to the presigned URL (expires in 15 min)
curl -X PUT "<uploadUrl from step 1>" \
-H "Content-Type: application/octet-stream" \
--data-binary @/path/to/video.mp4Response
200{
"data": {
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"uploadUrl": "https://storage.googleapis.com/...",
"expiresInSeconds": 900,
"uploadMethod": "PUT"
}
}PUT request with Content-Type: application/octet-stream. Processing starts automatically once the upload completes./v1/api/jobs/from-urlSubmit a publicly accessible video URL for FrameQuery to download and process. No file upload needed.
Body parameters
| Parameter | Type | Description |
|---|---|---|
| url | string | Publicly accessible URL of the video file (http or https) |
| fileName | string | Override the filename (defaults to filename from URL) |
curl -X POST https://api.framequery.com/v1/api/jobs/from-url \
-H "X-API-Key: fq_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://storage.example.com/rushes/A003_C008.R3D",
"fileName": "Camera A Take 8.R3D"
}'Response
200{
"data": {
"jobId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"status": "PENDING_FETCH"
}
}/v1/api/jobs/:jobIdRetrieve the current status and details of a job. Completed jobs include the full processedData with scenes and transcript.
Path parameters
| Parameter | Type | Description |
|---|---|---|
| jobId | string | The job ID |
curl https://api.framequery.com/v1/api/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "X-API-Key: fq_live_your_key_here"Response — in progress
Response
200{
"data": {
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "PENDING_FETCH",
"originalFilename": "A003_C008.R3D",
"source": "api",
"createdAt": "2026-02-21T14:30:00Z",
"updatedAt": "2026-02-21T14:30:00Z"
}
}Response — completed
Response
200{
"data": {
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "COMPLETED",
"originalFilename": "A003_C008.R3D",
"source": "api",
"createdAt": "2026-02-21T14:30:00Z",
"updatedAt": "2026-02-21T14:38:42Z",
"processedData": {
"length": 1847.5,
"scenes": [
{
"sceneId": "scene_a3f7c291-8e4b-4d1a-b562-9c0e3f8a71d4_45.8",
"startTs": 12.467,
"endTs": 45.8,
"description": "A woman in a blue blazer stands at the front of the room presenting a bar chart showing revenue growth across four quarters.",
"mainColor": "#3b82f6",
"detections": [
{
"name": "person",
"x": 0.12,
"y": 0.08,
"width": 0.22,
"height": 0.88,
"confidence": 0.9789
},
{
"name": "bar chart",
"x": 0.42,
"y": 0.05,
"width": 0.52,
"height": 0.68,
"confidence": 0.9156
}
]
}
],
"transcript": [
{
"Speaker": "a4b2c3d4-e5f6-7890-abcd-ef1234567890",
"StartTime": 12.4,
"EndTime": 18.9,
"Text": "Welcome everyone to today's product reveal."
},
{
"Speaker": "a4b2c3d4-e5f6-7890-abcd-ef1234567890",
"StartTime": 19.2,
"EndTime": 27.1,
"Text": "What we're about to show you has been two years in the making."
}
]
}
}
}
processedData field is only present when the job status is COMPLETED. Transcript fields use capitalised keys: StartTime, EndTime, Text. Times are in seconds./v1/api/jobsList jobs for your account with optional filtering. Uses cursor-based pagination.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by exact status (e.g. COMPLETED, PENDING_FETCH) |
| limit | integer | Results per page, 1–100 (default: 20) |
| cursor | string | Job ID from a previous response's nextCursor for pagination |
curl "https://api.framequery.com/v1/api/jobs?status=COMPLETED&limit=10" \
-H "X-API-Key: fq_live_your_key_here"Response
200{
"data": [
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "COMPLETED",
"originalFilename": "video.mp4",
"createdAt": "2026-02-21T14:30:00Z",
"updatedAt": "2026-02-21T14:38:42Z"
}
],
"nextCursor": "c3d4e5f6-a7b8-9012-cdef-345678901234"
}nextCursor value as the cursor query parameter until nextCursor is null.Billing & Credits
Video processing is billed per second based on the duration of the source video, with a 30-second minimum per job. Volume discounts kick in automatically after 100 hours per month.
Pricing
Billed per second with a 30-second minimum per job. After 100 hours in a billing month, the rate drops to $1.50/hr.
| Video duration | Billed duration | Cost |
|---|---|---|
| 10 seconds | 30 seconds (minimum) | $0.017 |
| 5 minutes | 5 min 0 sec | $0.167 |
| 45 minutes | 45 min 0 sec | $1.50 |
| 1 hour | 1 hr 0 min 0 sec | $2.00 |
| 3 hr 10 min | 3 hr 10 min 0 sec | $6.33 |
Volume discount
After 100 hours of processing in a billing month, all additional usage is billed at $1.50/hr instead of $2.00/hr. The discount applies automatically.
Plan allowances
| Plan | Included hours / month |
|---|---|
| Starter | 10 hours |
| Pro | 50 hours |
| Max | 250 hours |
Need more? Purchase credit packs (5 or 20 hours) from your dashboard. Credits roll over and don't expire.
How FrameQuery compares
FrameQuery delivers scene detection, transcription, object recognition, and semantic search in a single API call, no stitching services together.
| FrameQuery | Twelve Labs | Google Video AI | AWS Rekognition | |
|---|---|---|---|---|
| Price / hr | $2.00 | $2.40 | ~$4.68* | ~$4.32* |
| Volume rate | $1.50 after 100 hr | Custom | None | None |
| Billing granularity | Per second | Per second | Per 1-min chunk | Per 1-min chunk |
| Minimum charge | 30 sec | 10 sec | 1 min | 1 min |
| Scene detection | Included | Included | Add-on | Not available |
| Transcription | Included | Included | Separate API | Separate API |
| Object detection | Included | Limited | Add-on | Included |
| Semantic search | Included | Included | Not available | Not available |
| Speaker diarization | Included | Not available | Separate API | Not available |
| Camera RAW support | Included | Not available | Not available | Not available |
| Single API call | Yes | Yes | No (multiple) | No (multiple) |
* Estimated cost when combining label detection, transcription, and object tracking at published per-minute rates. Actual costs vary by feature selection.
Check your quota
/v1/api/quotaRetrieve your current plan, included hours, credit balance, and reset date.
curl https://api.framequery.com/v1/api/quota \
-H "X-API-Key: fq_live_your_key_here"Response
200{
"data": {
"plan": "pro",
"includedHours": 50,
"creditsBalanceHours": 5.0,
"resetDate": "2026-03-01T00:00:00Z"
}
}Errors
When a request fails, the API returns a JSON body with an error string and an appropriate HTTP status code.
{
"error": "The provided API key is invalid or has been revoked."
}Common error responses
| Status | Description |
|---|---|
| 400 | Bad request — missing required fields or malformed body |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — insufficient credits or missing scopes |
| 404 | Not found — the requested job or resource does not exist |
| 429 | Rate limited — too many requests, retry after the reset window |
| 500 | Internal error — something went wrong on our end |
Rate Limits
Rate limits protect the API from abuse and ensure fair usage across all accounts. Limits are applied per API key.
| Endpoint | Limit |
|---|---|
| General | 100 requests / minute |
| Uploads | 10 concurrent uploads |
| Key management | 10 requests / minute |
Rate limit information is included in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1708523400When rate limited, the API returns 429 Too Many Requests. Use the X-RateLimit-Reset header (Unix timestamp) to determine when you can retry.
Need help?
Having trouble with the API? Reach out and we'll get you sorted.