Skip to main content

Usage and limits

note

Every example uses https://video.pyler.ai and an API key in $VUF_API_KEY. See Authentication.

Check your quota​

GET /api/v1/usage returns the current quota and remaining allowance of the organization that owns the API key.

curl -s https://video.pyler.ai/api/v1/usage \
-H "Authorization: Bearer $VUF_API_KEY"
Response (200)
{
"plan": "trial",
"videos": { "used": 12, "limit": 100 },
"minutes": { "used": 340, "limit": 1000 },
"ads": { "used": 2, "limit": 20 },
"summaries": { "used": 5, "limit": 30 },
"remaining": { "videos": 88, "minutes": 660, "ads": 18, "summaries": 25 }
}
planstring

Name of the organization's quota plan.

videosUsage

Videos counted against the plan: used and limit.

minutesUsage

Video minutes counted against the plan: used and limit.

adsUsage

Ad creatives counted against the plan: used and limit.

summariesUsage

Custom summary requests counted against the plan: used and limit. Counted separately from videos.

remainingRemaining

videos, minutes, ads and summaries still available.

StatusMeaning
404No quota plan has been assigned to this organization yet, for example while the account is still being provisioned. Contact support if it persists.
503Quota datastore temporarily unavailable. Retry.

What the spec documents about how quota is consumed:

  • A custom summary request counts as one summary against summaries, not against videos, including one that ends as rejected. A request that ends as failed, or is still queued when its video is deleted, is returned to the quota. When the summary quota is exhausted the POST answers 422 with code: quota_exceeded_summaries and nothing is created.
  • An MRSS feed whose plan no longer permits MRSS shows plan_blocked: true; re-enabling it returns 422 with code: mrss.disabled.

Request limits​

Caps and defaults declared in the OpenAPI spec. Values outside a range fail schema validation with a 422.

Indexing​

EndpointLimit
GET /api/v1/videoslimit 1–100, default 50. Cursor pagination.
GET /api/v1/videos/{video_id}scene_limit 1–200, default 50; scene_offset ≥ 0.
GET /api/v1/videos/{video_id}/sceneslimit 1–200, default 50; offset ≥ 0.
GET /api/v1/videos/{video_id}/scenes/streamServer-side maximum stream duration; the stream ends with {"event": "timeout"} when reached. Reconnect for a fresh snapshot.
POST /api/v1/videos/batchitems up to 500 rows; total 0–500; per row url ≤ 2048 characters, title ≤ 500. Over-length items returns the 422 list form.
POST /api/v1/videos/batch/uploadRecommended about 10 files or fewer per request. Safety backstop: 413 above 500 files, counted per request and across a reused batch_id.
POST /api/v1/videos/batch/upload-sheet413 above the 10 MB sheet size cap or above 500 valid URL rows. 422 above 10000 data rows, with too many columns, or when the sheet expands past the uncompressed-size cap. Up to 100 dropped rows are listed in rejected_rows.
POST /api/v1/videos/{video_id}/custom-summariesinstructions ≤ 2000 characters; cast ≤ 100 members, each name 1–200 characters with ≤ 20 aliases; language ≤ 35 characters, language-tag pattern.
GET /api/v1/videos/{video_id}/custom-summarieslimit 1–100, default 50. Cursor pagination.
PATCH /api/v1/mrss-feeds/{feed_id}poll_interval_s one of 3600, 21600, 86400; label ≤ 200 characters, no control characters. Empty body is 422.
GET /api/v1/mrss-feeds/{feed_id}/itemslimit 1–200, default 50; offset ≥ 0.
EndpointLimit
POST /api/v1/search, POST /api/v1/search/deepk 1–100, default 20; scene_pool 10–200, default 60; min_relevance 0–10, default 3. video_id must be a UUID. No cursor: results are bounded by k per column.
POST /api/v1/search/multimodalk default 20; scene_pool default 60.

Ads​

EndpointLimit
POST /api/v1/ads/matchper_query_k 2–400, default 100; max_candidates 5–400, default 100; max_per_video 1–50, default 10.

Live​

EndpointLimit
POST /api/v1/live/streams, PATCH /api/v1/live/streams/{stream_id}segment_n 1–60, default 8; poll_interval_s 1–3600 seconds, default 6.

Rate limits​

Throughput is bounded by the organization quota above, the per-request caps, and the size backstops (413) on batch and sheet uploads. One request that is too large is rejected whole; it is not throttled or partially processed.

Request-rate limits may also be applied per organization, and the analysis stages behind search, matching and screening share capacity across customers. Build clients that treat 429 Too Many Requests like any other transient error: back off (honouring a Retry-After header when present) and retry under an overall deadline. Keep polling intervals at a few seconds, prefer the streaming endpoints over tight polling loops, and batch submissions instead of sending one request per video.

Timeouts and retries​

The following guidance is written for brand-safety screening and applies to every call that does work after the request is accepted.

A timeout is an unknown outcome, not a failure. Once the API has accepted a request it runs to completion and stores the result, whether or not your connection is still open. Only the response is lost. On a timeout, do not assume the work was not done: look the result up before resubmitting.

Recommended client timeout: 120 seconds. Most calls return in seconds; the ceiling is for the tail, not the typical case.

Raising it past 5 minutes has no effect. A request that has produced no data for 5 minutes is closed with 504. A streaming response that keeps producing lines can run longer, while a quiet request ends at the 5-minute mark. Streaming endpoints (/api/v1/ads/match, /api/v1/videos/{video_id}/scenes/stream, /api/v1/live/streams/{stream_id}/events, and /api/v1/search with judge: "text") return NDJSON; parse them line by line as data arrives.

Backoff. One or two retries under an overall deadline is enough. A retry occupies capacity that would otherwise serve your other requests and counts toward any rate limit, so keep retries deliberate rather than routine.

What is safe to retry​

SituationRetry?Why
500 on any endpointYesDocumented as "upstream service error, usually safe to retry".
503 on GET /api/v1/usageYesQuota datastore temporarily unavailable.
503 on POST /api/v1/videos/{video_id}/custom-summariesYesThe request was not queued and nothing will be generated for it. Each retry creates a new request.
Timeout on POST /api/v1/videos (URL)YesResubmitting a URL that is still being indexed for you returns the existing video_id / job_id with created: false instead of starting a second pipeline.
Timeout on POST /api/v1/videos/batch (kind: "url")Yes, with careRows whose URL is already being indexed are linked to the existing job (created: false). failed rows, including dispatch_failed, are meant to be resubmitted; confirm with GET /api/v1/videos/{video_id} first.
Timeout on POST /api/v1/videos/upload or /batch/uploadNot blindlyUploads have no natural dedup key. Poll GET /api/v1/videos/batch/{batch_id} first and resend only the files still missing. Skip items whose error is dispatch_failed: their video row is already committed and resending indexes the file twice.
504 from the edgeLook up firstThe work may have completed. Check the job, video, batch or screening result before sending again.
409, 413, 422NoThe request itself is wrong. Change it (see below).

Oversized and invalid requests​

413 Payload too large​

EndpointTriggerFix
POST /api/v1/videos/batch/uploadMore than 500 files, counted per request and across a reused batch_id.Split into requests of about 10 files.
POST /api/v1/videos/batch/upload-sheetSheet larger than 10 MB, or more valid URL rows than one batch accepts (currently 500).Split into multiple sheets.

422 Unprocessable​

Two body shapes exist, so branch on the type of detail before reading it:

  • Envelope form: {"detail": "message", "code": "stable.code"}. code is present when the failure has a stable machine-readable reason and absent otherwise. Examples in this guide: quota_exceeded_summaries, custom_summary_no_base_summary, custom_summary_no_org, mrss.bad_interval, mrss.bad_label, mrss.bad_status, mrss.bad_index_status, mrss.disabled.
  • Schema-validation list form: {"detail": [{"loc": [...], "msg": "..."}]}, with no code. Any endpoint that declares a body can return it; sending an over-length items array to POST /api/v1/videos/batch is the common case. A PATCH /api/v1/mrss-feeds/{feed_id} with an empty object returns this form too.

Sheet uploads add one sibling key: when code is no_valid_urls, the 413 / 422 body also carries rejected, the number of rows dropped while parsing (never recorded in any batch).

422 on upload-sheet
{ "detail": "no valid url rows in sheet", "code": "no_valid_urls", "rejected": 17 }

Other documented 422 triggers:

  • GET /api/v1/videos with a malformed reindex_run_id UUID or a reindexed_since without a UTC offset.
  • POST /api/v1/live/streams or PATCH with a new url whose manifest is unreachable or not a valid playlist.
  • POST /api/v1/videos/batch/upload-sheet with a file that is not .csv / .xlsx, has no url column, is malformed or corrupted, has no valid URL rows, exceeds 10000 data rows or the column / uncompressed-size caps.

Oversized error bodies​

One code is produced by the gateway itself: upstream_error_too_large means a downstream error body was too large to relay. The status code is preserved but the per-item reasons are gone. Retry with fewer items to get the detailed response.

See Errors for the full envelope and the common status table.

Next steps​