OpenRouter Analytics Schema Discovery
Discover what analytics data is available for querying. The meta endpoint returns live, always-current definitions of metrics, dimensions, filter operators, and granularities.
Prerequisites
- An OpenRouter management key. Management keys are separate from regular API keys — get one at https://openrouter.ai/settings/management-keys
- Pass it via or set the environment variable
Discovery Endpoint
GET https://openrouter.ai/api/v1/analytics/meta
Authorization: Bearer sk-or-v1-...
Or via the
skill scripts:
bash
cd <openrouter-analytics-skill-path>/scripts && npx tsx discover-schema.ts
Response Shape
json
{
"data": {
"metrics": [
{ "name": "request_count", "display_label": "Request Count", "is_rate": false, "display_format": "number" }
],
"dimensions": [
{ "name": "model", "display_label": "Model" }
],
"operators": [
{ "name": "eq", "value_type": "scalar" }
],
"granularities": [
{ "name": "day", "display_label": "Day" }
]
}
}
Understanding Metrics
Each metric has:
| Field | Meaning |
|---|
| Identifier to use in query requests |
| Human-readable label |
| Whether this is a ratio/rate (averaged, not summed) |
| How the value should be formatted: , , , , or |
Time Range Limits
Most volume and cost metrics support time ranges up to
365 days with daily granularity. Latency/throughput metrics and some dimensions (
,
,
,
,
,
,
) are limited to
31-day time ranges. If a query times out, try narrowing the time range or removing latency/throughput metrics and per-generation dimensions.
Metric Categories
Volume metrics (how much):
- — number of API requests (up to 365 days)
- , , — token counts (up to 365 days)
- — tokens used for extended thinking (up to 365 days)
- — tokens served from cache (up to 365 days)
- — number of BYOK requests (up to 365 days)
- — count of requests that triggered guardrails (31-day limit)
- — count of responses served from cache (31-day limit)
Cost metrics (how much money):
- — total cost in USD, including BYOK inference cost (up to 365 days). Computed as
sum(usage) + sum(byok_usage_inference)
so it reflects true spend for both credits and BYOK users.
- — BYOK (bring your own key) inference cost in USD (up to 365 days)
- — all charges billed to OpenRouter credits in USD, including BYOK platform fees (up to 365 days)
- — non-BYOK inference spend in USD; excludes requests made with user-provided keys (31-day limit)
- — BYOK platform fees in USD; the platform fee portion of charged on BYOK requests (31-day limit). includes both non-BYOK inference charges and these BYOK platform fees.
- — provider-side (upstream) cost in USD (up to 365 days)
- — cache cost component in USD (up to 365 days)
- — data logging cost adjustment in USD; typically negative when a data logging discount applies (up to 365 days)
- — web search cost in USD (up to 365 days)
- — provider-side web search cost in USD (up to 365 days)
- — file processing cost in USD (31-day limit)
- — provider-side file processing cost in USD (31-day limit)
- — web fetch cost in USD (31-day limit)
- — provider-side web fetch cost in USD (31-day limit)
Performance metrics (how fast):
- , , , — response latency in milliseconds
- , , , — tokens per second
Efficiency metrics (how well):
- — ratio of cached tokens to prompt tokens (0–1)
- — ratio of requests that triggered guardrails
- — ratio of responses served from cache
Understanding Dimensions
Each dimension has:
| Field | Meaning |
|---|
| Identifier to use in query requests |
| Human-readable label |
Dimensions are what you break down by — "show me spend
by model" means
.
You can combine up to 2 dimensions in a single query (e.g.,
).
Label Resolution
Some dimensions have their raw IDs automatically resolved to human-readable labels in query results. Data rows contain the resolved display names directly:
| Dimension | Resolved to |
|---|
| Key name/label |
| App title or origin URL |
| User name or email address |
| Workspace name |
All other dimensions (e.g.,
,
,
) are returned as-is without resolution.
Rows with an empty
value represent traffic not attributed to a specific org member (e.g., API keys created at the org level).
Dimension Categories
Available with all time ranges:
- — the OpenRouter model ID (permaslug)
- — model variant (e.g., standard, extended)
- — which API key made the request
- — the creator user ID (for org-level queries)
- — workspace ID
- — application ID
Limited to 31-day time ranges:
- — unique ID for each generation (use to drill down to individual requests, then inspect via the skill)
- — upstream provider name
- — request origin/source
- — request country
- — why the generation ended (stop, length, etc.)
- — custom user ID passed by the caller
- — bucketed context length (1K, 10K, 100K, etc.)
Classifier Dimensions
Beyond the standard dimensions above, you can group by classifier dimensions — dynamic labels produced by a user-created classifier (e.g., topic, sentiment, category).
- Use the request field to group by classifier-produced values
- Use the request field to filter on classifier values
- The classifier must belong to your account (validated server-side)
- Classifier dimensions/filters limit the query time range to 31 days
- See the
openrouter-analytics-query
skill for the full request shape
Mapping Questions to Classifier Queries
| Question pattern | Request fields | Notes |
|---|
| "Spend by topic" | classifier_dimensions: { classifier_id, dimension_names: ["topic"] }
+ | Single dimension → column aliased to |
| "Only billing-related requests" | classifier_filters: { classifier_id, filters: [{ field: "category", operator: "eq", value: "billing" }] }
| Filters support , , , only |
| "Sentiment breakdown including unclassified" | classifier_dimensions: { classifier_id, dimension_names: ["sentiment"], include_nulls: true }
| Includes rows without classification |
Understanding Operators
Filter operators for the
array in query requests:
| Operator | Value Type | Meaning |
|---|
| scalar | Equals |
| scalar | Not equals |
| scalar | Greater than |
| scalar | Greater than or equal |
| scalar | Less than |
| scalar | Less than or equal |
| array | In list |
| array | Not in list |
Understanding Granularities
Time bucketing for time-series queries:
| Granularity | Use when |
|---|
| Last few hours, real-time monitoring |
| Last 1–3 days |
| Last week to 3 months |
| Last 3–12 months |
| Year-scale trends |
When no granularity is set, the query returns aggregate totals without time bucketing.
Mapping Questions to Queries
Use this guide to translate natural-language questions into the right metric/dimension/filter combination:
| Question pattern | Metrics | Dimensions | Notes |
|---|
| "How much did I spend?" | | — | Add granularity for trends |
| "Which models cost the most?" | | | Order by desc |
| "How many requests?" | | — | Add or for breakdown |
| "How many tokens?" | | — | Use / for split |
| "Which provider is fastest?" | , | | 31-day limit |
| "What's my cache hit rate?" | | | Rate metric — shows per-model caching |
| "Which API key uses the most?" | , | | — |
| "Usage over time" | or | — | Set |
| "Latency trends" | | — | Set , 31d limit |
| "Usage by country" | | | 31-day limit |
| "How can I save money?" | , , | | See cost optimization in skill |
| "Show me individual requests" | , | | 31-day limit. Use returned IDs with skill for full metadata and content |
| "How much BYOK spend?" | | | Up to 365 days |
| "BYOK vs credits split?" | , | — | Both up to 365 days |
| "BYOK platform fees?" | | | 31-day limit |
| "Non-BYOK inference spend?" | | | 31-day limit |
| "How many guardrail triggers?" | , | | 31-day limit |
| "How many cached responses?" | , | | 31-day limit |
| "Where does my spend go?" | , , | — | Full cost breakdown (up to 365 days) |
| "Web search costs?" | , | | Up to 365 days |
| "File processing costs?" | , | | 31-day limit |
| "Web fetch costs?" | , | | 31-day limit |
Filter Value Reference
Several dimensions are label-resolved in query results — the response shows human-readable names, but filters must use the underlying ID. Here's where to find each:
| Dimension | Filter value | Where to find it |
|---|
| Numeric ID or 64-char SHA-256 hash | Numeric ID: generation metadata ( field). Hash: ( field). Hashes are auto-resolved server-side. If a hash can't be resolved, a sentinel value returns zero rows (no error). |
| Clerk user ID (e.g. ) | User settings or org member list — not the display name/email shown in results. |
| Workspace UUID | Workspace settings page or — not the workspace name shown in results. |
| Numeric app ID | Generation metadata ( field) or app settings — not the app title shown in results. |
| Permaslug (e.g. ) | Model page URL or — not the display name. |
Other dimensions (
,
,
,
,
, etc.) are not enriched — filter values match what's returned in results.
Constraints
- Maximum 2 dimensions per query
- Maximum 20 filters per query
- Maximum 10 classifier dimensions per query
- Maximum 10 classifier filters per query
- Maximum 10,000 rows returned per query (default 1,000)
- (1–10,000): controls max rows per dimension combination. Auto-computed on time-series queries with dimensions to guarantee full time-window coverage. Set explicitly to cap per-group rows (e.g., top N per model per day).
- Most volume/cost metrics: up to 365 days with daily granularity
- Latency/throughput metrics and per-generation dimensions: up to 31 days
- Classifier dimensions/filters: always limited to 31 days
- Minute granularity: only available when the time window is ≤ 3 hours
- Rate-limited to 64 requests per minute