Skip to content

Billable Metrics

In the context of Flexprice:

A billable metric is a measurable unit of usage that can be priced.

Good metrics are:

  • simple (easy to count)
  • stable (don’t change definition often)
  • composable (can combine with filters like model, quality, etc.)

Core Principle

For the AI system:

1 request = multiple attributes, but only a few core metrics

Avoid turning every parameter into a metric.

Instead:

  • keep metrics minimal
  • push complexity into filters / properties

Image Generation

image_count
  • Matches how providers often bill
  • Works for DALL·E, Seedream, Kling image

Event example

{
  "event_name": "image_generated",
  "properties": {
    "image_count": 1,
    "model": "dall-e-3",
    "size": "1024x1024",
    "quality": "hd"
  }
}

In Flexprice

  • meter: image_count
  • filters:

  • model

  • size
  • quality

Token-Based Models

input_tokens
output_tokens
  • Matches OpenAI / Gemini billing exactly
  • Gives flexibility for differential pricing

Event example

{
  "event_name": "image_generated",
  "properties": {
    "input_tokens": 1200,
    "output_tokens": 4000,
    "model": "gpt-image-1"
  }
}
  • meter: input_tokens
  • meter: output_tokens

Video Generation

video_seconds
  • Universal across Veo, Kling, MuAPI
  • Maps directly to compute cost

Event example

{
  "event_name": "video_generated",
  "properties": {
    "video_seconds": 8,
    "resolution": "1080p",
    "model": "veo-3.1-lite",
    "quality": "standard"
  }
}

In Flexprice

  • meter: video_seconds
  • filters:

  • resolution

  • model
  • quality

Tokenized Video (Seedance)

video_tokens
  • Your system already uses token estimation
  • Needed when provider doesn’t expose duration pricing

Event example

{
  "event_name": "video_generated",
  "properties": {
    "video_tokens": 982344,
    "generate_audio": true,
    "model": "seedance-1-5-pro"
  }
}

World Generation

world_count
  • Composite workflows don’t map cleanly to tokens or seconds
  • Treat as atomic unit

Event example

{
  "event_name": "world_generated",
  "properties": {
    "world_count": 1,
    "quality": "draft",
    "input_mode": "multi_image"
  }
}

Optional Supporting Metrics

These are not primary billing drivers, but useful.

Request count

request_count

Use for:

  • rate limiting
  • analytics
  • free-tier quotas

Output count

output_count

Use when:

  • n > 1 images/videos per request

Flexprice Metric Architecture

Meters:
- image_count
- input_tokens
- output_tokens
- video_seconds
- video_tokens
- world_count

Everything else → properties / filters


Mapping to Your Current System

From your repo:

Model Type Current Logic Recommended Metric
DALL·E per image image_count
GPT Image token-based input_tokens + output_tokens
Gemini token-based input_tokens + output_tokens
Veo duration-based video_seconds
Kling duration-based video_seconds
Seedance token formula video_tokens
MuAPI duration formula video_seconds
HappyHorse per job video_jobs or request_count
Marble credit-based world_count

Advanced Pattern

Hybrid Metric Strategy

Sometimes you want both:

{
  "video_seconds": 8,
  "video_tokens": 982344
}

Why:

  • fallback pricing
  • validation
  • analytics

Flexprice API Payload Templates

One important detail before the payloads: Flexprice’s official Create price API clearly supports meter_id, filter_values, billing_model, price_unit_type, tiers, and transform rules, while event ingestion accepts event_name, external_customer_id, event_id, properties, source, and timestamp. The public docs did not surface a dedicated “create meter” endpoint page in the API reference search, so the payloads below assume you already have the needed meter_id values available from your Flexprice setup.

1. Create a customer

Flexprice’s customer creation payload requires external_id and supports address and contact fields.

curl --request POST \
  --url https://us.api.flexprice.io/v1/customers \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "external_id": "cust_001",
  "email": "billing@example.com",
  "name": "Example Studio",
  "address_country": "DE",
  "address_city": "Berlin",
  "address_line1": "Example Street 1",
  "address_postal_code": "10115",
  "metadata": {
    "crm_account_id": "acc_42",
    "segment": "pro"
  }
}'

2. Create a plan

Flexprice’s Create plan endpoint takes a compact payload with name, description, display_order, lookup_key, and metadata.

curl --request POST \
  --url https://us.api.flexprice.io/v1/plans \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "name": "AI Pro",
  "description": "Usage-based AI generation plan for image, video, and world generation",
  "display_order": 10,
  "lookup_key": "ai_pro",
  "metadata": {
    "product_family": "ai_generation"
  }
}'

3. Optional: create a custom price unit for credits

Flexprice supports custom price units with base_currency, code, conversion_rate, name, and symbol. This is the cleanest way to represent your internal credits model if you want to expose “credits” alongside fiat pricing.

In your repo, you already convert USD into credits. You could reflect that as a Flexprice price unit or keep credits in your app and use Flexprice only for USD billing.

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices/units \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "base_currency": "USD",
  "code": "CREDIT",
  "conversion_rate": "0.01",
  "name": "AI Credit",
  "symbol": "cr",
  "metadata": {
    "note": "1 credit = 0.01 USD"
  }
}'

4. Attach a flat per-image DALL·E price to the plan

Flexprice’s Create price endpoint supports a usage price attached to a plan, with optional filter_values so you can keep one meter and vary pricing by model, size, or quality.

This example assumes you have a meter that counts generated images, such as meter_img_count.

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_img_count",
  "display_name": "DALL-E 3 1024 standard",
  "lookup_key": "dalle3_1024_std",
  "amount": "0.04",
  "filter_values": {
    "model": ["dall-e-3"],
    "size": ["1024x1024"],
    "quality": ["standard"]
  },
  "metadata": {
    "family": "openai_image"
  }
}'

For DALL·E 3 HD:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_img_count",
  "display_name": "DALL-E 3 1024 HD",
  "lookup_key": "dalle3_1024_hd",
  "amount": "0.08",
  "filter_values": {
    "model": ["dall-e-3"],
    "size": ["1024x1024"],
    "quality": ["hd"]
  }
}'

5. Attach GPT Image token pricing to the plan

Your repo treats gpt-image-1 as token-based, with separate input and output token charges. Flexprice can represent this cleanly as two usage prices, one per meter.

Input tokens:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_gpt_image_input_tokens",
  "display_name": "GPT Image input tokens",
  "lookup_key": "gpt_image_input_tokens",
  "amount": "0.00001",
  "transform_quantity": {
    "divide_by": 1,
    "round": "up"
  },
  "filter_values": {
    "model": ["gpt-image-1"]
  }
}'

Output tokens:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_gpt_image_output_tokens",
  "display_name": "GPT Image output tokens",
  "lookup_key": "gpt_image_output_tokens",
  "amount": "0.00004",
  "transform_quantity": {
    "divide_by": 1,
    "round": "up"
  },
  "filter_values": {
    "model": ["gpt-image-1"]
  }
}'

6. Attach Veo per-second pricing

Your current workflow prices Veo by duration, with resolution and quality affecting the result. In Flexprice, the cleanest pattern is one duration meter plus filtered prices per model and resolution tier.

Example for Veo 3.1 Lite at 1080p:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_video_seconds",
  "display_name": "Veo 3.1 Lite 1080p per second",
  "lookup_key": "veo_3_1_lite_1080p_sec",
  "amount": "0.08",
  "filter_values": {
    "model": ["veo-3.1-lite-generate-001"],
    "resolution": ["1080p"],
    "quality": ["standard"]
  }
}'

Example for Veo 3.1 at 4k:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_video_seconds",
  "display_name": "Veo 3.1 4k per second",
  "lookup_key": "veo_3_1_4k_sec",
  "amount": "0.60",
  "filter_values": {
    "model": ["veo-3.1-generate-001"],
    "resolution": ["4k"],
    "quality": ["standard"]
  }
}'

7. Attach Kling per-second pricing

Your Kling logic depends on duration, mode, resolution, input type, and model multiplier. In Flexprice, you can preserve that logic by keeping a per-second base price and pushing model/mode/resolution into filters.

Standard Kling v2.5 Turbo at 720p:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_video_seconds",
  "display_name": "Kling v2.5 Turbo std per second",
  "lookup_key": "kling_v2_5_turbo_std_sec",
  "amount": "0.05",
  "filter_values": {
    "model": ["kling-v2-5-turbo"],
    "mode": ["std"],
    "resolution": ["720p"],
    "input_type": ["text", "image"]
  }
}'

Kling Omni pro:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_video_seconds",
  "display_name": "Kling Omni pro per second",
  "lookup_key": "kling_omni_pro_sec",
  "amount": "0.12",
  "filter_values": {
    "model": ["kling-video-o1"],
    "mode": ["pro"],
    "resolution": ["720p", "1080p"]
  }
}'

8. Attach Seedance token pricing

Your repo calculates Seedance from token count, either provider-reported completion tokens or an internal estimation fallback. Flexprice can rate that as one token meter, split by audio and model family.

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_seedance_tokens",
  "display_name": "Seedance 1.5 Pro tokens",
  "lookup_key": "seedance_1_5_pro_tokens",
  "amount": "0.0000012",
  "filter_values": {
    "model": ["seedance-1-5-pro-251215"],
    "generate_audio": ["false"]
  }
}'

With audio enabled:

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_seedance_tokens",
  "display_name": "Seedance 1.5 Pro tokens with audio",
  "lookup_key": "seedance_1_5_pro_tokens_audio",
  "amount": "0.0000024",
  "filter_values": {
    "model": ["seedance-1-5-pro-251215"],
    "generate_audio": ["true"]
  }
}'

9. Attach HappyHorse fixed per-job pricing

HappyHorse in your repo is a fixed cost by mode, not duration-based in the implemented function. That maps naturally to a per-job meter.

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "entity_id": "<plan_id>",
  "entity_type": "PLAN",
  "type": "USAGE",
  "billing_cadence": "RECURRING",
  "billing_model": "FLAT_FEE",
  "billing_period": "MONTHLY",
  "billing_period_count": 1,
  "invoice_cadence": "ARREAR",
  "currency": "USD",
  "price_unit_type": "FIAT",
  "meter_id": "meter_video_jobs",
  "display_name": "HappyHorse standard job",
  "lookup_key": "happyhorse_std_job",
  "amount": "1.8",
  "filter_values": {
    "provider": ["alibaba"],
    "mode": ["std"]
  }
}'

10. Create a subscription for the customer

Flexprice’s subscription creation endpoint supports plan_id, customer_id, billing_period, currency, and optional addons and other settings.

curl --request POST \
  --url https://us.api.flexprice.io/v1/subscriptions \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "plan_id": "<plan_id>",
  "customer_id": "<customer_id>",
  "billing_period": "MONTHLY",
  "currency": "USD",
  "customer_timezone": "Europe/Berlin",
  "metadata": {
    "workspace_id": "ws_123",
    "source": "app_signup"
  }
}'

11. Ingest usage events from your AI workflows

Flexprice’s event ingestion API accepts event_name, external_customer_id, customer_id, event_id, properties, source, and timestamp. The properties object is exactly where your model, size, quality, resolution, duration, tokens, and input type should go.

DALL·E image event

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "image_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_img_0001",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:00:00Z",
  "properties": {
    "provider": "openai",
    "model": "dall-e-3",
    "size": "1024x1024",
    "quality": "standard",
    "image_count": 1
  }
}'

GPT Image token event

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "image_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_img_0002",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:01:00Z",
  "properties": {
    "provider": "openai",
    "model": "gpt-image-1",
    "input_tokens": 1200,
    "output_tokens": 4160,
    "size": "1024x1024",
    "quality": "high"
  }
}'

Veo video event

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "video_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_vid_0001",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:02:00Z",
  "properties": {
    "provider": "google-vertex",
    "model": "veo-3.1-lite-generate-001",
    "duration_seconds": 8,
    "resolution": "1080p",
    "quality": "standard",
    "video_count": 1,
    "generate_audio": true
  }
}'

Kling video event

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "video_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_vid_0002",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:03:00Z",
  "properties": {
    "provider": "kling",
    "model": "kling-v2-6",
    "mode": "pro",
    "duration_seconds": 10,
    "resolution": "720p",
    "input_type": "image",
    "generate_audio": false
  }
}'

Seedance token event

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "video_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_vid_0003",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:04:00Z",
  "properties": {
    "provider": "byteplus",
    "model": "seedance-1-5-pro-251215",
    "completion_tokens": 982344,
    "duration_seconds": 5,
    "resolution": "1080p",
    "fps": 24,
    "generate_audio": false
  }
}'

Marble world-generation event

Even though your current Marble logic is internal and credit-oriented, it still fits as a usage event.

curl --request POST \
  --url https://us.api.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "event_name": "world_generated",
  "external_customer_id": "cust_001",
  "event_id": "evt_world_0001",
  "source": "ai-gateway",
  "timestamp": "2026-04-14T15:05:00Z",
  "properties": {
    "provider": "marble",
    "model": "Marble 0.1-plus",
    "input_mode": "multi_image",
    "quality": "draft",
    "world_count": 1
  }
}'

12. Bulk-create multiple prices in one call

Flexprice also supports bulk price creation with an items array. That is usually the fastest way to load your model catalog into one environment.

curl --request POST \
  --url https://us.api.flexprice.io/v1/prices/bulk \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "items": [
    {
      "entity_id": "<plan_id>",
      "entity_type": "PLAN",
      "type": "USAGE",
      "billing_cadence": "RECURRING",
      "billing_model": "FLAT_FEE",
      "billing_period": "MONTHLY",
      "billing_period_count": 1,
      "invoice_cadence": "ARREAR",
      "currency": "USD",
      "price_unit_type": "FIAT",
      "meter_id": "meter_img_count",
      "display_name": "DALL-E 2 1024",
      "lookup_key": "dalle2_1024",
      "amount": "0.02",
      "filter_values": {
        "model": ["dall-e-2"],
        "size": ["1024x1024"]
      }
    },
    {
      "entity_id": "<plan_id>",
      "entity_type": "PLAN",
      "type": "USAGE",
      "billing_cadence": "RECURRING",
      "billing_model": "FLAT_FEE",
      "billing_period": "MONTHLY",
      "billing_period_count": 1,
      "invoice_cadence": "ARREAR",
      "currency": "USD",
      "price_unit_type": "FIAT",
      "meter_id": "meter_video_jobs",
      "display_name": "HappyHorse pro job",
      "lookup_key": "happyhorse_pro_job",
      "amount": "2.4",
      "filter_values": {
        "provider": ["alibaba"],
        "mode": ["pro"]
      }
    }
  ]
}'

How to model the system in Flexprice

The most reliable pattern is this:

  • Use one customer per billed workspace or account. Flexprice’s customer object is designed around your own external_id.
  • Use one plan per commercial tier. Flexprice’s plan API is intentionally lightweight, and pricing attaches separately.
  • Use prices with filter_values to express model-specific logic without exploding your plan count. Flexprice’s Create price endpoint explicitly supports filter_values, meter_id, tiers, and transforms.
  • Send raw usage events from your workflows, with the real model parameters in properties. Flexprice’s event ingestion endpoint is built for that shape.
  • Keep credits either as a custom price unit or as an app-layer abstraction on top of the Flexprice-rated USD amount. Flexprice’s price-unit API supports custom units and conversion rates.

The one gap is meter creation itself. Since price creation clearly references meter_id, the safe implementation path is to provision meters in Flexprice first, then plug those IDs into the price payloads above.