---
title: "Create a limit policy"
method: POST
path: "/api/limit-policies/"
tags: ["Limit Policies"]
---

# Create a limit policy

`POST /api/limit-policies/`

## Request

- Base URL: `https://api.respan.ai`
- URL: `https://api.respan.ai/api/limit-policies/`
- Auth: HTTP bearer

Create a usage cap. The policy takes effect immediately: alert rules notify, and block rules reject gateway requests with `429` once the cap is reached.

## Request body

- LimitPolicyCreateRequest
  - `name` string, required
  - `scope` string: Display category. Not used for matching.
  - `scope_value` string, required: Identity to meter. For composites (`compose` length >= 2), a JSON list of values in `compose` order.
  - `compose` string[]
  - `metric` 'cost' | 'request_count' | 'token_count', required: What the cap counts. `cost` is US dollars, `token_count` is total tokens, `request_count` is calls.
  - `algorithm` 'balance_fixed_window'
  - `period` string, nullable: Named bucket, integer-seconds string, or `null` for cumulative.
  - `anchor` 'calendar' | 'first_event' | 'explicit_ts'
  - `anchor_at` string, date-time, nullable
  - `effective_at` string, date-time, nullable
  - `expires_at` string, date-time, nullable
  - `priority` integer
  - `is_active` boolean
  - `rules` LimitPolicyRule[], required
    - `trigger` object, required: Threshold condition. Common shape: `{"counter": {"operator": "gte", "value": 10000}}`.
    - `severity` 'soft' | 'hard', required: `soft` fires an alert without blocking; `hard` blocks the request with 429.
    - `description` string, nullable: Shown in events and alert payloads.
    - `is_active` boolean
    - `notification_method_id` string, nullable: Optional OrganizationNotificationMethod id to ping when this rule trips.

## Response `201`

The created policy.

- LimitPolicy: A stored cap: watch a scope, measure a metric, refill on a cadence, and fire rules at thresholds.
  - `id` string, uuid
  - `name` string: Human label.
  - `scope` string: Display category (e.g. `API Key`, `Model`). Not used for matching.
  - `scope_value` string: Identity to meter. For composites, a JSON list of values in `compose` order.
  - `compose` string[]: Empty for single-dimension policies. Members: `organization_id`, `api_key_id`, `user_id`, `customer_identifier`, `model`, `endpoint`.
  - `metric` 'cost' | 'request_count' | 'token_count': What the cap counts. `cost` is US dollars, `token_count` is total tokens, `request_count` is calls.
  - `algorithm` 'balance_fixed_window': `balance_fixed_window` is the shipped algorithm. `token_bucket` is reserved and not yet enforced.
  - `period` string, nullable: Cadence. A named bucket (`minute`, `hour`, `day`, `week`, `month`), a positive integer of seconds as a string, or `null` for a cumulative (lifetime) cap that never resets.
  - `anchor` 'calendar' | 'first_event' | 'explicit_ts': When a recurring window aligns. `calendar` resets on the boundary (e.g. the 1st of the month).
  - `anchor_at` string, date-time, nullable: Required when `anchor` is not `calendar`.
  - `effective_at` string, date-time, nullable: Active-range start. `null` = live since forever.
  - `expires_at` string, date-time, nullable: Active-range end. `null` = live until deactivated. Must be after `effective_at`.
  - `priority` integer: Higher is checked first.
  - `is_active` boolean
  - `rules` LimitPolicyRule[]: Any number of soft rules and at most one effective hard rule.
    - `trigger` object, required: Threshold condition. Common shape: `{"counter": {"operator": "gte", "value": 10000}}`.
    - `severity` 'soft' | 'hard', required: `soft` fires an alert without blocking; `hard` blocks the request with 429.
    - `description` string, nullable: Shown in events and alert payloads.
    - `is_active` boolean
    - `notification_method_id` string, nullable: Optional OrganizationNotificationMethod id to ping when this rule trips.
  - `threshold_value` number: First hard-rule threshold; falls back to the largest soft threshold.
  - `meter_definition_id` string: Stable hash of the policy identity. Same identity resolves to the same id across recreations.
  - `current_state` LimitPolicyCurrentState: Live counter for the current window instance.
    - `meter_id` string
    - `interval_start` integer, nullable: Epoch seconds, inclusive.
    - `interval_end` integer, nullable: Epoch seconds, exclusive.
    - `current_value` number: Accumulated value in this window.
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Validation failed.
- `403`: The API key is missing, invalid or expired.

## Changes

- **2026-10-21** `c30bcb383ee8`: 11 breaking, 15 info
  - request body became required
  - the response property `anchor_at` became nullable for the status `201`
  - the response property `current_state/interval_end` became nullable for the status `201`
  - the response property `current_state/interval_start` became nullable for the status `201`
  - …22 more
- **2026-10-02** `b830b79cfd52`: 1 breaking, 3 info
  - the response property `rules/items/description` became nullable for the status `201`
  - the request property `rules/items/description` became nullable
  - added the non-success response with the status `400`
  - added the non-success response with the status `403`
- **2026-09-17** `6fa8242da8c2`: 1 warning, 1 info
  - deleted the `header` request parameter `Authorization`
  - the endpoint scheme security `RespanApiKey` was added to the API
- **2026-09-12** `22b868ef3f22`: 1 info
  - api operation id `create-limit-policy` removed and replaced with `limitPolicies_create_limit_policy`

[Operation history](https://skmtc.dev/respan/apis/respan-api/changes/api/limit-policies/post.md)

---

[API](https://skmtc.dev/respan/apis/respan-api.md) - [All operations](https://skmtc.dev/respan/apis/respan-api/llms.txt) - [OpenAPI document](https://skmtc.dev/respan/apis/respan-api/revisions/900841387635?raw)
