Docs
BlogHomeStart building

Spending & Rate Limits

Keep every project's spending and traffic under control.


Three independent controls, all per project. Every limit of an account and a project, and which ones Totalum can raise, is on Limits.

  • Credit limits — a monthly cap on development credits (prompts, publishes, builder actions), on infrastructure credits (what the running app uses), and on the development environment's infrastructure (the preview, its database and its scheduled jobs; 100 credits a month unless you change it).
  • Feature limits — switch each built-in feature of the app (AI, e-mail, web scraping…) on or off and give it its own monthly budget.
  • Rate limit — requests per minute each visitor (IP) may send to the published site.

A refused call never reaches a provider and costs nothing. Budgets reset on the 1st of each month (UTC).

#Get Credit Limits

GET/api/v1/vcaas/projects/:projectId/credit-limitsapi-keyFree

The project's monthly caps and what it spent this month (UTC), per category — the same figures the caps are enforced on:

  • development — everything the AI agent's prompts and the project's builder actions (publishes, rebuilds, restores, exports, code edits…) were charged this month;
  • infrastructure — what the running app used this month: hosting, database and every built-in feature (the total of the all feature in Feature Limits);
  • developmentInfrastructure — the part of it used by the development environment: the preview, the development database, the preview's scheduled jobs and the built-in calls made from the preview (never the AI agent's own prompts, which are development credits).

All start again at 0 on the 1st of each month, 00:00 UTC.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.creditLimits.maxDevelopmentCreditsPerMonthnumber | nullThe development cap; null = no cap
data.creditLimits.maxInfrastructureCreditsPerMonthnumber | nullThe infrastructure cap; null = no cap
data.creditLimits.maxDevInfrastructureCreditsPerMonthnumber | nullThe development environment's cap (default 100); null = no cap. Absent when it cannot be read right now
data.usedThisMonthobject | null{ development, infrastructure, developmentInfrastructure } credits spent this month; null when it cannot be read right now
data.devInfrastructureStoppedbooleantrue while the development environment is stopped at its cap

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/credit-limits
Success · 200 OK
json
{ "errors": null, "data": { "creditLimits": { "maxDevelopmentCreditsPerMonth": 5000, "maxInfrastructureCreditsPerMonth": 300, "maxDevInfrastructureCreditsPerMonth": 100 }, "usedThisMonth": { "development": 42.5, "infrastructure": 3.1, "developmentInfrastructure": 0.4 }, "devInfrastructureStopped": false } }

#Update Credit Limits

PATCH/api/v1/vcaas/projects/:projectId/credit-limitsapi-keyFree

Set the project's monthly caps. null removes a cap. Defaults for a project created through the API: a development cap of 5,000 credits a month and an infrastructure cap of 300 credits a month; every project's development environment has a cap of 100 credits a month. The first two can also be set when the project is created (Launch Project, Create Project).

Values: a cap is a number above 0, or null for no cap; 0 or a negative number is refused with 400 INVALID_LIMIT. The infrastructure caps (the app's and the development environment's) are applied as the whole number at or above the value, at least 1: 0.4 is applied as 1 and 12.5 as 13. The development cap is applied exactly as sent.

At the development cap a new prompt, and any paid builder action (a publish, a rebuild…), is refused with 403 PROJECT_CREDIT_LIMIT_REACHED and costs nothing. A prompt already running when its charges cross the cap is stopped: it finishes the step in progress, wraps up so that what it built works, and ends with PROJECT_CREDIT_LIMIT_REACHED. It can go past the cap by the step that crossed it plus at most 5 credits, and every credit it used is charged.

At the infrastructure cap (it counts hosting, database, files, AI, e-mail and every other built-in) there are two steps, each for that project only:

  1. At the cap the app's built-in features are refused with 429 FEATURE_LIMIT_REACHED (errorDetails.feature = all): AI, e-mail, file uploads, web scraping and search, browser sessions, document scanning, PDFs and screenshots, speech, real-time rooms, vectors, connected accounts, and creating or changing scheduled jobs. The site, its database, hosting and stored files keep running.
  2. At the cap plus a reserve of 10 % of the cap, at least 5 credits (300 → 330, 20 → 25, 2 → 7) the project is paused: its site and preview show a paused page and its API keys answer 429 PROJECT_CREDIT_LIMIT_REACHED (errorDetails: category: "infrastructure", limit, pauseAt).

Raising the cap re-checks both at once: the pause lifts when the new cap plus its reserve is above this month's spend, and the built-in features come back when the new cap is above it. Removing the cap (null) lifts both; both also end at the start of next month (UTC). The reserve is fixed; it cannot be changed.

At the development environment's cap (maxDevInfrastructureCreditsPerMonth, 100 credits a month by default; it counts the preview, the development database, the preview's scheduled jobs and the built-in calls made from the preview) the development environment of that project stops: the preview shows a paused page, the preview's scheduled jobs are skipped, and built-in calls made from the development environment answer 429 DEV_INFRASTRUCTURE_LIMIT_REACHED. The published site, its database and its scheduled jobs keep running, and the AI agent can still work on the project. It runs again when:

  • you send the agent a prompt: the development environment gets one more full cap from this month's spend (with a cap of 100 and 104 spent, it stops again at 204);
  • you raise the cap above this month's development-environment spend, or remove it (null);
  • the next month starts (UTC).

A sudden jump in database usage is charged like any other usage and is not paused on its own: you get an alert (Unusual database usage) and it continues until the infrastructure cap plus its reserve pauses the project. Set one on every project.

The first time a project reaches a cap in a month you get the project.credit_limit.reached webhook ({ projectId, category, spent, limit }) and an e-mail — once per project, per cap and per month, however many requests are refused. The infrastructure cap tells you at each step, once per step and month: the payload adds stage ("builtins_stopped" at the cap, "project_paused" at the pause) and pauseAt (the spend that pauses the project). The development environment's cap sends category: "development_infrastructure", stage: "dev_stopped" and stopAt (the spend that stopped it), once a month; a later stop in the same month is told in the project's chat. A new cap applies from the next prompt; a prompt already running keeps the cap it started with.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
maxDevelopmentCreditsPerMonthnumber | nullNo¹Positive number, or null for no cap
maxInfrastructureCreditsPerMonthnumber | nullNo¹Positive number, or null for no cap
maxDevInfrastructureCreditsPerMonthnumber | nullNo¹The development environment's cap: positive number, or null for no cap

¹ At least one of the three.

Response fields

FieldTypeDescription
data.creditLimits.maxDevelopmentCreditsPerMonthnumber | nullWhen sent: the new development cap
data.creditLimits.maxInfrastructureCreditsPerMonthnumber | nullWhen sent: the new infrastructure cap
data.creditLimits.maxDevInfrastructureCreditsPerMonthnumber | nullWhen sent: the development environment's cap as applied (a whole number, at least 1)

Example request

curl -X PATCH \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"maxDevelopmentCreditsPerMonth":500,"maxInfrastructureCreditsPerMonth":null}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/credit-limits
Success · 200 OK
json
{ "errors": null, "data": { "creditLimits": { "maxDevelopmentCreditsPerMonth": 500, "maxInfrastructureCreditsPerMonth": null } } }

Error codes

CodeHTTPMeaning
MISSING_LIMIT_FIELDS400No cap was sent
INVALID_CREDIT_LIMITS400The body is not an object
INVALID_LIMIT400A cap is not a positive number or null (0 included)
OWNER_ONLY403Only the account owner can change the caps

#List Projects at Their Limit

GET/api/v1/vcaas/projects/credit-limits/at-limitapi-keyFree

The account's projects that reached a monthly credit limit this month (UTC), with their caps and spend — the list behind the platform's "projects at their monthly limit" notice. Raise one with Update Credit Limits.

Response fields

FieldTypeDescription
data.projectsobject[]The projects at a limit (empty when none)
data.projects[].projectIdstringThe project id
data.projects[].labelstring | nullIts display name
data.projects[].createdViastring | null"ui" (the platform) or "api"
data.projects[].creditLimitsobject{ maxDevelopmentCreditsPerMonth, maxInfrastructureCreditsPerMonth }
data.projects[].usedThisMonthobject{ development, infrastructure } credits spent this month
data.projects[].atLimitobject{ development, infrastructure }: true for each limit reached
data.resetsAtstringWhen the limits reset (the start of next month, UTC)
data.canEditbooleanWhether this caller may raise them (the account owner)

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/credit-limits/at-limit
Success · 200 OK
json
{ "errors": null, "data": { "projects": [{ "projectId": "barber-bookings", "label": "Barber bookings", "createdVia": "api", "creditLimits": { "maxDevelopmentCreditsPerMonth": 5000, "maxInfrastructureCreditsPerMonth": 300 }, "usedThisMonth": { "development": 812.4, "infrastructure": 300.2 }, "atLimit": { "development": false, "infrastructure": true } }], "resetsAt": "2026-11-01T00:00:00.000Z", "canEdit": true } }
End of List Projects at Their LimitNext endpointGETGet Feature Limits

#Get Feature Limits

GET/api/v1/vcaas/projects/:projectId/limitsapi-keyFree

Every built-in feature of the app: whether it is on, its monthly budgets and what it used this month. Every feature is on by default, with no budget of its own.

FeatureCovers
aiEvery AI call below
ai_textChat, structured output, vision, embeddings, reranking
ai_imageImage generation and editing
ai_videoVideo generation
speechSpeech-to-text and text-to-speech
webWeb scraping, crawling, search, extraction
browserCloud browser sessions
scanDocument scanning (OCR)
storageFile uploads and stored files
emailSending e-mail
pdfPDFs, screenshots, SEO pings
vectorsVector search
realtimeReal-time rooms
cronScheduled job runs
mobileMobile app builds
databaseThe database (cannot be switched off)
workerThe site itself (cannot be switched off)
allEverything: one switch and one total monthly budget, in credits (the infrastructure cap: spent, the built-in features stop; plus its reserve, the project is paused)

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.planstringThe account's plan
data.featuresobjectFeature → its entry
data.features.<feature>.enabledbooleanfalse = switched off
data.features.<feature>.parentstring | undefined"ai" for ai_text, ai_image, ai_video (the parent's switch and budget also apply)
data.features.<feature>.monthlyCreditCapnumber | undefinedBudget in credits, when set
data.features.<feature>.monthlyUnitCapnumber | undefinedBudget in the feature's unit, when set
data.features.<feature>.unitstringcalls, recipients, pages, bytes, session_minutes, room_minutes, executions, builds, rows, requests or credits
data.features.<feature>.consumedobject{ credits, units } this month
data.features.<feature>.periodobject{ from, to }: the month
data.features.<feature>.planCeilingobjectHighest budget your plan allows: { monthlyCreditCap, monthlyUnitCap } (null = none)
data.features.<feature>.sourcestringplan (default), user (you set it) or ceo (set by Totalum support)

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/limits
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "plan": "starter",
    "features": {
      "ai_video": { "enabled": false, "parent": "ai", "unit": "calls", "consumed": { "credits": 0, "units": 0 }, "period": { "from": "2026-10-01T00:00:00.000Z", "to": "2026-11-01T00:00:00.000Z" }, "planCeiling": { "monthlyCreditCap": null, "monthlyUnitCap": 20 }, "source": "user" },
      "all": { "enabled": true, "monthlyCreditCap": 300, "unit": "credits", "consumed": { "credits": 12.4, "units": 0 }, "period": { "from": "2026-10-01T00:00:00.000Z", "to": "2026-11-01T00:00:00.000Z" }, "planCeiling": { "monthlyCreditCap": null, "monthlyUnitCap": null }, "source": "user" }
    }
  }
}

The answer lists every feature; the example shows two.

Concurrency. Besides budgets, two features have per-project concurrency limits: browser (3 open sessions, 1 on the free plan; a call up to 60 s) and web (1 call at a time, up to 10 s). A call over the limit waits in a fair queue, then answers 429 CONCURRENCY_QUEUE_TIMEOUT (or CONCURRENCY_QUEUE_FULL) with errorDetails.retryAfter. A project makes at most 100 built-in calls per second in total; above it a call answers 429 RATE_LIMITED with Retry-After.

Busy hours. When an app spends half of what would trigger the emergency stop within one hour, its image, video and speech AI calls answer 429 AI_MEDIA_PAUSED_THIS_HOUR until that hour ends (UTC), with Retry-After; text AI and everything else keep working. They come back by themselves at the next hour; releasing an emergency-stop pause also brings them back.

End of Get Feature LimitsNext endpointPUTSet Feature Limits

#Set Feature Limits

PUT/api/v1/vcaas/projects/:projectId/limitsapi-keyFree

Switch features on or off and set monthly budgets. Send only the features you change; null removes a budget. Account owner only. Setting the budget of all also sets the project's infrastructure credit limit.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
featuresobjectYesFeature → { enabled?, monthlyCreditCap?, monthlyUnitCap? }

Response fields

FieldTypeDescription
data.planstringThe account's plan
data.featuresobjectEvery feature after the change, as in Get Feature Limits

Example request

curl -X PUT \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"features":{"ai_video":{"enabled":false},"ai":{"monthlyCreditCap":200},"all":{"monthlyCreditCap":500}}}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/limits
Success · 200 OK
json
{ "errors": null, "data": { "plan": "starter", "features": { "ai_video": { "enabled": false, "parent": "ai", "unit": "calls", "consumed": { "credits": 0, "units": 0 }, "period": { "from": "2026-10-01T00:00:00.000Z", "to": "2026-11-01T00:00:00.000Z" }, "planCeiling": { "monthlyCreditCap": null, "monthlyUnitCap": 20 }, "source": "user" } } } }

What the app's code gets when a feature is refused (show a message, do not retry):

App errorHTTPMeaning
FEATURE_DISABLED403The feature (or ai, or all) is switched off; errorDetails.feature names it
FEATURE_LIMIT_REACHED429Its budget is spent; errorDetails: feature, limit, used, unit, resetsAt
AI_MEDIA_PAUSED_THIS_HOUR429The app used a lot of credits this hour, so image, video and speech AI wait until the hour ends (UTC); Retry-After and errorDetails.resetsAt say when. Text AI and everything else keep working

Error codes

CodeHTTPMeaning
UNKNOWN_FEATURE400Not a feature of the table above
INVALID_LIMIT400A negative or non-integer budget, or a monthlyUnitCap on all
FEATURE_NOT_DISABLABLE400database and worker cannot be switched off
LIMIT_ABOVE_PLAN_CEILING400A budget above your plan's ceiling
OWNER_ONLY403Only the account owner can change budgets
End of Set Feature LimitsNext endpointGETGet Rate Limit

#Get Rate Limit

GET/api/v1/vcaas/projects/:projectId/rate-limitapi-keyFree

How many requests per minute each visitor (IP) may send to the published site.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.perIpPerMinutenumber | nullRequests per minute per IP; null = off
data.allowlistCidrsstring[]IP ranges that are never limited (up to 50)
data.verifiedBotsExemptbooleanSearch engines and other verified bots are not limited
data.planDefaultnumberYour plan's default
data.planMaxnumber | nullThe highest value your plan allows
data.canDisablebooleanWhether your plan may switch it off
data.sourcestring"plan" or "user"

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/rate-limit
Success · 200 OK
json
{ "errors": null, "data": { "perIpPerMinute": 5000, "allowlistCidrs": [], "verifiedBotsExempt": true, "planDefault": 5000, "planMax": 60000, "canDisable": true, "source": "plan" } }

#Update Rate Limit

PATCH/api/v1/vcaas/projects/:projectId/rate-limitapi-keyFree

Change the per-visitor limit, the allowlist or the bot exemption. Send only what changes.

Separately, each visitor may cause at most 20 paid built-in calls a minute (AI, web, browser, screenshots), so nobody can spend the project's credits by calling a public AI page in a loop; over it the app's call answers 429 RATE_LIMIT_EXCEEDED with Retry-After. Ask the agent to change it ("let each visitor send 60 AI messages a minute").

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
perIpPerMinutenumber | nullNoFrom 10 to 60,000 on paid plans (5,000 on the free plan, the default everywhere); null = off (paid plans only)
allowlistCidrsstring[]NoUp to 50 IP ranges (203.0.113.0/24)
verifiedBotsExemptbooleanNoExempt verified bots

Response fields

FieldTypeDescription
dataobjectThe setting after the change, as in Get Rate Limit

Example request

curl -X PATCH \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"perIpPerMinute":300}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/rate-limit
Success · 200 OK
json
{ "errors": null, "data": { "perIpPerMinute": 300, "allowlistCidrs": [], "verifiedBotsExempt": true, "planDefault": 5000, "planMax": 60000, "canDisable": true, "source": "user" } }

Error codes

CodeHTTPMeaning
VALIDATION_ERROR400A value is out of range
RATE_LIMIT_ABOVE_PLAN400Above your plan's maximum (errorDetails.planMax)
INVALID_CIDR400An allowlist entry is not an IPv4 or IPv6 range
RATE_LIMIT_DISABLE_NOT_ALLOWED403The free plan cannot switch the limit off
End of Update Rate Limit Back to top