> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runledger.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runledger.co/_mcp/server.

# Create a cost model

POST https://api.runledger.co/v1/cost_models
Content-Type: application/json

A cost model is what you pay a provider for one model: the units it is metered in, the dimensions that change the rate, and which of them your customer controls and which the provider controls. Send `catalog_model` to take all of that, and the rates, from the provider price catalog (`source: catalog`), and add `overrides` for your own negotiated or committed rates. Or send `provider`, `model` and a `rate_card` to enter the rates yourself (`source: custom`). Rates live on rate cards, so changing them never changes the model. In live mode an agent's write is refused with `403` and `proposal_required`: put the same object in a config proposal. People change live configuration in the dashboard.

Reference: https://docs.runledger.co/api-reference/cost-models/create

## Authentication

- `Authorization` header (bearer token, required) — A secret key (`rl_sk_test_...`, `rl_sk_live_...`) or a restricted key with limited scopes (`rl_rk_test_...`, `rl_rk_live_...`). A restricted key can carry `agent: true` and an `expires_at`, and is what a coding agent should hold. Keys end in a checksum so secret scanners can recognize them. People create, roll and revoke keys in the dashboard; `GET /v1/api_keys` lists them, masked.

## Request

### Headers

- `Runledger-Dry-Run` (boolean, optional) — Send `true` to see what the write would do without doing it. The response is the normal body with `dry_run: true`, `changes` (path, before, after), `warnings` and `errors` added, and nothing is stored. Validation runs as it would for a real write. The request is kept in request logs, tagged **Dry run**. When the same change is then made for real, its audit event has `dry_run_preceded: true`.

### Body (application/json)

This endpoint expects a CreateCostModelRequest.

- `key` (string, required) — A key you choose. Requests accept the key or the ID.
- `name` (string, required)
- `source` (enum, optional) — Where the rates come from. Default `catalog` when `catalog_model` is sent, and `custom` otherwise.
  - Allowed values: `catalog`, `custom`
- `catalog_model` (string, optional) — Takes the provider, model, kind, units, dimensions and rates from this catalog model (`catm_...`), and follows its price versions from each `effective_from`. With it, leave out `provider`, `model`, `kind`, `units`, `dimensions` and `rate_card`. Use `overrides` to change a rate.
- `overrides` (list of CostOverride, optional) — Your own rates for a catalog cost model. Not for a custom cost model, whose rate cards are already yours.
- `provider` (string, optional) — A custom cost model. The provider as `trace()` names it.
- `model` (string, optional) — A custom cost model. The model ID as the provider names it, as `trace()` sends it.
- `kind` (enum, optional) — What the model produces. Open: new values may be added.
  - Allowed values: `text`, `image`, `video`, `audio`, `speech`, `embedding`, `compute`, `agent_session`, `other`
- `units` (list of CostUnitDeclaration, optional)
- `dimensions` (list of CostDimension, optional)
- `rate_card` (RateCardInput, optional) — A custom cost model. The first rates. Without `effective_from` the card is active from the moment the model is created, and a given `effective_from` can't be in the past. In a configuration document, a `rate_card` adds a new version only when its rates differ from the latest, so applying the document twice changes nothing the second time.
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

## Response

### 200

The new cost model.

- `id` (string, required)
- `object` ("cost_model", required)
- `key` (string, required)
- `name` (string, required)
- `source` (enum, required) — Where a cost model's rates come from. `catalog`: the provider price catalog that Runledger maintains, with your overrides on top. `custom`: rate cards you enter yourself.
  - Allowed values: `catalog`, `custom`
- `provider` (string, required)
- `model` (string, required)
- `status` (enum, required)
  - Allowed values: `active`, `archived`
- `units` (list of CostUnitDeclaration, required)
- `dimensions` (list of CostDimension, required)
- `livemode` (boolean, required)
- `created_at` (datetime, required)
- `catalog_model` (string, optional) — For `source: catalog`. The catalog model this cost model follows (`catm_...`).
- `overrides` (list of CostOverride, optional) — Your overrides of the catalog rates. Always empty for a custom cost model.
- `kind` (enum, optional) — What the model produces. Open: new values may be added.
  - Allowed values: `text`, `image`, `video`, `audio`, `speech`, `embedding`, `compute`, `agent_session`, `other`
- `active_rate_card` (string, optional) — The rate card in force now (`rc_...`). Left out until one is active.
- `active_rate_card_version` (integer, optional)
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.
- `actor` (Actor, optional) — Who made a change. Runledger fills it in from the credential, so a client can't claim to be someone else: an API key always acts as itself, the dashboard calls the API as the signed-in person, and the MCP server passes the client's name and the person it acts for. A request that sends a `Runledger-Actor` header is refused with `400 parameter_unknown`.

## Errors

### 400 Bad Request Error

The request was malformed or a parameter was invalid. A configuration that fails validation, such as two equally specific overrides with overlapping dates, returns `config_invalid`.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 401 Unauthorized Error

The API key is missing, invalid or revoked.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 403 Forbidden Error

The key can't do this. A restricted or agent key lacks the scope (`insufficient_scope`, with `required_scope`), a live key called a sandbox-only endpoint, an agent tried a live config write that needs a proposal (`proposal_required`), or something only a signed-in person can do.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 409 Conflict Error

The object is in a state that doesn't allow this (`invalid_state`, for example approving a stale config proposal or deleting a price rule a plan rate still uses), or a request with the same idempotency key is still in progress.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 422 Unprocessable Entity Error

The idempotency key was already used with a different request body.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 429 Too Many Requests Error

Too many requests. Wait for the number of seconds in `Retry-After`.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 500 Internal Server Error

Something went wrong on Runledger's side. Safe to retry with the same idempotency key.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

## Types

### CostOverride

Your own rate, layered on a catalog cost model. For each usage line, an override that is in force at the line's timestamp and matches its unit and dimensions beats the catalog price, and the most specific match wins: one that names a unit and dimensions beats one that names only a unit, and that beats one that names neither. Two equally specific overrides whose dates overlap are a validation error: the write is refused with `400` and `config_invalid`, and nothing is saved. Send exactly one of `discount_percent` and `unit_amount_decimal`.

- `key` (string, required) — A key you choose, unique within the cost model. Cost lines name it when it priced them.
- `unit` (string, optional) — Limit the override to one unit. Leave it out for every unit of the model.
- `dimensions` (map from string to string, optional) — Limit the override to lines with these dimension values. Leave a dimension out to match any value.
- `discount_percent` (string, optional) — A decimal from 0 to 1 taken off the catalog rate of every matching line, tiers included. `"0.2"` is 20% below the catalog price.
- `unit_amount_decimal` (string, optional) — Replaces the catalog rate with this one, in minor units for `per` units. Needs `unit`. A unit the catalog model doesn't have is added, which is how you extend a catalog model.
- `per` (string, optional, default: 1) — How many units `unit_amount_decimal` covers, as a decimal string.
- `effective_from` (datetime, optional) — When the override starts to apply. Leave it out to apply from the moment it is saved.
- `effective_to` (datetime, optional) — When it stops applying, such as the end of a commitment. Leave it out for no end.
- `category` (enum, optional) — A label for reporting. It changes no price. Open: new values may be added.
  - Allowed values: `negotiated_discount`, `committed_spend`, `private_deployment`, `self_hosted`, `other`
- `reason` (string, optional) — Why, in your words. For example the contract it comes from. Recorded in the audit log.

### CostUnitDeclaration

One unit a cost model is metered in.

- `unit` (string, required) — What is counted. Use `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_write_tokens`, `reasoning_tokens`, `images`, `video_seconds`, `audio_seconds`, `gpu_seconds`, `session_hours`, `requests` or `searches`, or your own plural, such as `tool_calls`.
- `controlled_by` (enum, required) — Who decides the value. `customer`: your end customer's request sets it, so it is known before the work starts and `check()` can price it. Examples are the resolution, the quality, the seconds of video asked for and the number of reference images. `provider`: the model provider or your own infrastructure sets it while the work runs, so it is known only afterwards and `trace()` reports it. Examples are the input and output tokens and the GPU seconds.
  - Allowed values: `customer`, `provider`
- `description` (string, optional)

### CostDimension

A property that changes the rate for a unit, such as resolution, quality, context bucket or service tier.

- `key` (string, required)
- `controlled_by` (enum, required) — Who decides the value. `customer`: your end customer's request sets it, so it is known before the work starts and `check()` can price it. Examples are the resolution, the quality, the seconds of video asked for and the number of reference images. `provider`: the model provider or your own infrastructure sets it while the work runs, so it is known only afterwards and `trace()` reports it. Examples are the input and output tokens and the GPU seconds.
  - Allowed values: `customer`, `provider`
- `values` (list of string, optional) — The values the rate cards may use. A usage line with another value gets the `rate_not_found` warning.
- `description` (string, optional)

### RateCardInput

- `currency` (string, required) — Lowercase ISO 4217 code the rates are in.
- `rates` (list of CostRate, required)
- `effective_from` (datetime, optional) — When this version starts to price usage. It must be now or later, and after the start of every earlier version of the cost model, including a scheduled one. A new version applies only from this date onward. Recorded usage is never repriced: events and costs dated before it keep the earlier version. Leave it out to save a draft.
- `as_of` (date, optional) — The date you read these rates from the provider, so a person can see how fresh they are.
- `source_reference` (string, optional) — Where the rates came from, such as the provider's pricing page address or a contract reference.

### Actor

Who made a change. Runledger fills it in from the credential, so a client can't claim to be someone else: an API key always acts as itself, the dashboard calls the API as the signed-in person, and the MCP server passes the client's name and the person it acts for. A request that sends a `Runledger-Actor` header is refused with `400 parameter_unknown`.

- `type` (enum, required) — `user`: a signed-in person, including a person at Runledger support acting in your account, whose `name` reads `Runledger support`. `agent`: an agent connected through an OAuth session or an agent key. `key`: any other API key. `rule`: a reversal rule. `system`: Runledger itself, such as a clock.
  - Allowed values: `user`, `agent`, `key`, `rule`, `system`
- `id` (string, optional) — The user, key or agent session ID. Left out for `rule` and `system`, except a change a customer makes on the customer view, which carries the customer session's ID.
- `name` (string, optional) — A display name, such as a person's name, a key's name or the MCP client.
- `on_behalf_of` (string, optional) — The person an agent acts for, when known.
- `client` (string, optional) — The software that made the call, such as an MCP client or SDK.

### ConfigIssue

A problem or warning about a part of a configuration document.

- `code` (string, required) — A stable code. New codes may be added.
- `message` (string, required)
- `path` (string, optional) — Where in the document, as a dotted path.

### ErrorResponseBlockedByItems

- `object` (enum, required) — The kind of object in the way: a routing rule, or a customer with a `payment_provider` override.
  - Allowed values: `routing_rule`, `customer`
- `id` (string, required) — The object's ID, `rr_...` for a routing rule and `cus_...` for a customer.

### CostRate

The price of one unit, for the usage lines whose dimensions match. A rate with fewer dimensions matches more lines, and the most specific matching rate wins. Send `unit_amount_decimal` or `tiers`.

- `unit` (string, required)
- `per` (string, optional, default: 1) — How many units `unit_amount_decimal` covers, as a decimal string. Use `"1000000"` for a price per million tokens.
- `unit_amount_decimal` (string, optional) — Minor units of the rate card's currency for `per` units, as a decimal string.
- `dimensions` (map from string to string, optional) — The dimension values this rate is for. Leave a dimension out to match any value.
- `tier_mode` (enum, optional, default: graduated) — With `tiers`. `graduated`: each band's price applies to the quantity inside that band. `volume`: the band the whole quantity falls in prices all of it, as when a long prompt is priced higher in full.
  - Allowed values: `graduated`, `volume`
- `tiers` (list of RateTier, optional)

### RateTier

One band of a quantity. The last band leaves out `up_to`.

- `unit_amount_decimal` (string, required) — Minor units per unit of the band, as a decimal string.
- `up_to` (string, optional) — The quantity this band runs up to, as a decimal string.

## Examples

**Request**

```json
{
  "key": "vidlab-v2",
  "name": "Vidlab v2 video"
}
```

**Response**

```json
{
  "id": "cm_6bR3xN8vQe",
  "object": "string",
  "key": "vidlab-v2",
  "name": "string",
  "source": "catalog",
  "provider": "vidlab",
  "model": "vidlab-v2",
  "status": "active",
  "units": [
    {
      "unit": "video_seconds",
      "controlled_by": "customer",
      "description": "string"
    }
  ],
  "dimensions": [
    {
      "key": "resolution",
      "controlled_by": "customer",
      "values": [
        "720p",
        "1080p"
      ],
      "description": "string"
    }
  ],
  "livemode": true,
  "created_at": "2024-01-15T09:30:00Z",
  "catalog_model": "catm_5xT7qD2mKa",
  "overrides": [
    {
      "key": "textco_negotiated",
      "unit": "input_tokens",
      "dimensions": {
        "resolution": "1080p"
      },
      "discount_percent": "0.2",
      "unit_amount_decimal": "10",
      "per": "1",
      "effective_from": "2024-01-15T09:30:00Z",
      "effective_to": "2024-01-15T09:30:00Z",
      "category": "negotiated_discount",
      "reason": "Negotiated 20% off to 31 December 2026"
    }
  ],
  "kind": "text",
  "active_rate_card": "string",
  "active_rate_card_version": 1,
  "metadata": {},
  "actor": {
    "type": "user",
    "id": "usr_4tN8vB2cXk",
    "name": "Claude Code",
    "on_behalf_of": "usr_4tN8vB2cXk",
    "client": "claude-code"
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.runledger.co/v1/cost_models"

payload = {
    "key": "vidlab-v2",
    "name": "Vidlab v2 video"
}
headers = {
    "Authorization": "Bearer <api_key>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.runledger.co/v1/cost_models';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <api_key>', 'Content-Type': 'application/json'},
  body: '{"key":"vidlab-v2","name":"Vidlab v2 video"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.runledger.co/v1/cost_models"

	payload := strings.NewReader("{\n  \"key\": \"vidlab-v2\",\n  \"name\": \"Vidlab v2 video\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <api_key>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.runledger.co/v1/cost_models")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <api_key>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"key\": \"vidlab-v2\",\n  \"name\": \"Vidlab v2 video\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.runledger.co/v1/cost_models")
  .header("Authorization", "Bearer <api_key>")
  .header("Content-Type", "application/json")
  .body("{\n  \"key\": \"vidlab-v2\",\n  \"name\": \"Vidlab v2 video\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.runledger.co/v1/cost_models', [
  'body' => '{
  "key": "vidlab-v2",
  "name": "Vidlab v2 video"
}',
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.runledger.co/v1/cost_models");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <api_key>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"key\": \"vidlab-v2\",\n  \"name\": \"Vidlab v2 video\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <api_key>",
  "Content-Type": "application/json"
]
let parameters = [
  "key": "vidlab-v2",
  "name": "Vidlab v2 video"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runledger.co/v1/cost_models")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```