← Blog

Claude API Usage-Based Billing with Stripe: A Guide

2026-10-12 · 6 min read · SubToAPI Team

If you're building a product on top of Claude and charging customers for it, flat subscriptions almost never match your actual costs. Token usage varies wildly between users — one customer sends short classification prompts, another runs long RAG pipelines with 50k-token contexts. The fix is usage-based billing: you track tokens consumed per customer and charge through Stripe's metered pricing, so your revenue scales with your Claude spend instead of guessing at a flat fee.

This article covers the practical mechanics: what usage data you need, how Stripe's metered billing API works, how to map Claude's token counts into Stripe usage records, and the architecture that keeps this reliable at scale. It also covers where things typically break — late usage reporting, double-counting, and reconciling streamed responses.

Why usage-based billing fits Claude API costs

Claude pricing is per-token, split between input and output tokens, with different rates per model. If you're reselling Claude access as part of a SaaS feature, your own costs move in lockstep with usage. Charging a flat $29/month works until a power user burns $400 of tokens that month — then you're losing money on your best customers.

Usage-based billing solves this by letting you:

The tradeoff is complexity: you now need accurate, timely token metering, a billing pipeline that survives partial failures, and a way to reconcile Stripe invoices against actual API responses.

What you need to track

For every request you need, at minimum:

Claude's API returns usage data in the response body on every call — usage.input_tokens and usage.output_tokens. If you're calling Claude directly, you're responsible for capturing this on every single request, including streamed ones where the usage object only arrives in the final chunk.

If you're not calling Claude directly but routing through an API layer like SubToAPI, usage metadata is already exposed per key in the dashboard and in the response payload — see /docs/messages — which removes the need to build your own token-counting instrumentation from scratch.

Stripe's metered billing model

Stripe handles usage-based billing through metered prices attached to a subscription. The flow is:

  1. Create a Product and a recurring Price with usage_type: metered.
  2. Subscribe the customer to that price (no quantity needed upfront).
  3. Report usage throughout the billing period using usage records.
  4. Stripe aggregates usage records and bills automatically at period end.

Creating the metered price:

curl https://api.stripe.com/v1/prices \
  -u $STRIPE_SECRET_KEY: \
  -d "unit_amount=1" \
  -d "currency=usd" \
  -d "recurring[usage_type]=metered" \
  -d "recurring[interval]=month" \
  -d "billing_scheme=per_unit" \
  -d "product=prod_XXXX"

Here unit_amount=1 means 1 cent per unit — you decide what a "unit" represents (e.g., 1,000 tokens, or a cost-weighted credit).

Reporting usage against a subscription item:

curl https://api.stripe.com/v1/subscription_items/si_XXXX/usage_records \
  -u $STRIPE_SECRET_KEY: \
  -d "quantity=1420" \
  -d "timestamp=$(date +%s)" \
  -d "action=increment"

action=increment adds to existing usage for the period; set overwrites it. For token billing, you almost always want increment.

Mapping Claude tokens to Stripe usage records

The core decision is: what is one "unit"? Two common approaches:

Approach 1 — report raw tokens, price per token. Simple but means your Stripe price has a tiny unit_amount (fractions of a cent), which Stripe supports but makes invoices harder to read.

Approach 2 — convert tokens to a cost-weighted credit, then report credits. You calculate (input_tokens input_rate + output_tokens output_rate) per request, round to a sensible unit (e.g., $0.001 per credit), and report credits. This is cleaner on invoices and lets you apply a markup in one place.

A minimal Node example that wraps a Claude call and reports usage:

async function callAndMeter(customerSubItemId, messages) {
  const res = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": process.env.ANTHROPIC_KEY,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify({ model: "claude-opus-4", max_tokens: 1024, messages }),
  });
  const data = await res.json();
  const { input_tokens, output_tokens } = data.usage;

  const credits = Math.ceil(input_tokens * 0.003 + output_tokens * 0.015);

  await fetch(`https://api.stripe.com/v1/subscription_items/${customerSubItemId}/usage_records`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
      "content-type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      quantity: String(credits),
      timestamp: String(Math.floor(Date.now() / 1000)),
      action: "increment",
    }),
  });

  return data;
}

The same pattern applies whether you call Claude directly or through a proxy. If you use SubToAPI's keys (sub_live_...), you get the same usage object back in the response per /docs/quickstart, so this metering logic doesn't change — you just swap the endpoint and auth header.

A practical architecture

For anything beyond a side project, run metering as its own step, decoupled from the request path:

  1. App calls Claude (directly or via SubToAPI) and gets a response with usage data.
  2. App writes a usage event to a queue or database row immediately — don't rely on the Stripe call succeeding synchronously.
  3. A background worker batches events per customer per hour and reports them to Stripe with increment.
  4. A reconciliation job compares your internal usage log against Stripe's reported usage weekly to catch drift from retries or failed Stripe calls.

This decoupling matters because Stripe's usage record endpoint can fail or rate-limit, and you don't want a billing hiccup to break your product's response latency.

For streaming responses, the usage object only appears in the final SSE event (message_delta with a usage field), so your metering hook has to fire on stream completion, not on first token. See /docs/streaming for the event shapes if you're building this against SubToAPI.

Markup and pricing strategy

Most teams apply a fixed multiplier (e.g., 1.4–2x) on top of raw Claude cost per token, rather than exposing raw token pricing to customers. This absorbs volatility if Anthropic changes pricing and covers your infrastructure overhead. Whatever multiplier you choose, keep the conversion math in one function so you can adjust it without touching every call site.

If you'd rather not build and maintain the metering, key management, and dashboard yourself, that's effectively what SubToAPI does: it gives each application its own sub_live_... key, tracks usage per key, and surfaces it in a dashboard — see /pricing — so you can pull usage numbers straight into your own Stripe billing job instead of instrumenting every Claude call manually.

Common pitfalls

questions

Can Stripe bill per-token directly without a credit conversion? Yes — set a very small unit_amount on the metered price and report raw token counts. It works but produces invoices with fractional-cent line items that are harder for customers to read.

Do I need a separate Stripe price for input vs output tokens? You can use two metered prices on the same subscription (one for input, one for output) if you want itemized billing, or combine them into one cost-weighted credit for simplicity. Most teams prefer the single-credit approach.

What happens if a usage record report to Stripe fails? The usage is lost unless you retry — Stripe doesn't pull usage from you. Always persist usage events in your own database first, then report to Stripe from a retryable background job.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →