Skip to Content
Automation APIPlans, quotas & limits

Plans, quotas & limits

The API is plan-gated

API access is a paid feature. Two things follow from that:

  • POST /v1/keys answers 403 upgrade_required on a plan without it.
  • Every API-key-authenticated request re-checks the plan, so a downgrade takes effect immediately rather than at the next renewal.

GET /v1/usage is the authority, not this page. Quotas are set per deployment and change with plans; the numbers below describe the hosted backend at the time of writing. Read them from the API rather than hard-coding them.

GET /v1/usage

GET /v1/usage Authorization: Bearer pm_live_…
{ "period": "2026-08", "renderJobs": 42, "renderDurationMs": 3120450, "renderedMinutes": 18.4, "reservedRenderMinutes": 0.5, "apiRequests": 311, "assetProcessingBytes": 184320000, "limits": { "apiEnabled": true, "monthlyRenderMinutes": 60, "monthlyApiRequests": 1000, "maxActiveApiKeys": 5, "maxUploadBytes": 104857600 } }

Scope: usage:read.

FieldMeaning
periodThe UTC billing month, YYYY-MM. Counters reset when it rolls over.
renderedMinutesOutput duration committed by finished renders
reservedRenderMinutesHeld by jobs currently queued or processing
apiRequestsAPI-key-authenticated requests this period
assetProcessingBytesBytes ingested from image / media inputs
limits.apiEnabledCheck this first. false means keys will not work.

Gate your integration on limits.apiEnabled, not on the plan name. It is the single flag the server itself uses, and it accounts for a lapsed payment as well as a plan that never included the API.

Quotas on the hosted backend

QuotaAPI-enabled plan
Render minutes per month60
API requests per month1,000
Active API keys5
Asset ingest per input (by URL)The smaller of your plan cap (100 MB) and the hard 80 MB fetch limit — so 80 MB
File upload (POST /v1/assets)Your plan’s maxUploadBytes — 100 MB

A quota breach is 403 with a machine-readable code, not a silent truncation:

{ "code": "quota_exceeded", "kind": "render_minutes", "limit": 60, "used": 59.5, "message": "Monthly render minutes quota reached." }

How render minutes are counted

Minutes are reserved before the render and settled after it — the reserve is the template’s durationSeconds, converted to minutes.

This is why a job can be refused while renderedMinutes still looks comfortable: the check is renderedMinutes + reservedRenderMinutes + this job. Reserving up front is what stops two concurrent requests from both passing the same last-minute check and jointly overrunning the quota.

The reservation is released when the job reaches any terminal state, including cancelled and failed. A cancelled render costs you nothing.

Note that the reserve is based on the template’s output duration, not on how long the render takes. A 6-second clip that takes four minutes of CPU to produce still costs 0.1 minutes of quota.

Request counting

Only API-key requests are counted against monthlyApiRequests. Editor and dashboard traffic on a session JWT is not. Every /v1 call with a key counts — including the polling loop, which is a reason to prefer webhooks on a tight quota.

Uploaded and ingested bytes cost the same

assetProcessingBytes meters both routes to the same outcome: a file sent to POST /v1/assets, and an asset fetched from a URL you passed as an input. There is no cheaper path, deliberately — the choice between them should be about what your workflow is holding, not about billing.

Lifetime24 hours, then deleted
Counted againstassetProcessingBytes
Size caplimits.maxUploadBytes — over it is 413 naming the limit
TypesPNG, JPEG, WebP, GIF, SVG, MP4, WebM, QuickTime, MP3, WAV, M4A

Uploads are temporary because they exist to feed a render. For a permanent asset library, use the editor — /assets is a separate, session-authenticated surface and is not part of this API.

Importing a project

POST /v1/templates/import costs one API request and nothing else — no render minutes, no asset bytes. The bundle’s own asset blobs are never ingested, because the render service cannot use bytes that live on your disk; that is what the derived inputs are for.

The template limits below still apply to what the imported composition declares: a project at 8192px or a 90-minute composition is refused.

Rate limits

Separate from the monthly quota, and evaluated per account:

LimitValueAnswer
Render creation60 per minute429 rate_limit_exceeded
All requests300 per minute per IP429

Hard limits

These are not plan-dependent — they apply to every deployment.

LimitValue
Output formatMP4 (H.264) only
Output dimensions64–4096 px per side, at most 16 megapixels
Output fps1–120, integer
Template width / height≤ 8192
Template fps≤ 240
Template durationSeconds≤ 3600
Request body10 MB
Asset fetch80 MB
Template name120 characters
Template description500 characters
Page size on any list100
Idempotency-Key8–128 URL-safe characters

Not supported

Alpha in outputMP4 has none — transparency flattens onto the comp background
AudioNot muxed by the hosted renderer yet
Non-MP4 formatsWebM, ProRes, PNG sequence, Lottie, SVG and GIF are desktop-export only
Plugin effectsSkipped silently — the hosted renderer has no plugins installed
Layer or keyframe CRUDNot exposed; the API renders templates, it does not edit them
Reading a template back as a .motionImport is one-way; there is no export endpoint
Last updated on