Skip to main content

Cost Controls API

The Cost Controls API provides budget management and LLM usage tracking capabilities, enabling organizations to set spending limits, receive alerts, and enforce budget policies on LLM usage.

SDK Support

All Cost Controls endpoints are available through the official SDKs:

Base URL: http://localhost:8081 (Orchestrator)

Authentication

All endpoints require:

  • Authorization: Basic base64(clientId:clientSecret) header
  • Content-Type: application/json header (for POST/PUT requests)

Optional:

  • X-Org-ID header -- Scopes budgets and usage to a specific tenant

Overview

Cost controls in AxonFlow are governance policies. Budget limits are enforced at multiple scopes:

ScopeDescription
organizationOrganization-wide budget
teamBudget for a specific team
agentBudget for an individual agent
workflowBudget for a workflow
userBudget for a specific user

Budget Management

Create Budget

Create a new budget with spending limits.

POST /api/v1/budgets

Headers:

Content-Type: application/json
X-Org-ID: your-org-id
X-Client-Secret: your-secret

Request Body:

{
"id": "monthly-budget",
"name": "Monthly Production Budget",
"scope": "organization",
"limit_usd": 1000.00,
"period": "monthly",
"on_exceed": "warn",
"alert_thresholds": [50, 80, 100]
}
FieldTypeRequiredDefaultDescription
idstringYes-Unique budget identifier
namestringYes-Human-readable budget name
scopestringYes-Budget scope: organization, team, agent, workflow, user
scope_idstringNonullID of the scoped entity (required for team/agent/workflow/user scopes)
limit_usdnumberYes-Budget limit in USD
periodstringYes-Budget period: daily, weekly, monthly, quarterly, yearly
on_exceedstringYes-Action when exceeded: warn, block, downgrade
alert_thresholdsnumber[]No[]Percentage thresholds for alerts (e.g., [50, 80, 100])
enabledbooleanNotrueWhether budget enforcement is active

Response (201 Created):

{
"id": "monthly-budget",
"name": "Monthly Production Budget",
"scope": "organization",
"limit_usd": 1000.00,
"period": "monthly",
"on_exceed": "warn",
"alert_thresholds": [50, 80, 100],
"enabled": true,
"created_at": "2026-01-04T00:00:00Z",
"updated_at": "2026-01-04T00:00:00Z"
}

List Budgets

List all budgets for the organization.

GET /api/v1/budgets

Query Parameters:

ParameterTypeDescription
scopestringFilter by scope
limitintegerMaximum results (default: 50)
offsetintegerPagination offset (default: 0)

Response:

{
"budgets": [
{
"id": "monthly-budget",
"name": "Monthly Production Budget",
"scope": "organization",
"limit_usd": 1000.00,
"period": "monthly",
"on_exceed": "warn",
"alert_thresholds": [50, 80, 100],
"enabled": true
}
],
"total": 1
}

Get Budget

Get a specific budget by ID.

GET /api/v1/budgets/{id}

Update Budget

Update an existing budget configuration.

PUT /api/v1/budgets/{id}

Request Body:

{
"name": "Updated Budget Name",
"limit_usd": 2000.00,
"on_exceed": "block",
"alert_thresholds": [25, 50, 75, 100]
}

All fields are optional. Only provided fields are updated.

Delete Budget

Delete a budget by ID.

DELETE /api/v1/budgets/{id}

Response: 204 No Content

Budget Status & Alerts

Get Budget Status

Get real-time status of a budget including current usage.

GET /api/v1/budgets/{id}/status

Response:

{
"budget": {
"id": "monthly-budget",
"name": "Monthly Production Budget",
"limit_usd": 1000.00,
"period": "monthly"
},
"used_usd": 450.25,
"remaining_usd": 549.75,
"percentage": 45.025,
"period_start": "2026-01-01T00:00:00Z",
"period_end": "2026-02-01T00:00:00Z",
"is_exceeded": false,
"is_blocked": false
}

Get Budget Alerts

Get alerts triggered for a specific budget.

GET /api/v1/budgets/{id}/alerts

Query Parameters:

ParameterTypeDescription
limitintegerMaximum alerts to return (default: 50)

Response:

{
"alerts": [
{
"id": "alert-123",
"budget_id": "monthly-budget",
"alert_type": "threshold",
"threshold": 50,
"percentage_reached": 52.3,
"amount_usd": 523.00,
"message": "Budget 'Monthly Production Budget' has reached 50% threshold",
"created_at": "2026-01-15T10:30:00Z"
}
],
"count": 1
}

Check Budget (Pre-flight)

Check if a request should be allowed based on budget constraints. Use this before making LLM requests.

POST /api/v1/budgets/check

Request Body:

{
"org_id": "your-org-id",
"team_id": "engineering",
"agent_id": "support-bot"
}

Response (Allowed):

{
"allowed": true
}

Response (Blocked):

{
"allowed": false,
"action": "block",
"budget_id": "team-budget",
"budget_name": "Engineering Team Budget",
"used_usd": 520.00,
"limit_usd": 500.00,
"percentage": 104.0,
"message": "Budget 'Engineering Team Budget' exceeded - requests blocked"
}

Usage Tracking

Get Usage Summary

Get aggregated LLM usage for the current period.

GET /api/v1/usage

Query Parameters:

ParameterTypeDescription
periodstringPeriod: daily, weekly, monthly, quarterly, yearly

Response:

{
"period": "monthly",
"period_start": "2026-01-01T00:00:00Z",
"period_end": "2026-02-01T00:00:00Z",
"total_cost_usd": 1523.45,
"total_requests": 45230,
"total_tokens_in": 12500000,
"total_tokens_out": 8750000,
"by_provider": {
"openai": {
"cost_usd": 890.00,
"requests": 25000,
"tokens_in": 7500000,
"tokens_out": 5000000
},
"anthropic": {
"cost_usd": 633.45,
"requests": 20230,
"tokens_in": 5000000,
"tokens_out": 3750000
}
}
}

Get Usage Breakdown

Get usage broken down by a specific dimension.

GET /api/v1/usage/breakdown

Query Parameters:

ParameterTypeDescription
group_bystringDimension: provider, model, agent, team, workflow
periodstringPeriod: daily, weekly, monthly, quarterly, yearly

Response:

{
"group_by": "model",
"period": "monthly",
"items": [
{
"name": "gpt-4o",
"cost_usd": 650.00,
"requests": 8500,
"percentage": 42.7
},
{
"name": "claude-sonnet-4",
"cost_usd": 480.00,
"requests": 12000,
"percentage": 31.5
},
{
"name": "gpt-4o-mini",
"cost_usd": 120.00,
"requests": 18000,
"percentage": 7.9
}
],
"total_cost_usd": 1523.45
}

List Usage Records

Get detailed usage records with filtering.

GET /api/v1/usage/records

Query Parameters:

ParameterTypeDescription
limitintegerMaximum records (default: 50)
offsetintegerPagination offset
providerstringFilter by provider
modelstringFilter by model
start_timestringFilter by start time (ISO 8601)
end_timestringFilter by end time (ISO 8601)

Response:

{
"records": [
{
"id": "usage-123",
"timestamp": "2026-01-04T12:30:00Z",
"provider": "openai",
"model": "gpt-4o",
"tokens_in": 1500,
"tokens_out": 800,
"cost_usd": 0.045,
"agent_id": "support-bot",
"team_id": "engineering",
"request_id": "req-abc123"
}
],
"total": 1
}

Pricing Information

Get Pricing

Get model pricing information.

GET /api/v1/pricing

Query Parameters:

ParameterTypeDescription
providerstringFilter by provider
modelstringFilter by model

Response:

{
"pricing": [
{
"provider": "openai",
"model": "gpt-4o",
"input_price_per_1k": 0.01,
"output_price_per_1k": 0.03,
"currency": "USD",
"updated_at": "2026-01-01T00:00:00Z"
},
{
"provider": "anthropic",
"model": "claude-sonnet-4",
"input_price_per_1k": 0.003,
"output_price_per_1k": 0.015,
"currency": "USD",
"updated_at": "2026-01-01T00:00:00Z"
}
]
}

Cost Estimation

Added in v4.3.0.

Estimate the cost of a workflow plan before execution.

Estimate Plan Cost

Estimate the cost of a plan definition without creating or executing it.

POST /api/v1/plans/estimate

Headers:

Content-Type: application/json
X-Org-ID: your-org-id
X-Client-Secret: your-secret

Request Body:

{
"provider": "openai",
"model": "gpt-4",
"steps": [
{
"name": "analyze",
"type": "llm_call",
"estimated_tokens_in": 1000,
"estimated_tokens_out": 500
}
]
}
FieldTypeRequiredDescription
providerstringYesLLM provider (e.g., openai, anthropic)
modelstringYesModel identifier (e.g., gpt-4, claude-sonnet-4)
stepsarrayYesList of plan steps to estimate
steps[].namestringYesStep name
steps[].typestringYesStep type: llm_call, tool_call, connector_call
steps[].estimated_tokens_inintegerYesEstimated input tokens for this step
steps[].estimated_tokens_outintegerYesEstimated output tokens for this step

Response (200 OK):

{
"estimated_cost_usd": 0.045,
"currency": "USD",
"breakdown": [
{
"step": "analyze",
"type": "llm_call",
"tokens_in": 1000,
"tokens_out": 500,
"cost_usd": 0.045
}
]
}
FieldTypeDescription
estimated_cost_usdnumberTotal estimated cost in USD
currencystringCurrency (always USD)
breakdownarrayPer-step cost breakdown (Evaluation and Enterprise only; Community receives an empty array)
breakdown[].stepstringStep name
breakdown[].typestringStep type
breakdown[].tokens_inintegerEstimated input tokens
breakdown[].tokens_outintegerEstimated output tokens
breakdown[].cost_usdnumberEstimated cost for this step

Tiered Access:

EditionResponse DetailRate Limit
CommunityAggregate total only (breakdown is empty)10 estimates/day
EvaluationFull per-step breakdown100 estimates/day
EnterpriseFull per-step breakdownUnlimited

Error Responses:

HTTP StatusError CodeDescription
400INVALID_ESTIMATE_REQUESTMissing required fields or invalid step configuration
404PRICING_NOT_FOUNDNo pricing data available for the specified provider/model
429RATE_LIMIT_EXCEEDEDDaily estimate limit exceeded for your edition

Get Plan Cost Estimate

Get a cost estimate for an existing plan by ID.

GET /api/v1/plans/{id}/cost

Headers:

X-Org-ID: your-org-id
X-Client-Secret: your-secret

Path Parameters:

ParameterTypeDescription
idstringPlan ID

Response (200 OK):

{
"plan_id": "plan_abc123",
"estimated_cost_usd": 0.045,
"currency": "USD",
"breakdown": [
{
"step": "analyze",
"type": "llm_call",
"tokens_in": 1000,
"tokens_out": 500,
"cost_usd": 0.045
}
]
}
FieldTypeDescription
plan_idstringThe plan ID
estimated_cost_usdnumberTotal estimated cost in USD
currencystringCurrency (always USD)
breakdownarrayPer-step cost breakdown (Evaluation and Enterprise only; Community receives an empty array)

The breakdown array fields are identical to those in the Estimate Plan Cost response.

Error Responses:

HTTP StatusError CodeDescription
404PLAN_NOT_FOUNDNo plan found with the specified ID
429RATE_LIMIT_EXCEEDEDDaily estimate limit exceeded for your edition

SDK Examples

Python

from axonflow import AxonFlow, CreateBudgetRequest, BudgetScope, BudgetPeriod, BudgetOnExceed

async with AxonFlow(endpoint="http://localhost:8080") as client:
# Create a budget
budget = await client.create_budget(CreateBudgetRequest(
id="team-budget",
name="Engineering Budget",
scope=BudgetScope.TEAM,
scope_id="engineering",
limit_usd=5000.0,
period=BudgetPeriod.MONTHLY,
on_exceed=BudgetOnExceed.WARN,
alert_thresholds=[50, 80, 100]
))

# Check budget before request
decision = await client.check_budget(BudgetCheckRequest(
team_id="engineering"
))

if decision.allowed:
# Proceed with LLM request
pass
else:
print(f"Request blocked: {decision.message}")

# Get usage summary
usage = await client.get_usage_summary(period="monthly")
print(f"Total cost: ${usage.total_cost_usd:.2f}")

TypeScript

import { AxonFlow, BudgetScope, BudgetPeriod, BudgetOnExceed } from '@axonflow/sdk';

const client = new AxonFlow({
endpoint: 'http://localhost:8080'
});

// Create a budget
const budget = await client.createBudget({
id: 'team-budget',
name: 'Engineering Budget',
scope: BudgetScope.TEAM,
scopeId: 'engineering',
limitUsd: 5000,
period: BudgetPeriod.MONTHLY,
onExceed: BudgetOnExceed.WARN,
alertThresholds: [50, 80, 100]
});

// Check budget before request
const decision = await client.checkBudget({
teamId: 'engineering'
});

if (decision.allowed) {
// Proceed with LLM request
} else {
console.log(`Request blocked: ${decision.message}`);
}

// Get usage breakdown
const breakdown = await client.getUsageBreakdown('model', 'monthly');
breakdown.items.forEach(item => {
console.log(`${item.name}: $${item.costUsd.toFixed(2)} (${item.percentage}%)`);
});

Go

package main

import (
"context"
"fmt"
axonflow "github.com/getaxonflow/axonflow-sdk-go/v3"
)

func main() {
client, _ := axonflow.NewClient(axonflow.AxonFlowConfig{
Endpoint: "http://localhost:8080",
})
defer client.Close()

ctx := context.Background()

// Create a budget
budget, _ := client.CreateBudget(ctx, axonflow.CreateBudgetRequest{
ID: "team-budget",
Name: "Engineering Budget",
Scope: "team",
ScopeID: "engineering",
LimitUSD: 5000.0,
Period: "monthly",
OnExceed: "warn",
AlertThresholds: []int{50, 80, 100},
})
fmt.Printf("Created budget: %s\n", budget.ID)

// Check budget before request
decision, _ := client.CheckBudget(ctx, axonflow.CheckBudgetRequest{
TeamID: "engineering",
})

if decision.Allowed {
// Proceed with LLM request
} else {
fmt.Printf("Request blocked: %s\n", decision.Message)
}

// Get usage summary
usage, _ := client.GetUsageSummary(ctx, "monthly")
fmt.Printf("Total cost: $%.2f\n", usage.TotalCostUSD)
}

Java

import com.getaxonflow.sdk.AxonFlow;
import com.getaxonflow.sdk.AxonFlowConfig;
import com.getaxonflow.sdk.types.costcontrols.CostControlTypes.*;

public class CostControlsExample {
public static void main(String[] args) {
AxonFlowConfig config = AxonFlowConfig.builder()
.endpoint("http://localhost:8080")
.build();

try (AxonFlow client = AxonFlow.create(config)) {
// Create a budget
Budget budget = client.createBudget(CreateBudgetRequest.builder()
.id("team-budget")
.name("Engineering Budget")
.scope(BudgetScope.TEAM)
.scopeId("engineering")
.limitUsd(5000.0)
.period(BudgetPeriod.MONTHLY)
.onExceed(BudgetOnExceed.WARN)
.alertThresholds(List.of(50, 80, 100))
.build());

// Check budget before request
BudgetDecision decision = client.checkBudget(BudgetCheckRequest.builder()
.teamId("engineering")
.build());

if (decision.isAllowed()) {
// Proceed with LLM request
} else {
System.out.println("Request blocked: " + decision.getMessage());
}

// Get usage summary
UsageSummary usage = client.getUsageSummary("monthly");
System.out.printf("Total cost: $%.2f%n", usage.getTotalCostUsd());
}
}
}

Budget Period Values

ValueDescription
dailyResets every day at midnight UTC
weeklyResets every Monday at midnight UTC
monthlyResets on the 1st of each month
quarterlyResets every 3 months (Jan, Apr, Jul, Oct)
yearlyResets on January 1st

On Exceed Actions

ValueDescription
warnLog a warning and send alerts, but allow requests to continue
blockBlock all LLM requests until the next budget period
downgradeSwitch to a cheaper model automatically (Enterprise only)

Error Responses

HTTP StatusError CodeDescription
400INVALID_BUDGET_CONFIGInvalid budget configuration
404BUDGET_NOT_FOUNDBudget not found
409BUDGET_EXISTSBudget with this ID already exists
500INTERNAL_ERRORInternal server error

Example (400):

{
"error": {
"code": "INVALID_BUDGET_CONFIG",
"message": "limit_usd must be a positive number"
}
}

See Error Codes for the complete list.

Community vs Enterprise

FeatureCommunityEnterprise
Usage tracking
Budget limits
Budget alerts
Pre-flight budget check
Budget hierarchy
Usage forecast
Usage export
Alert channels (Slack, email, webhook)
Auto-downgrade
Budget rollover
Cost dashboard