Skip to content

Pricing API

When the ChatGPT API launched, it was a simple price per token. Over time, LLM providers made pricing complicated: different prices for input and output tokens, batch discounts, prompt caching, reasoning tokens, tool fees, time-of-day pricing and more. A provider response tells you the usage. It does not tell you the cost. You have to do the math yourself. The same request can cost 2x more during peak hours or 10x less if you hit the cache.

The Metergraph Pricing API does the math. Send the original provider response and get the accurate cost, or look up a model’s current price. It uses the same catalog and resolver that price every call Metergraph ingests. For the same model, usage and price, it returns the figure the dashboard shows.

The API is free. You do not need an account.

Check the supported models and pricing first, or go straight to a lookup when you already know the model and provider.

  1. List the supported models and pricing.

    curl https://app.metergraph.dev/v1/pricing/models
  2. Look up a price. Name the model and the provider or channel that bills it.

    curl https://app.metergraph.dev/v1/pricing/lookup \
    -H 'Content-Type: application/json' \
    -d '{"model":"gpt-5","context":{"provider":"openai","region":"global"}}'
  3. Calculate a request’s cost. Send the original provider response as you received it. The API recognizes the provider’s format.

    curl https://app.metergraph.dev/v1/pricing/calculate \
    -H 'Content-Type: application/json' \
    -d '{
    "trace": {
    "type": "message",
    "model": "claude-sonnet-5",
    "usage": {"input_tokens": 1000, "cache_read_input_tokens": 800, "output_tokens": 200}
    },
    "context": {"region": "global"}
    }'

The result includes the resolved price, the total cost, the component breakdown, the price’s source and effective date, and any missing dimensions that would make the answer more precise.

Metergraph reads only the fields needed to calculate the price: the model, the provider, the usage counts, the region, the service tier and the timestamp. Prompt and response content is ignored and never stored. Every other field is discarded before the request is resolved. Request and response bodies are not logged. Send the provider response as you received it. You do not need to strip content first.

The catalog currently covers more than 600 priced model, channel and region combinations. This includes direct providers, cloud platforms such as Bedrock and Vertex, and gateways such as Vercel AI Gateway and OpenRouter. The models endpoint is the live list.

Endpoint Purpose
GET /v1/pricing/models Supported models, their aliases, and the channels, regions, service tiers and pricing dimensions each one has a current price for.
POST /v1/pricing/lookup The price in effect for a model, without usage.
POST /v1/pricing/calculate The cost of one request, from a provider response or trace.

The POST endpoints change nothing. They use POST only to carry a body, so a retry is always safe.

In the models listing, each price’s dimensions name the rates it has: input, output, cache_read, cache_write_5m, cache_write_1h, batch_input, batch_output and request_fee. Its service_tiers include batch only when it has both batch rates.

{
"model": "gpt-5",
"context": {
"provider": "openai",
"channel": "openai-api",
"region": "global",
"service_tier": "standard",
"at": "2026-10-19T12:00:00Z"
}
}

Only model is required. Every context field is optional.

Field Meaning
provider The company you called, such as openai or anthropic. It selects that provider’s direct channel.
channel The channel that bills the call, such as aws-bedrock or vercel-ai-gateway. It takes precedence over provider, because a gateway and a direct provider can price the same model differently.
region The billing region. Leave it out if you do not know it. The result tells you whether it mattered.
service_tier standard or batch. A tier the catalog does not price gets the standard rate and is flagged.
at An ISO 8601 timestamp. The price in effect at that time is used. Defaults to the time of the request.
{
"trace": { "...": "the provider response, unchanged" },
"context": { "region": "global" }
}

format is optional for a provider response: the API recognizes it from the fields each provider always returns. Name it when the response is too sparse to recognize, such as a body you rebuilt with only its usage. An OpenTelemetry span or a Langfuse observation always needs its format.

Format Send
openai.chat_completions An OpenAI Chat Completions response, or a gateway response in the same shape.
openai.responses An OpenAI Responses API response.
anthropic.messages An Anthropic Messages API response.
gemini.generate_content A Google Gemini generateContent response.
bedrock.converse An Amazon Bedrock Converse response. It does not name the model, so send model with it.
otel.gen_ai One OpenTelemetry GenAI span, as OTLP/JSON or with attributes as a flat object.
langfuse One Langfuse generation observation.

model next to trace overrides the model the trace names.

Cached tokens are billed once, whichever way the response counts them. OpenAI and Gemini count cached tokens inside the input total, and gateways such as OpenRouter report cache writes beside cache reads. Each is billed at its own rate. Gemini thinking tokens are billed as output.

context takes the same fields as a lookup. A value in context overrides the same value read from the trace. If context has no at, the trace’s own timestamp is used. If the trace has none, the time of the request is used.

Each provider response format implies its provider. A gateway that relays another company’s model, such as LiteLLM or OpenRouter serving Claude, still returns a Chat Completions or Responses body in OpenAI’s shape. Set provider or channel in context to the one that billed the call. For an OTel span or a Langfuse observation the provider is read from the trace. Set it in context if the trace does not say.

Both POST endpoints also accept a records array. Each record has the shape of a single request, and the response has one result per record, in order.

{"records": [
{"model": "gpt-5", "context": {"provider": "openai"}},
{"model": "claude-sonnet-5", "context": {"provider": "anthropic"}}
]}

Every response carries a results array.

{
"results": [
{
"status": "priced",
"model_id": "anthropic/claude-sonnet-5",
"price": {
"channel": "anthropic-api",
"region": "global",
"currency": "USD",
"input_per_mtok": "2.0",
"output_per_mtok": "10.0",
"cache_read_per_mtok": "0.2",
"effective_from": "2026-06-30",
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing"
},
"effective_at": "2026-10-20T12:00:00Z",
"currency": "USD",
"cost": "0.00416000",
"known_subtotal": "0.00416000",
"estimated_total": null,
"token_basis": "exact",
"catalog_hash": "<catalog SHA-256>",
"retrieval": [],
"components": {
"input": "0.00200000",
"output": "0.00200000",
"cache_read": "0.00016000",
"cache_write": "0.00000000",
"request": "0.00000000",
"search": "0"
},
"missing_dimensions": []
}
]
}

The price above is abridged. The full object also lists the cache write, batch and per-request rates, the pricing rules, and the end of the price’s effective window.

Field Meaning
status priced, partial or unpriced.
model_id The catalog’s id for the model, not the alias you sent.
price The applied price, with its channel, region, rates per million tokens, effective window and source.
effective_at The time the price was resolved for.
currency The ISO 4217 currency of every amount in the result.
cost Calculate only. The complete total, or null when any part is unresolved.
components Token, request and search amounts. Fully priced components sum to cost.
known_subtotal Sum of exactly priced components; excludes token estimates.
estimated_total Fallback estimate when tokens are partial and all retrieval costs are known; otherwise null.
token_basis exact, fallback_estimate or unknown.
catalog_hash SHA-256 identifying the catalog snapshot used.
retrieval Counted retrieval operations, costs and supporting price sources.
missing_dimensions What the answer depends on that the request did not say.

Amounts are decimal strings, so no precision is lost in transit.

partial means a price applied, but the figure depends on something the request left out. unpriced means no price applied. In both cases missing_dimensions names what the request would need to say:

Dimension Meaning
region A global price was used, but regional billing differs. Send region for an exact figure. When only regional prices exist, the result is unpriced.
channel No provider or channel identified a price, so the result is unpriced. Send provider or channel.
input_tokens, output_tokens A usage count was missing. cost is null; any token fallback appears in estimated_total, not known_subtotal.
service_tier The catalog does not price the requested tier, so the standard rate was used.
model The trace did not name a model.

An unknown model is not an error. It returns unpriced, so one record never fails the rest of a batch.

Metergraph may rate limit high-volume use. Every successful or rate-limited response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers. A rate-limited request returns 429 with Retry-After.

A request body may be at most 8 MiB, and a request may carry at most 100 records.

Every error has the same shape:

{"error": {"code": "invalid_request", "message": "body must be valid JSON", "request_id": "3f9a0c1d2b4e5f60"}}
Code Status Meaning
invalid_request 400 The body is not valid JSON or a field has the wrong type.
unsupported_trace_format 400 format is not one of the supported formats, or is missing for a trace the API does not recognize.
too_many_records 400 The request carries more records than allowed.
payload_too_large 413 The body is larger than allowed.
rate_limited 429 The rate limit was reached. Retry after Retry-After seconds.
pricing_unavailable 503 Pricing could not be served. Retry shortly.

The codes are stable. The message text may change, so match on code.

POST /v1/pricing/calculate includes Anthropic web-search and web-fetch fees on anthropic-api. Observed retrieval on an unsupported channel remains unresolved.

The response returns model_id once, at the result level. It adds components.search, retrieval details with price IDs and sources, and catalog_hash.

  • cost is set only for a fully priced call.
  • known_subtotal contains exact components. estimated_total separately includes a token fallback when all retrieval fees are known.
  • Missing required charges produce partial; an unknown model stays unpriced.

Retrieval counts must be nonnegative integers. Langfuse accepts usageDetails.web_search_requests and usageDetails.web_fetch_requests. OpenTelemetry accepts the Metergraph extension attributes gen_ai.usage.web_search_requests and gen_ai.usage.web_fetch_requests.