Credits and limits
What each feature charges in credits, the ceilings every plan enforces, and the codes an API call returns when one of them refuses the work.
Two independent things can stop a job: the workspace's credit balance and the
plan's ceilings. Both refuse before any work is queued, and both use the same
warning-* vocabulary whether they arrive on a 400 or on a failed job.
Credits
Credits are the display unit and tokens the accounting unit: 1 credit is 1000 tokens. Every endpoint that reports a balance reports both, so a client never has to know the ratio:
curl https://app.shortfast.com/api/v1/workspace \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx"{
"data": {
"id": "k9WmT3xR2pQ7nV4bd",
"plan": "pro",
"credits": { "credits": 42500, "tokens": 42500000 },
"limits": { },
"usage": { }
}
}What each feature costs
GET /reference/credit-costs returns the table that applies to the connected
workspace, never both, live, in both units and with the unit each cost applies
to. Read it rather than hardcoding: prices move.
Accounts created on or after October 26, 2026
| Feature | Credits | Tokens | Per |
|---|---|---|---|
| Video | 24 | 24000 | minute |
| AI avatar | 300 | 300000 | minute |
| Animated b-rolls | 375 | 375000 | minute; High quality 1,125 a minute |
| AI image | 1 | 1000 | image |
| Script or description | 0.08 | 80 | generation |
The Video charge replaces the render, captions, scenes and premium voice
charges of the older table, so renders and the premium voice cost nothing extra.
A one-minute video built from stock footage comes to about 24 credits, and one
in an AI image style to about 37.
Accounts created before October 26, 2026
These accounts keep the previous table:
| Feature | Credits | Tokens | Per |
|---|---|---|---|
| Video render | 1.8 | 1800 | minute |
| Subtitles and b-rolls | 1.2 | 1200 | minute |
| AI avatar | 300 | 300000 | minute |
| AI image | 1 | 1000 | image |
| Script or description | 0.08 | 80 | generation |
| Premium voice | 3 | 3000 | minute, on top of the standard voice |
Duration-based costs are charged against the video's own length, so a 40 second
render costs about two thirds of a minute's worth. On this table the premium
voice line is what voiceProvider: "gemini" adds over openai, per minute of
narration.
Estimate before you queue: multiply the video's durationSeconds by the per-
minute cost, add an AI image per scene for type: ai, and compare against
credits.tokens. An empty balance refuses the call with warning-no-credits.
Plan limits
GET /workspace returns the ceilings for the connected workspace. A limit the
plan does not cap is null, which means unlimited.
| Field | What it caps |
|---|---|
maxRendersPerMonth | Renders started in a rolling 30 day window. It caps generations over the same window too |
maxAutomations | Blueprints that can exist at once |
maxProfiles | Publishing profiles that can exist at once |
maxSocialAccounts | Social accounts connected across all profiles. 0 means the workspace can download but not publish |
maxVideoDurationSeconds | How long a source video may be to run through generation |
maxRenderSeconds | How long a video may be to render |
storageBytes | Total stored bytes across the library. Deleting a video credits its bytes back |
watermark | true when renders on this plan carry a watermark |
Roughly, by plan:
| Plan | Renders and generations per month | Automations | Profiles | Social accounts | Max render length | Watermark | Publishing |
|---|---|---|---|---|---|---|---|
| Free | 3 | None | 1 | None | 3 minutes | Yes | No, download only |
| Starter | Uncapped | 1 | 1 | 1 | 10 minutes | No | Yes |
| Pro | Uncapped | 3 | 1 | 3 | 12 minutes | No | Yes |
| Advanced | Uncapped | 6 | 2 | 6 | 15 minutes | No | Yes |
Treat that table as orientation, not as a contract. GET /workspace is the live
answer for the workspace your key resolved to, including any storage ceiling, and
it is the only thing that stays right when plans change.
Long videos (videoDuration: "long", 8 to 15 minutes) need an Advanced plan and
are otherwise refused with warning-plan-duration. A workspace on a paid trial
can also run into a daily cap of 5 generations, which arrives as
warning-trial-daily-limit.
Current usage
GET /stats reports the counters against those ceilings, so you can throttle
yourself instead of discovering the limit on a refused call:
{
"data": {
"plan": "pro",
"videos": 37,
"automations": { "used": 2, "limit": 3 },
"clips": 118,
"generations": { "used": 24, "limit": null, "since": "2026-07-09T11:42:03.000Z" },
"renders": { "used": 19, "limit": null, "since": "2026-07-09T11:42:03.000Z" },
"storage": { "used": 3187441664, "limit": 50000000000 },
"credits": { "tokens": 42500000, "credits": 42500 },
"maxRenderSeconds": 720,
"maxDurationSeconds": 7200
}
}since is the start of the rolling window the generations and renders
counters cover. A limit of null means the plan does not cap that counter.
How a limit surfaces
A refusal reaches you in one of two shapes, with the same code in both.
Synchronously, when the call is refused before the job is queued, as a 400:
{
"error": {
"status": 400,
"code": "warning-plan-renders",
"message": "You have reached the limit of 3 renders per month on your current plan. Upgrade to render more videos."
}
}Asynchronously, when a running job hits it, on the job document:
process.error, render.error or publish.error, each carrying code,
message, blocked and action. The same object rides on the
video.generation_failed, video.render_failed and
video.publish_failed events.
blocked: true marks an expected limit rather than a crash, so a client can
render it as a quota instead of a red failure. action is the fix to offer:
| Code | Title | Action | Blocked |
|---|---|---|---|
warning-trial-daily-limit | Daily trial limit reached | trial | true |
warning-trial-animated-limit | Animated trial limit reached | trial | true |
warning-no-credits | Out of credits | credits | true |
warning-plan-duration | Video too long for your plan | upgrade | true |
warning-plan-render-time | Video too long to render on your plan | upgrade | true |
warning-plan-renders | Monthly render limit reached | upgrade | true |
warning-plan-generations | Monthly generation limit reached | upgrade | true |
warning-plan-automations | Automation limit reached | upgrade | true |
warning-plan-publish | Publishing needs a paid plan | upgrade | true |
warning-plan-storage | Storage limit reached | upgrade | true |
warning-source-not-ready | Source video is not ready | none | true |
warning-publish-not-configured | No publishing destination | configure | true |
warning-publish-no-accounts | No connected account | connect | true |
warning-publish-auth | Reconnect your social account | connect | true |
warning-publish-duplicate | Already posted | none | true |
warning-publish-rate-limited | Posting limit reached | schedule | true |
warning-publish-rejected | Post refused | none | true |
voice-refused | The voiceover couldn't be made | retry | false |
voice-unavailable | Voiceover unavailable | retry | false |
no-scenes | No scenes | retry | false |
media-generation-failed | Visuals failed | retry | false |
unsupported-video | Unsupported video | retry | false |
timeout | Took too long | retry | false |
media-stalled | A scene didn't load | retry | false |
publish-timeout | Publishing timed out | retry | false |
publish-unavailable | Accounts unreachable | retry | false |
internal-error | Something went wrong | retry | false |
schedule means the platform takes posts again at the time in retryAt, and
retry means trying again may work. trial means the limit lifts when the
plan starts: end the trial early from the billing page rather than upgrading.
warning-plan-storage refuses a generation when the workspace is at its
storage ceiling. Free space by deleting videos, which credits their bytes back
to the quota.
GET /reference/failure-codes returns exactly this list, in this order, so a
client can render an unfamiliar code by looking it up instead of falling back
to a raw message.
A failed job can also carry a warning-* code that isn't in the list, such as
warning-invalid-input when a faceless video has no captions and transcribe
off (see Videos). It comes with blocked: true,
action: "none" and a message that says what to change.