> 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.

# List catalog models

GET https://api.runledger.co/v1/catalog/models

The provider models in the catalog, with the units and dimensions each is priced in and the price version in force. Filter by provider, kind or unit to find the model a piece of code calls. A model that isn't listed isn't covered by the freshness target and needs a custom cost model. Needs `config:read`.

Reference: https://docs.runledger.co/api-reference/catalog/models/list

## 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

### Query parameters

- `provider` (string, optional) — Only models from this provider key.
- `kind` (enum, optional) — Only this kind of model.
  - Allowed values: `text`, `image`, `video`, `audio`, `speech`, `embedding`, `compute`, `agent_session`, `other`
- `unit` (string, optional) — Only models priced in this unit, such as `video_seconds`.
- `status` (enum, optional) — Only models with this status. Default `active`.
  - Allowed values: `active`, `deprecated`
- `limit` (integer, optional, default: 20) — Maximum number of objects to return, from 1 to 100.
- `cursor` (string, optional) — The `next_cursor` from the previous page.

## Response

### 200

A page of catalog models.

- `object` ("list", required)
- `data` (list of CatalogModel, required)
- `has_more` (boolean, required)
- `next_cursor` (string, optional)

## 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.

### 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

### CatalogModel

A provider model in the Runledger-maintained price catalog. Read-only. The same catalog serves the sandbox and live.

- `id` (string, required)
- `object` ("catalog_model", required)
- `provider` (string, required) — The provider key.
- `model` (string, required) — The model ID as the provider names it, as `trace()` sends it.
- `name` (string, required)
- `status` (enum, required) — `active`: priced and kept current. `deprecated`: the provider has retired or replaced it. Existing cost models keep working with the last price version, and no new price versions are published.
  - Allowed values: `active`, `deprecated`
- `units` (list of CostUnitDeclaration, required)
- `dimensions` (list of CostDimension, required)
- `last_verified_at` (datetime, required) — When Runledger last checked this model's prices against their source.
- `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`
- `current_price` (string, optional) — The price version in force now (`catp_...`). Left out until one is.
- `current_price_version` (integer, optional)
- `price` (CatalogPrice, optional) — The price version in force, expanded. Only on retrieve.
- `updated_at` (datetime, optional)

### 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.

### 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)

### CatalogPrice

One version of a catalog model's rates. A published version never changes: a price change, or a correction, is a new version with its own `effective_from`. A corrected price takes effect from the date it is published, not from an earlier date.

- `id` (string, required)
- `object` ("catalog_price", required)
- `catalog_model` (string, required) — The catalog model ID.
- `version` (integer, required) — 1 for the first version of a catalog model's prices, then one more for each change.
- `status` (enum, required) — `scheduled`: takes effect at `effective_from`. `active`: in force. `superseded`: replaced by a later version.
  - Allowed values: `scheduled`, `active`, `superseded`
- `currency` (string, required) — The currency the provider quotes. Runledger doesn't convert between currencies.
- `rates` (list of CostRate, required)
- `effective_from` (datetime, required) — When these rates start to apply. It is never earlier than the day the version is published, and that includes a correction. Costs dated before it keep the earlier version, and recorded costs are never repriced.
- `as_of` (date, required) — The date Runledger read these rates from the source.
- `source_url` (string, required) — Where the rates were read: the provider's pricing page or its change notice.
- `published_at` (datetime, required) — When Runledger published this version, which can be before it takes effect. The freshness target is to publish a provider's price change within 1 business day of the provider publishing it. A business day is Monday to Friday in Sydney time, with no public holiday calendar.
- `last_verified_at` (datetime, required) — When Runledger last checked this version against its source.
- `effective_to` (datetime, optional) — When a later version replaced this one. Left out while it is in force.
- `source_reference` (string, optional) — A note on the source, such as the page title or the notice date.
- `changes` (list of ConfigChange, optional) — What differs from the version before, rate by rate.

### 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)

### ConfigChange

One field that changes, with its value before and after. A removed object or field has `before` and no `after`: a replace (`PUT /v1/config`) lists each removal this way, before anything is applied.

- `path` (string, required)
- `before` (any, optional) — The current value. Left out when the field is new.
- `after` (any, optional) — The new value. Left out when the field is removed.

### 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

**Response**

```json
{
  "object": "string",
  "data": [
    {
      "id": "catm_5xT7qD2mKa",
      "object": "string",
      "provider": "vidlab",
      "model": "vidlab-v2",
      "name": "Vidlab v2 video",
      "status": "active",
      "units": [
        {
          "unit": "video_seconds",
          "controlled_by": "customer",
          "description": "string"
        }
      ],
      "dimensions": [
        {
          "key": "resolution",
          "controlled_by": "customer",
          "values": [
            "720p",
            "1080p"
          ],
          "description": "string"
        }
      ],
      "last_verified_at": "2024-01-15T09:30:00Z",
      "kind": "text",
      "current_price": "string",
      "current_price_version": 1,
      "price": {
        "id": "catp_9cW4hL6rBv",
        "object": "string",
        "catalog_model": "catm_5xT7qD2mKa",
        "version": 1,
        "status": "scheduled",
        "currency": "usd",
        "rates": [
          {
            "unit": "video_seconds",
            "per": "1",
            "unit_amount_decimal": "12",
            "dimensions": {
              "resolution": "1080p"
            },
            "tier_mode": "graduated",
            "tiers": [
              {
                "unit_amount_decimal": "300",
                "up_to": "200000"
              }
            ]
          }
        ],
        "effective_from": "2024-01-15T09:30:00Z",
        "as_of": "2026-10-20",
        "source_url": "https://vidlab.example/pricing",
        "published_at": "2024-01-15T09:30:00Z",
        "last_verified_at": "2024-01-15T09:30:00Z",
        "effective_to": "2024-01-15T09:30:00Z",
        "source_reference": "Vidlab pricing page, read 20 October 2026",
        "changes": [
          {
            "path": "outcome_definitions.chat_resolution.confirmation_window",
            "before": null,
            "after": null
          }
        ]
      },
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "string"
}
```

**SDK Code**

```python
import requests

url = "https://api.runledger.co/v1/catalog/models"

headers = {"Authorization": "Bearer <api_key>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.runledger.co/v1/catalog/models';
const options = {method: 'GET', headers: {Authorization: 'Bearer <api_key>'}};

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"
	"net/http"
	"io"
)

func main() {

	url := "https://api.runledger.co/v1/catalog/models"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <api_key>")

	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/catalog/models")

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

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <api_key>'

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.get("https://api.runledger.co/v1/catalog/models")
  .header("Authorization", "Bearer <api_key>")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.runledger.co/v1/catalog/models', [
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.runledger.co/v1/catalog/models");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <api_key>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <api_key>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runledger.co/v1/catalog/models")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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()
```