Quotas

Create a quota

Creates a project-scoped quota. requests is valid for scope: project/api_key; tokens and cost_usd are valid for scope: project/agent/actor; storage_bytes is valid for scope: project only. Any other scope/metric pair is rejected with 400 (no attribution exists to enforce it). An actor quota caps one end user's spend, matched from the generation's session; a null scope_ref means one budget per actor rather than a pooled project total. A cost_usd quota may name one meter_type to cap; omitting it caps every priced meter. A duplicate quota (same project, scope, scope_ref, metric, window, meter_type) is rejected with 409.

storage_bytes caps a stored total rather than a windowed one, so it takes window: current and every other metric refuses that value (400 either way). It is enforced at the corpus write paths — file upload and create, document create, document ingest and re-ingest, memory create — with 409 QUOTA_STORAGE_EXCEEDED and no Retry-After, since no window reset clears a footprint.

post/v1/projects/{project_id}/quotas

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/quotas
  • Auth: one of:
    • HTTP bearer
    • OAuth 2 (authorization code, authorization URL https://api.naturali.ai/authorize, token URL https://api.naturali.ai/token, scopes: mcp:access)

Request body

scope'project' | 'api_key' | 'agent' | 'actor' required

The scope the quota applies to

scope_refstring nullable

Public id of the api key / agent / actor the quota applies to. For api_key and agent scope, NULL means all entities of that scope type in the project. For actor scope, NULL means one budget per actor — each end user gets their own allowance — rather than a pooled total across all actors.

metric'requests' | 'tokens' | 'cost_usd' | 'storage_bytes' required

The metric being capped

window'rolling_1m' | 'rolling_1h' | 'rolling_24h' | 'calendar_month' | 'current' required

The window over which the metric is aggregated. current is the only accepted value for storage_bytes (a stored total is not aggregated over time) and is refused for every other metric.

limitnumber required

The cap. Must be a positive integer for requests/tokens/storage_bytes (bytes); fractional values are allowed for cost_usd.

mode'enforce' | 'monitor'

enforce blocks with 429 (requests at the middleware, tokens/cost_usd at the pre-generation check); monitor observes without blocking — a breach fires the quotas.exceeded webhook and writes a quotas:MonitorBreach audit entry, but the request is let through.

on_unpriced'block' | 'allow'

Only for metric cost_usd (400 on any other metric). What an enforce quota does when the current window is a pricing blackout — several metered llm_tokens events, none of them priced, so the aggregate is 0 however much was actually spent. Platform meters such as compute_execution are read for the aggregate but never for this verdict. block (the default) refuses new generations with 409 QUOTA_UNENFORCEABLE until pricing is configured; allow accepts the unmeasurable spend explicitly. Either way a quota_unpriced exception is filed. monitor-mode quotas never block regardless. A partly priced window is not a blackout: no posture refuses it, it is enforced on its priced total, and it files the same exception.

meter_type'llm_tokens' | 'compute_execution' | 'api_request' | 'storage' | 'tool_execution'

Only for metric cost_usd (400 on any other metric). The meter this cap answers for. Omit it and the cap sums every priced meter, which is the existing behaviour; name one and only that meter's cost counts, so an AI spend cap is not consumed by platform meters the operator prices (and vice versa). Part of the quota's identity, so two meter scopes can share a scope/metric/window and neither conflicts with an unscoped cap. Immutable after creation — replace the quota to change it.

Example request

{
  "scope_ref": "key_V1StGXR8Z5jdHi6B",
  "metric": "requests",
  "limit": 600,
  "mode": "enforce",
  "on_unpriced": "block"
}

Response

Quota created successfully

idstring
project_idstring
scope'project' | 'api_key' | 'agent' | 'actor'
scope_refstring nullable

Public id of the api key / agent / actor the quota applies to. For api_key and agent scope, NULL means all entities of that scope type in the project. For actor scope, NULL means one budget per actor rather than a pooled total across all actors.

metric'requests' | 'tokens' | 'cost_usd' | 'storage_bytes'
window'rolling_1m' | 'rolling_1h' | 'rolling_24h' | 'calendar_month' | 'current'

The window the metric is aggregated over; current on storage_bytes, which caps a stored total and never resets.

limitnumber
mode'enforce' | 'monitor'
meter_type'llm_tokens' | 'compute_execution' | 'api_request' | 'storage' | 'tool_execution' | 'null' nullable

The meter a cost_usd cap answers for. Null is every priced meter, which is what a quota created without one carries.

on_unpriced'block' | 'allow' | 'null' nullable

Pricing posture of a cost_usd quota over an unpriced blackout — block refuses generations, allow lets them through (the quota_unpriced exception is filed either way, and for a partly priced window, which no posture refuses). Null for metrics with no pricing dependency.

created_atstring date-time
updated_atstring date-time

Example response

{
  "id": "quota_V1StGXR8Z5jdHi6B",
  "current_usage": {
    "window_key": "2026-07-07T12:31Z",
    "count": 42
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.