Metered usage

Charge customers based on the usage they actually consume.

For the complete documentation index, see llms.txt. Prefer markdown by appending .md to documentation URLs or sending Accept: text/markdown.

Metered usage billing lets you charge customers for what they actually use, such as API calls, AI tokens, storage, generated images, or processed jobs.

TurboStarter supports metered billing for recurring subscriptions:

  • define a billing variant as BillingType.METERED
  • let customers subscribe through the normal checkout flow
  • report billable usage from trusted server-side code
  • query usage later for billing screens or internal checks

How it works

Metered billing follows a simple pattern:

A customer subscribes to a metered recurring plan.

Your app tracks billable usage on the backend.

Your server reports that usage to the billing provider.

The provider aggregates usage and bills the customer for the billing period.

This works especially well when your product usage changes over time and a flat subscription would be too rigid.

Configuration

Metered usage is configured like any other billing variant, but with type: BillingType.METERED.

index.ts
export const config = billingConfigSchema.parse({
  plans: [
    {
      id: BillingPlan.PREMIUM,
      name: "Premium",
      description: "Best for usage-based products",
      badge: "Popular",
      features: [
        "Unlimited projects",
        "Usage-based billing",
        "Priority support",
      ],
      variants: [
        {
          id: "price_monthly_metered",
          meterId: "mtr_your_meter_id",
          type: BillingType.METERED, 
          unit: "credit",
          model: BillingModel.RECURRING,
          interval: RecurringInterval.MONTH,
          trialDays: 7,
          tiers: [
            { cost: 8, upTo: 25_000 },
            { cost: 6, upTo: 125_000 },
            { cost: 4 },
          ],
        },
      ],
    },
  ],
}) satisfies BillingConfig;

Let's break down the fields:

  • id: The provider-specific price, variant, or product ID. This must match your billing provider exactly.
  • type: Must be BillingType.METERED.
  • meterId: The provider meter identifier used when querying usage.
  • unit: The unit shown in your UI, such as credit, request, or token.
  • model: Must be BillingModel.RECURRING.
  • interval: Required for metered billing.
  • trialDays: Optional trial length.
  • cost: Use this for a simple flat usage rate.
  • tiers: Use this for tiered usage pricing.

Metered billing is recurring-only

Metered variants cannot use BillingModel.ONE_TIME. The billing schema validates this for you.

Fixed usage pricing

Use cost when every unit should cost the same amount.

index.ts
{
  id: "price_monthly_metered",
  meterId: "mtr_your_meter_id",
  type: BillingType.METERED,
  unit: "request",
  model: BillingModel.RECURRING,
  interval: RecurringInterval.MONTH,
  cost: 5,
}

Tiered usage pricing

Use tiers when you want the unit price to change as usage grows.

index.ts
{
  id: "price_monthly_metered",
  meterId: "mtr_your_meter_id",
  type: BillingType.METERED,
  unit: "credit",
  model: BillingModel.RECURRING,
  interval: RecurringInterval.MONTH,
  tiers: [
    { cost: 8, upTo: 25_000 },
    { cost: 6, upTo: 125_000 },
    { cost: 4 },
  ],
}

This works well for patterns like:

  • charging the same amount for every API call or token
  • cheaper unit pricing at higher usage volumes
  • including an initial amount of usage at a lower or zero rate

Checkout

Metered variants use the normal recurring checkout flow. Unlike per-seat billing, TurboStarter does not send a quantity during checkout for metered plans.

That is because metered billing is based on usage reported later, not on an upfront seat count.

In practice, this means:

  1. Checkout creates the subscription
  2. Your app reports usage after billable work happens
  3. The provider calculates the final bill from reported usage

Reporting usage

Usage reporting should happen in trusted server-side code only.

That can be:

  • an API route
  • a server action
  • a background job
  • a queue worker

Do not report usage directly from the browser

Usage affects invoices, so the backend should be the source of truth.

Example flow

Identify the billing reference for the user or organization.

Resolve the provider customer connected to that reference.

Perform the billable work.

Report the usage amount to the billing provider.

report-usage.ts
import { getCustomersByReferenceId } from "@workspace/billing/server";
import { recordUsage } from "@workspace/billing-web/server";

export const reportCreditsUsage = async ({
  referenceId,
  quantity,
}: {
  referenceId: string;
  quantity: number;
}) => {
  const [customer] = await getCustomersByReferenceId(referenceId);

  if (!customer) {
    return { recorded: false };
  }

  return recordUsage({
    externalId: customer.externalId,
    quantity,
    event: "credits_used",
  });
};

In this example:

  • referenceId is the user or organization being billed
  • externalId is the provider customer ID stored by TurboStarter
  • event is used by providers that track usage through meter events

Keep usage reporting idempotent when possible

If the same billable action can be retried, make sure your own backend logic avoids double-reporting usage.

Querying usage

TurboStarter also supports querying aggregated usage. This is useful when you want to:

  • show current usage in billing settings
  • show usage during a trial or billing period
  • validate internal dashboards or support workflows
get-usage.ts
import { getCustomersByReferenceId } from "@workspace/billing/server";
import { getUsage } from "@workspace/billing-web/server";

export const getCurrentUsage = async ({
  referenceId,
  meterId,
  start,
  end,
}: {
  referenceId: string;
  meterId: string;
  start: Date;
  end: Date;
}) => {
  const [customer] = await getCustomersByReferenceId(referenceId);

  if (!customer) {
    return { usage: 0, start, end };
  }

  return getUsage({
    externalId: customer.externalId,
    meterId,
    start,
    end,
  });
};

The billing UI can use this to show usage for the active subscription period.

Provider notes

Metered billing works across the supported web billing providers, but the way usage is reported differs slightly.

  • Stripe: reports billing meter events for a customer and queries usage through the configured meterId
  • Lemon Squeezy: records usage against the active subscription item and returns the current period usage from that item
  • Polar: ingests meter events for a customer and queries aggregated totals through the configured meterId

The two IDs that matter most are:

  • variant.id: the provider price, variant, or product ID used for checkout
  • meterId: the provider meter identifier used for querying usage

Make sure both match the correct objects in your billing provider.

Discounts

Metered variants support cost and tiers, so TurboStarter can still describe the pricing model in the UI.

One important difference from flat and per-seat recurring plans is that automatic recurring discount comparison is not applied to metered variants in the same way. In most cases, your metered plan pricing should be explained directly through the variant pricing itself.

Testing

Before shipping, test the full flow:

  1. Subscribe to a metered plan.
  2. Trigger a billable action from your app.
  3. Verify that usage is reported successfully from server-side code.
  4. Query usage for the current period and confirm it matches what you expect.
  5. Check the billing provider dashboard to make sure usage and invoicing look correct.

If something looks off, the most common causes are:

  • the variant is missing type: BillingType.METERED
  • the meterId is missing or incorrect
  • usage is being reported from the wrong billing reference
  • the provider customer does not exist yet for that reference
  • your backend is reporting usage twice for the same billable action

For most usage-based SaaS products, the simplest setup is:

  1. Create a recurring metered variant with a clear unit.
  2. Configure the matching metered price in your billing provider.
  3. Add a meterId to your billing config.
  4. Report usage only from trusted server-side code.
  5. Query usage for the current billing period anywhere you want to show progress or billing context.

This gives you a clean model: TurboStarter handles subscription checkout and billing state, while your application decides what counts as billable usage and when to report it.

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Try TurboStarter