API docs · Tools
SDKs
Official Python and TypeScript SDKs (0.2) with uploads, polling, retries, typed errors and webhook verification. Go, Rust and Ruby SDKs at 0.1.
All SDKs are open source in FrameQuery/framequery-sdks. They follow one design, so method names, field names and error classes match across languages, and they match the API: every field is the JSON name in the language's usual case (jobId is job_id in Python, jobId in TypeScript).
| Language | Version | Install |
|---|---|---|
| Python 3.9+ | 0.2 | pip install framequery |
| TypeScript / JavaScript (Node 18+, Deno, Bun, edge) | 0.2 | npm install framequery |
| Go | 0.1 | go get github.com/framequery/framequery-go |
| Rust | 0.1 | cargo add framequery |
| Ruby | 0.1 | gem install framequery |
The Go, Rust and Ruby SDKs are at 0.1 and move to the 0.2 surface next; see their READMEs in the SDK repository. This page documents 0.2.
What the SDKs do for you
- Uploads straight to storage, streamed from disk and never read into memory whole: one
PUTup to 32 MiB, resumable 16 MiB chunks above that, resumed automatically after network errors. - Waiting on
statewith sensible backoff;wait()returns the terminal job and never raises forfailed. - Retries on
429,503and connection errors, honouringRetry-After, with an idempotency key on every create so a retry never creates a second job. - Typed errors carrying
code,doc_url/docUrl,param,request_id/requestIdandretry_after/retryAfter. - Webhook verification without extra dependencies.
Client
from framequery import FrameQuery, AsyncFrameQuery
fq = FrameQuery() # api_key defaults to FRAMEQUERY_API_KEY
fq = FrameQuery(api_key="fq_live_...", timeout=60, max_retries=3)
async with AsyncFrameQuery() as afq: # same methods, as coroutines
job = await afq.jobs.get(job_id)Keyless calls (pricing.get, pricing.estimate, formats.list) and webhook verification work without a key.
Process in one call
process() creates the job (upload or URL), waits for it, and fetches whichever results completed.
result = fq.process("interview.mp4", processing_mode="all", metadata={"assetId": "A003_C008"})
result.job # Job (state "completed")
result.transcript # Transcript, or None if that stage was skipped
result.scenes # list[Scene], or None if that stage was skippedIf the job ends failed or expired, process() raises JobFailedError (with job, code and stage); cancelled raises JobCancelledError. Stages that completed are still readable through fq.jobs.transcript(...) and fq.jobs.scenes(...).
Methods
| Operation | Python | TypeScript |
|---|---|---|
| Create and upload a file | fq.jobs.create(path_or_file, ...) | fq.jobs.create(file, {...}) |
| Create, upload elsewhere | fq.jobs.create_upload(file_name=...) | fq.jobs.createUpload({ fileName }) |
| Create from a URL | fq.jobs.create_from_url(url, ...) | fq.jobs.createFromUrl(url, {...}) |
| Batch | fq.batches.create(clips, mode=...) | fq.batches.create(clips, { mode }) |
| Get | fq.jobs.get(job_id) | fq.jobs.get(jobId) |
| List (auto-paging) | for job in fq.jobs.list(state="completed") | for await (const job of fq.jobs.list({ state: "completed" })) |
| Wait | fq.jobs.wait(job_id, timeout=7200) (seconds) | fq.jobs.wait(jobId, { timeout }) (ms) |
| Cancel | fq.jobs.cancel(job_id) | fq.jobs.cancel(jobId) |
| Transcript (JSON) | fq.jobs.transcript(job_id) | fq.jobs.transcript(jobId) |
| Transcript (SRT, VTT, TXT) | fq.jobs.transcript(job_id, format="srt") | fq.jobs.transcript(jobId, { format: "srt" }) |
| Scenes | fq.jobs.scenes(job_id) | fq.jobs.scenes(jobId) |
| Balance | fq.quota.get() | fq.quota.get() |
| Price table | fq.pricing.get() | fq.pricing.get() |
| Price estimate | fq.pricing.estimate(duration_seconds, ...) | fq.pricing.estimate(durationSeconds, {...}) |
| Formats | fq.formats.list() | fq.formats.list() |
| Verify a webhook | fq.webhooks.verify(payload, headers) | await fq.webhooks.verify(payload, headers) |
| Verify and parse | fq.webhooks.unwrap(payload, headers) | await fq.webhooks.unwrap(payload, headers) |
| Delivery log | fq.webhooks.list_deliveries(job_id=...) | fq.webhooks.listDeliveries({ jobId }) |
| Test delivery | fq.webhooks.send_test(url) | fq.webhooks.sendTest(url) |
jobs.get sends include=none by default; results come from transcript() and scenes().
Errors
| Class | When |
|---|---|
InvalidRequestError | 400, 413, 415, 422; param names the field |
AuthenticationError | 401, or no key configured (raised before any request) |
InsufficientCreditsError | 402 |
PermissionDeniedError | 403 |
NotFoundError | 404 NOT_FOUND |
ResultsUnavailableError | 404 RESULTS_UNAVAILABLE: stop; see reason |
ConflictError | 409 JOB_NOT_CANCELLABLE, IDEMPOTENCY_IN_PROGRESS |
ResultsNotReadyError | 409 RESULTS_NOT_READY: try later |
RateLimitError | 429 |
InternalServerError | 5xx |
APIConnectionError, APITimeoutError | No HTTP response |
JobFailedError, JobCancelledError, WaitTimeoutError | From process() / wait() |
UploadError | The storage upload failed |
WebhookVerificationError | Bad signature, headers or timestamp |
All API errors derive from APIError, and everything from FrameQueryError.
from framequery import InsufficientCreditsError, RateLimitError, ResultsNotReadyError
try:
transcript = fq.jobs.transcript(job_id)
except ResultsNotReadyError as err:
print("not yet, retry in", err.retry_after, "s")
except InsufficientCreditsError as err:
print("top up:", err.doc_url)Webhooks
See Webhooks for complete handlers. The secret defaults to FRAMEQUERY_WEBHOOK_SECRET. In Python, framequery.webhooks.verify(payload, headers, secret) and framequery.webhooks.unwrap(...) also work without a client. Always pass the raw request body.
Other languages and plain HTTP
Any HTTP client works: see the API reference for every endpoint, or generate a client from the OpenAPI document.