Usage & Analytics
LLM API provides a Public API that lets you programmatically access usage reports, organization members, and transaction history. These endpoints use a dedicated API key for authentication, separate from the API keys used for LLM requests.
Authentication
All Public API endpoints require an API key passed via the Authorization header or X-Api-Key header:
# Using Authorization header
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31" \
-H "Authorization: Bearer your_public_api_key"
# Using X-Api-Key header
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31" \
-H "X-Api-Key: your_public_api_key"
INFO
Public API keys are organization-scoped and have fine-grained permissions via scopes. Contact your organization admin to obtain a key with the appropriate scopes.
Scopes
Each Public API key is assigned one or more scopes that control access:
| Scope |
Description |
reports:read |
Read usage reports |
members:read |
Read organization members |
transactions:read |
Read organization transactions |
api_keys:read |
Read API keys |
organization:read |
Read organization information |
admin |
Full administrative access |
Models Usage Report
Returns a detailed usage report for your organization, with flexible grouping and filtering options. Requires the reports:read scope.
Endpoint
GET /api/v1/organizations/reports/models/usage
Query Parameters
| Parameter |
Type |
Required |
Description |
start_date |
string |
Yes |
Start date (format: YYYY-MM-DD) |
end_date |
string |
Yes |
End date (format: YYYY-MM-DD) |
api_key_id |
string |
No |
Filter usage by a specific API key ID |
provider |
string |
No |
Filter by model provider |
page |
integer |
No |
Page number (default: 1) |
page_size |
integer |
No |
Items per page, 1-500 (default: 20) |
group_by |
string |
No |
Group breakdown by: model, provider, api_key, day, metadata, or none (default: none) |
metadata_key |
string |
When group_by=metadata |
Predefined metadata header to bucket by (e.g. x-llmapi-team). See Metadata. |
metadata[…] |
string |
No |
Narrow the report to requests matching the given metadata header, e.g. metadata[x-llmapi-team]=backend. Repeatable; combined with AND. Keys must be one of the predefined Metadata headers — unknown keys return HTTP 400. |
Response
{
"items": [
{
"model": "gpt-4o",
"provider": "openai",
"api_key_id": "key_abc123",
"date": "2025-03-15",
"requests": 1250,
"prompt_tokens": 524000,
"completion_tokens": 128000,
"total_tokens": 652000,
"cost": "3.45"
},
{
"model": "claude-sonnet-4-20250514",
"provider": "anthropic",
"api_key_id": "key_abc123",
"date": "2025-03-15",
"requests": 450,
"prompt_tokens": 174000,
"completion_tokens": 38000,
"total_tokens": 212000,
"cost": "1.35"
}
],
"summary": {
"total_requests": 1700,
"total_prompt_tokens": 698000,
"total_completion_tokens": 166000,
"total_tokens": 864000,
"total_cost": "4.80",
"top_model": "gpt-4o",
"providers_used": ["openai", "anthropic"]
},
"period": {
"start_date": "2025-03-15",
"end_date": "2025-03-15"
},
"pagination": {
"page": 1,
"page_size": 20,
"total": 2,
"total_pages": 1
}
}
Response Fields
Items — each item in the items array represents a usage record:
| Field |
Type |
Description |
model |
string |
Model identifier |
provider |
string |
Provider name (e.g., openai, anthropic) |
api_key_id |
string |
API key that generated the usage |
date |
string |
Date of usage (present when group_by is day) |
metadata_value |
string |
Distinct value of the metadata key (present when group_by is metadata) |
requests |
integer |
Number of API requests |
prompt_tokens |
integer |
Total input/prompt tokens |
completion_tokens |
integer |
Total output/completion tokens |
total_tokens |
integer |
Sum of prompt and completion tokens |
cost |
string |
Total cost in USD |
Summary — aggregated totals across all items:
| Field |
Type |
Description |
total_requests |
integer |
Total number of requests |
total_prompt_tokens |
integer |
Total input tokens |
total_completion_tokens |
integer |
Total output tokens |
total_tokens |
integer |
Total tokens |
total_cost |
string |
Total cost in USD |
top_model |
string |
Most used model |
providers_used |
string[] |
List of providers used in the period |
Examples
cURL
# Daily usage breakdown for March 2025
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31&group_by=day" \
-H "Authorization: Bearer your_public_api_key"
# Usage grouped by model for a specific API key
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31&group_by=model&api_key_id=key_abc123" \
-H "Authorization: Bearer your_public_api_key"
# Cost breakdown per team (requests must carry X-LLMAPI-Team)
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31&group_by=metadata&metadata_key=x-llmapi-team" \
-H "Authorization: Bearer your_public_api_key"
# Per-model cost restricted to a single environment + feature (AND filter)
curl -G "https://api.llmapi.ai/api/v1/organizations/reports/models/usage" \
--data-urlencode "start_date=2025-03-01" \
--data-urlencode "end_date=2025-03-31" \
--data-urlencode "group_by=model" \
--data-urlencode "metadata[x-llmapi-environment]=prod" \
--data-urlencode "metadata[x-llmapi-feature]=chat-assistant" \
-H "Authorization: Bearer your_public_api_key"
TypeScript
const response = await fetch(
"https://api.llmapi.ai/api/v1/organizations/reports/models/usage?" +
new URLSearchParams({
start_date: "2025-03-01",
end_date: "2025-03-31",
group_by: "day",
}),
{
headers: { Authorization: "Bearer your_public_api_key" },
}
);
const report = await response.json();
console.log(`Total cost: ${report.summary.total_cost}`);
console.log(`Total requests: ${report.summary.total_requests}`);
for (const item of report.items) {
console.log(`${item.date}: ${item.model} — ${item.requests} requests, ${item.cost}`);
}
Python
import requests
response = requests.get(
"https://api.llmapi.ai/api/v1/organizations/reports/models/usage",
params={
"start_date": "2025-03-01",
"end_date": "2025-03-31",
"group_by": "day",
},
headers={"Authorization": "Bearer your_public_api_key"},
)
report = response.json()
print(f"Total cost: ${report['summary']['total_cost']}")
print(f"Total requests: {report['summary']['total_requests']}")
for item in report["items"]:
print(f"{item['date']}: {item['model']} — {item['requests']} requests, ${item['cost']}")
Grouping Examples
Use the group_by parameter to control how usage data is aggregated:
group_by=model — one row per model, useful for comparing model costs
group_by=provider — one row per provider (OpenAI, Anthropic, etc.)
group_by=api_key — one row per API key, useful for per-team or per-project tracking
group_by=day — one row per day, useful for trend analysis
group_by=metadata — one row per distinct value of a metadata header (requires metadata_key), useful for cost attribution by team, environment, feature, or any custom dimension
group_by=none — a single aggregated row (default)
Grouping by Metadata
When group_by=metadata, the required metadata_key parameter selects which header to bucket by. Only the nine predefined X-LLMAPI-* headers are accepted — see Metadata for the full list.
# Cost per team for March 2025
curl "https://api.llmapi.ai/api/v1/organizations/reports/models/usage?start_date=2025-03-01&end_date=2025-03-31&group_by=metadata&metadata_key=x-llmapi-team" \
-H "Authorization: Bearer your_public_api_key"
Sample response (abbreviated):
{
"items": [
{
"model": "",
"provider": "",
"api_key_id": "",
"metadata_value": "backend",
"requests": 1542,
"prompt_tokens": 892301,
"completion_tokens": 234102,
"total_tokens": 1126403,
"cost": "12.45"
},
{
"model": "",
"provider": "",
"api_key_id": "",
"metadata_value": "ml-ops",
"requests": 873,
"prompt_tokens": 401200,
"completion_tokens": 98400,
"total_tokens": 499600,
"cost": "5.67"
}
]
}
Rows whose requests didn't carry the chosen metadata header are excluded from the result.
Organization Members
Returns a paginated list of members in your organization. Requires the members:read scope.
Endpoint
GET /api/v1/organizations/members
Query Parameters
| Parameter |
Type |
Required |
Description |
after_id |
string |
No |
Cursor for pagination (last seen member ID) |
email |
string |
No |
Filter by email |
limit |
integer |
No |
Number of results, 1-500 (default: 20) |
Response
{
"members": [
{
"id": "usr_abc123",
"email": "alice@example.com",
"name": "Alice Johnson",
"role": "owner",
"status": "active",
"last_login_at": "2025-03-15T10:30:00Z",
"joined_at": "2024-06-01T08:00:00Z"
},
{
"id": "usr_def456",
"email": "bob@example.com",
"name": "Bob Smith",
"role": "member",
"status": "active",
"joined_at": "2024-09-15T12:00:00Z"
}
],
"has_more": false,
"last_id": "usr_def456"
}
Response Fields
| Field |
Type |
Description |
id |
string |
Member ID |
email |
string |
Member email address |
name |
string |
Member display name |
role |
string |
Organization role |
status |
string |
Member status |
last_login_at |
string |
Last login timestamp (may be null) |
joined_at |
string |
Date the member joined |
Pagination
Members use cursor-based pagination. To fetch the next page, pass last_id from the response as the after_id query parameter:
# First page
GET /api/v1/organizations/members?limit=50
# Next page
GET /api/v1/organizations/members?limit=50&after_id=usr_def456
Example
cURL
curl "https://api.llmapi.ai/api/v1/organizations/members?limit=50" \
-H "Authorization: Bearer your_public_api_key"
TypeScript
async function fetchAllMembers() {
const members = [];
let afterId: string | undefined;
do {
const params = new URLSearchParams({ limit: "100" });
if (afterId) params.set("after_id", afterId);
const res = await fetch(
`https://api.llmapi.ai/api/v1/organizations/members?${params}`,
{ headers: { Authorization: "Bearer your_public_api_key" } }
);
const data = await res.json();
members.push(...data.members);
afterId = data.has_more ? data.last_id : undefined;
} while (afterId);
return members;
}
Python
import requests
def fetch_all_members() -> list:
members = []
after_id = None
while True:
params = {"limit": 100}
if after_id:
params["after_id"] = after_id
response = requests.get(
"https://api.llmapi.ai/api/v1/organizations/members",
params=params,
headers={"Authorization": "Bearer your_public_api_key"},
)
data = response.json()
members.extend(data["members"])
if data["has_more"]:
after_id = data["last_id"]
else:
break
return members
Organization Transactions
Returns a paginated list of billing transactions for your organization. Requires the transactions:read scope.
Endpoint
GET /api/v1/organizations/transactions
Query Parameters
| Parameter |
Type |
Required |
Description |
after_id |
string |
No |
Cursor for pagination (last seen transaction ID) |
status |
string |
No |
Filter by transaction status |
type |
string |
No |
Filter by transaction type |
currency |
string |
No |
Filter by currency code (ISO 4217) |
start_date |
string |
No |
Return transactions on or after this date (YYYY-MM-DD) |
end_date |
string |
No |
Return transactions on or before this date (YYYY-MM-DD) |
limit |
integer |
No |
Items per page, 1-200 (default: 20) |
Response
{
"data": [
{
"id": "txn_abc123",
"organization_id": "org_xyz789",
"type": "credit_purchase",
"amount": "50.00",
"credit_amount": "50.00",
"currency": "USD",
"status": "completed",
"stripe_payment_intent_id": "pi_abc123",
"description": "Credit top-up",
"created_at": "2025-03-15T10:30:00Z",
"updated_at": "2025-03-15T10:30:05Z"
}
],
"has_more": false,
"last_id": "txn_abc123"
}
Response Fields
| Field |
Type |
Description |
id |
string |
Transaction ID |
organization_id |
string |
Organization ID |
type |
string |
Transaction type |
amount |
string |
Transaction amount |
credit_amount |
string |
Credit amount applied |
currency |
string |
Currency code (ISO 4217) |
status |
string |
Transaction status |
stripe_payment_intent_id |
string |
Stripe payment intent ID (if applicable) |
stripe_invoice_id |
string |
Stripe invoice ID (if applicable) |
description |
string |
Transaction description |
related_transaction_id |
string |
Related transaction (e.g., for refunds) |
refund_reason |
string |
Reason for refund (if applicable) |
created_at |
string |
Creation timestamp |
updated_at |
string |
Last update timestamp |
Pagination
Transactions use cursor-based pagination. To fetch the next page, pass last_id from the response as the after_id query parameter:
# First page
GET /api/v1/organizations/transactions?limit=50
# Next page
GET /api/v1/organizations/transactions?limit=50&after_id=txn_abc123
Example
cURL
# Get all transactions for March 2025
curl "https://api.llmapi.ai/api/v1/organizations/transactions?start_date=2025-03-01&end_date=2025-03-31" \
-H "Authorization: Bearer your_public_api_key"
TypeScript
const response = await fetch(
"https://api.llmapi.ai/api/v1/organizations/transactions?" +
new URLSearchParams({
start_date: "2025-03-01",
end_date: "2025-03-31",
limit: "100",
}),
{
headers: { Authorization: "Bearer your_public_api_key" },
}
);
const { data: transactions, has_more, last_id } = await response.json();
for (const txn of transactions) {
console.log(`${txn.created_at}: ${txn.type} — ${txn.amount} ${txn.currency}`);
}
Python
import requests
response = requests.get(
"https://api.llmapi.ai/api/v1/organizations/transactions",
params={
"start_date": "2025-03-01",
"end_date": "2025-03-31",
"limit": 100,
},
headers={"Authorization": "Bearer your_public_api_key"},
)
data = response.json()
for txn in data["data"]:
print(f"{txn['created_at']}: {txn['type']} — ${txn['amount']} {txn['currency']}")
Error Responses
All Public API endpoints return errors in the following format:
{
"error": true,
"message": "Parameter 'start_date' is required",
"status": 400
}
| Status Code |
Description |
400 |
Bad Request — missing or invalid parameters |
401 |
Unauthorized — missing or invalid API key |
403 |
Forbidden — API key missing required scope for this endpoint |