Capabilities
Budgets
Budgets hold the money your organization and its projects spend on AI usage, and the per-user spend limits that apply inside them. These endpoints let you read the budgets you administer, see what each member has spent against their limit, and set per-user limits in bulk.
Every organization has one organization budget. When project budgets are turned on, each project can also have a project budget that draws from the organization budget. Funding, renewal, overage behavior, and whether member limits are on are changed in the admin UI; the API reads them and sets per-member limits.
All paths below are relative to your instance's base URL
(https://<your-domain>.nomic.ai/api/v0). See the
overview for authentication details.
Scopes
| Scope | Grants |
|---|---|
developer:budgets:read | Read the budgets you administer and their per-user limits. |
developer:budgets:write | Set per-user limits on the budgets you administer. |
The broad developer scope implies developer:budgets:write, which in turn
implies developer:budgets:read.
Scopes only let a key call these endpoints; access to each budget still follows
your role. Organization admins administer the organization budget and every
project budget. A project admin administers that project's budget. A budget you
do not administer is reported as 404.
Wherever a path takes a budget {id}, you can pass organization in place of a
UUID to address the organization budget.
List budgets
GET /budgets
List the budgets the caller administers: the organization budget first, then
project budgets by name. All budgets fit in one page, so nextCursor is always
null.
Scope: developer:budgets:read · Rate limit: Standard (300 req / min)
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
scope | organization | project | — | Only return budgets of this type. |
projectId | uuid | — | Only return this project's budget. |
Response
{
"data": [
{
"id": "019abc12-3456-7890-abcd-ef1234567890",
"name": "Acme",
"parentId": null,
"scope": { "type": "organization", "projectBudgetsEnabled": true },
"funding": {
"amountCents": 5000000,
"spentCents": 1234500,
"remainingCents": 3765500,
"renewal": null
},
"onExhausted": "block",
"memberLimits": {
"enabled": true,
"defaultCents": 20000,
"period": "monthly",
"resetsAt": "2026-11-01T00:00:00.000Z"
}
},
{
"id": "019abc12-3456-7890-abcd-ef1234567891",
"name": "Downtown Office Tower",
"parentId": "019abc12-3456-7890-abcd-ef1234567890",
"scope": {
"type": "project",
"projectId": "019c587e-3456-7890-abcd-ef1234567890"
},
"funding": {
"amountCents": 100000,
"spentCents": 42000,
"remainingCents": 58000,
"renewal": { "cadence": "monthly", "amountCents": 100000 }
},
"onExhausted": "use_parent",
"memberLimits": {
"enabled": false,
"defaultCents": null,
"period": null,
"resetsAt": null
}
}
],
"nextCursor": null,
"totalCount": 2
}
| Field | Type | Description |
|---|---|---|
data[].id | uuid | Budget ID. |
data[].name | string | Project name for project budgets, else the budget name. |
data[].parentId | uuid? | Budget this one draws from, or null for the organization budget. |
data[].scope | object | { "type": "organization", "projectBudgetsEnabled": boolean } or { "type": "project", "projectId": uuid }. |
data[].funding.amountCents | number | Total granted to this budget, in US cents. The organization budget is funded by its contract. |
data[].funding.spentCents | number | Spent from this budget, in US cents. |
data[].funding.remainingCents | number | Left to spend, in US cents. Negative when overspent. |
data[].funding.renewal | object? | { "cadence": "weekly" | "monthly", "amountCents": number } — a repeating grant that refills the budget — or null. |
data[].onExhausted | block | use_parent | When the budget runs out, block stops spend and use_parent keeps spending from the parent budget. |
data[].memberLimits.enabled | boolean | Whether per-user limits apply on this budget. |
data[].memberLimits.defaultCents | number? | Limit for members without their own, in US cents. null means no default limit. |
data[].memberLimits.period | daily | weekly | monthly | null | Window each member limit resets on (UTC; weeks start Monday). null means the limit covers the whole contract. |
data[].memberLimits.resetsAt | string? | ISO-8601 end of the current window, or null when period is null. |
nextCursor | null | Always null. |
totalCount | number | Number of budgets returned. |
Errors
| Status | Cause |
|---|---|
400 | Invalid query parameters. |
401 | Missing or invalid API key. |
403 | API key lacks developer:budgets:read scope. |
429 | Standard rate limit exceeded. |
Example
curl "https://<your-domain>.nomic.ai/api/v0/budgets?scope=project" \
-H "Authorization: Bearer $NOMIC_API_KEY"
Get a budget
GET /budgets/{id}
Fetch a single budget. Pass organization as the ID for the organization
budget, which holds the organization-wide per-user limits.
Scope: developer:budgets:read · Rate limit: Standard (300 req / min)
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string (uuid or literal) | Budget ID, or organization for the organization budget. |
Response
Returns 200 OK with the budget under data, in the same shape as an item of
List budgets.
Errors
| Status | Cause |
|---|---|
401 | Missing or invalid API key. |
403 | API key lacks developer:budgets:read scope. |
404 | No budget with this ID that the caller administers. |
429 | Standard rate limit exceeded. |
Example
curl "https://<your-domain>.nomic.ai/api/v0/budgets/organization" \
-H "Authorization: Bearer $NOMIC_API_KEY"
List budget members
GET /budgets/{id}/members
List each member's own limit, the limit that applies to them, and their spend in the budget's current window, ordered by email. The organization budget covers every user in the organization; a project budget covers the project's members.
Scope: developer:budgets:read · Rate limit: Standard (300 req / min)
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string (uuid or literal) | Budget ID, or organization for the organization budget. |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Max results per page (1–100). |
cursor | string | — | Opaque cursor from a previous page's nextCursor. |
Response
{
"data": [
{
"id": "019abc12-3456-7890-abcd-ef1234567890",
"email": "alice@example.com",
"name": "Alice Johnson",
"limitCents": 50000,
"effectiveLimitCents": 50000,
"spentCents": 12340,
"status": "within_limit"
},
{
"id": "019abc12-3456-7890-abcd-ef1234567891",
"email": "bob@example.com",
"name": "Bob Lee",
"limitCents": null,
"effectiveLimitCents": 20000,
"spentCents": 20000,
"status": "limit_reached"
}
],
"nextCursor": null,
"totalCount": 2
}
| Field | Type | Description |
|---|---|---|
data[].id | uuid | User ID of the member. |
data[].email | string | Member email. |
data[].name | string? | Member display name, or null if unset. |
data[].limitCents | number? | The member's own limit in US cents, or null when they follow the budget's memberLimits.defaultCents. |
data[].effectiveLimitCents | number? | The limit that applies to the member, in US cents. null when member limits are off or neither an own limit nor a default is set. |
data[].spentCents | number | Spent in the budget's current limit window (see memberLimits.period), in US cents. |
data[].status | unlimited | within_limit | limit_reached | unlimited when no limit applies, limit_reached once spend in the current window has reached the limit, else within_limit. |
nextCursor | string? | Cursor for the next page, or null if none remain. |
totalCount | number | Total members of the budget. |
Errors
| Status | Cause |
|---|---|
401 | Missing or invalid API key. |
403 | API key lacks developer:budgets:read scope. |
404 | No budget with this ID that the caller administers. |
409 | A project budget while project budgets are turned off (project_budgets_disabled). |
429 | Standard rate limit exceeded. |
Example
curl "https://<your-domain>.nomic.ai/api/v0/budgets/organization/members?limit=100" \
-H "Authorization: Bearer $NOMIC_API_KEY"
Set budget member limits
POST /budgets/{id}/members
Set or clear per-user limits in one batch of 1–100 changes, applied atomically:
nothing is written unless every entry is valid. A limitCents of null clears
the member's own limit so the budget's default applies. Sending the same batch
twice changes nothing.
Member limits must be turned on for the budget, and every user must already be one of the budget's members; identify each by user ID or email. Changing limits on the organization budget requires the key owner to be an organization admin.
Scope: developer:budgets:write · Rate limit: Write (60 req / min)
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string (uuid or literal) | Budget ID, or organization for the organization budget. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
members | array | Yes | 1–100 limit changes to apply as one batch. |
members[].user | string (uuid or email) | Yes | The member, by user ID or email address. |
members[].limitCents | integer (≥ 0) | null | Yes | The member's own limit in US cents, or null to clear it so the default applies. |
Response
Returns 200 OK with the changed members after the batch, ordered by email, in
the same shape as List budget members (without
pagination fields).
Errors
| Status | Cause |
|---|---|
400 | Validation error, or an unknown, repeated, or non-member user. |
401 | Missing or invalid API key. |
403 | API key lacks developer:budgets:write scope, or the organization budget and the key owner is not an organization admin. |
404 | No budget with this ID that the caller administers. |
409 | Member limits are off for this budget (member_limits_disabled), or project budgets are turned off (project_budgets_disabled). |
429 | Write rate limit exceeded. |
Example
Give Bob a $250 limit and put Carol back on the budget default:
curl -X POST "https://<your-domain>.nomic.ai/api/v0/budgets/organization/members" \
-H "Authorization: Bearer $NOMIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"members": [
{ "user": "bob@example.com", "limitCents": 25000 },
{ "user": "carol@example.com", "limitCents": null }
]
}'