Nubu
  • Features
  • How it works
  • Pricing
  • Integrations
  • FAQ
  • Contact
  • Docs
  • Blog
  • Log in
  • Log inSign up
    Nubu

    Creative automation for teams that ship campaigns, not busywork.

    Try for free
    Product
    FeaturesNubu MotionHow it worksPricingIntegrationsFAQ
    Company
    AboutContactDocsBlogLog in
    Legal
    Privacy policyTerms of serviceData deletion
    © 2026 Bear Studios · Nubu
    Introduction
    Templates overviewInstalling Nubu BuilderAdding template compsAdding propertiesAdding footageSetting defaultsExporting your templateUploading a template
    Flows overviewAvailable nodesAdding a nodeConnecting nodesWorking with dataTemplates and outputsUsing AI nodesBuilding a creative
    CampaignsTemplatesAssetsGlossariesRendersSettings
    Connecting Meta AdsConnecting Google AdsConnecting OpenAIConnecting Gemini AI StudioConnecting Anthropic
    AI Assistant overviewPrompt examplesAssistant settings and controls
    Nubu Motion scripts: the reference
    OverviewQuick startDifferences from After EffectsDeterminism
    JavaScript supportReferences and idsProject scriptsState and timeBudgets and errorsFormatting values
    GlobalTime conversionInterpolationVector mathsRandom and noiseColour conversionOther maths
    LayerCompPropertyKeyframePathGeometryProjectComponentConsoleComponentControlExpressionControl
    The property vocabularyAnimators propertiesAudio propertiesContent propertiesContents propertiesEffects propertiesExpression controls propertiesFills propertiesInstance controls propertiesLayer propertiesLayout propertiesMasks propertiesStrokes propertiesText style propertiesRig propertiesRigging propertiesRig controllers propertiesRigging weights propertiesRigging mesh propertiesRig items propertiesRig splines propertiesRig simulation propertiesRig properties propertiesRig drivers propertiesRig draw order propertiesTransform propertiesTrim path properties
    Error codesBudgets
    Changelog
    API overviewAuthentication and keysRate limits and errorsUploading assetsFoldersUploading templatesTriggering a flowRuns and creativesAPI Request deliveryCallbacks
    All docs
    API

    Rate limits and errors

    How fast you may call the API, and what every error looks like.

    Rate limits#

    • Each key may make 10 requests per second sustained, with a burst of 60.
    • Each organisation may trigger up to 60 flow runs per minute and begin up to 120 uploads per minute (every file in a batch counts). These caps apply across all of the organisation's keys.

    Over a limit the API answers 429 rate_limited with a Retry-After header (in seconds). Wait that long and retry. Polling a run once every few seconds, or uploading files one after another, stays comfortably inside the limits.

    The error shape#

    Every error has the same shape:

    {
      "error": {
        "code": "validation_failed",
        "message": "The request is invalid.",
        "details": [{ "field": "limit", "message": "Number must be greater than or equal to 1" }],
        "request_id": "0b2d1c4e-..."
      }
    }
    

    details is present when there is something specific to say: a list of { field, message } entries for a validation failure, or an object such as { "reason": "render_locked" } for a conflict.

    StatusCodeMeaning
    401invalid_keyThe key is missing, unknown, expired or revoked.
    403plan_requiredThe organisation is not on the Business+ plan, or its billing needs attention.
    403scope_requiredThe key lacks the route's scope (details.required_scope).
    404not_foundNo such resource in this organisation.
    409conflictThe request is valid but the resource's state does not allow it yet; details.reason says which: storage_full (no storage left, see below), render_locked or locked (a creative mid-render), upload_incomplete / not_uploading (an upload's bytes are not there yet, or there is nothing to complete), duplicate (a name or id already taken), idempotency_conflict (an Idempotency-Key reused with a different body), no_trigger / flow_unavailable / actor_unavailable (a flow that cannot be triggered).
    422validation_failedThe request itself is wrong; details lists each problem.
    413payload_too_largeThe request body is over 1 MB. File bytes go to the upload URL, never through the API.
    429rate_limitedOver a limit; Retry-After says how long to wait.
    500internal_errorSomething failed on Nubu's side. Retry, and quote the request id if it persists.

    A path that does not exist under /api/v1 answers 404 not_found in the same shape. Every error carries only these fields; nothing about Nubu's internals rides an error.

    When storage is full#

    The API never bypasses the organisation's storage allowance: it stops exactly where the app would, and says so.

    • Beginning an upload or a replacement (assets and templates, single or batch) whose file would not fit the remaining storage answers 409 conflict with details.reason storage_full; the message says how much is free. In a batch, that file's entry carries the error and the others proceed.
    • Approving or re-rendering a creative while storage is full answers 409 conflict with details.reason storage_full: new renders are paused.
    • Triggering a flow with auto_queue: true still builds (202), but the creatives wait in review: the answer and the run carry "queue_fallback": "storage" and a notice saying why. Approve them once space is free.
    • If an organisation goes over its allowance while renders are queued (output sizes are only known when they land), the queued creatives are failed with the storage message and the run reports them as render_failed; re-render them once space is free.

    Free space by deleting assets, templates or renders, through the API or the app: the accounting updates at once and the gates lift with it.

    Retrying#

    • Retry 429 after Retry-After, and 500 with a short backoff.
    • Never retry a 4xx other than 429 without changing the request.
    • Completing an upload twice, or triggering the same flow twice, creates nothing twice: a completed upload answers the same asset again, and each trigger is its own run. Use external_id on a trigger to recognise your own runs.