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
Recommended Metric Set¶
Image Generation¶
- 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¶
- 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¶
- 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)¶
- 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¶
- 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¶
Use for:
- rate limiting
- analytics
- free-tier quotas
Output count¶
Use when:
n > 1images/videos per request
Flexprice Metric Architecture¶
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:
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_valuesto express model-specific logic without exploding your plan count. Flexprice’sCreate priceendpoint explicitly supportsfilter_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.