Rate Limits

Learn about API rate limits and how to work with them.

Ironclad CLM Limits

These API limits are set on a per-company basis. Each bucket below covers a set of HTTP methods and endpoint paths that share a single quota — for example, GET /public/api/v1/workflows and GET /public/api/v1/workflows/{id} both fall under the same 400-requests-per-minute quota (other than the sub-resource buckets listed separately).

Limits stated in requests per minute (RPM) unless otherwise stated.

Public API Buckets (base path /public/api/v1)

BucketLimit (RPM)MethodsPath (* as a wildcard)
pub:workflows:read400GET, HEAD/public/api/v1/workflows*
pub:workflows:write40POST, PUT, PATCH, DELETE/public/api/v1/workflows*
pub:workflows:sign-status50POST, PATCH/public/api/v1/workflows/{id}/sign-status*
pub:workflows:signatures50POST, PATCH/public/api/v1/workflows/{id}/signatures*
pub:records:read600GET, HEAD/public/api/v1/records*
pub:records:write200POST, PUT, PATCH, DELETE/public/api/v1/records*
pub:records:export10GET, HEAD/public/api/v1/records/export*
pub:entities:read600GET, HEAD/public/api/v1/entities*
pub:entities:write200POST, PUT, PATCH, DELETE/public/api/v1/entities*
pub:obligations:read600GET, HEAD/public/api/v1/obligations*
pub:obligations:write200POST, PUT, PATCH, DELETE/public/api/v1/obligations*
pub:exports:create20POST/public/api/v1/exports
pub:exports:download20GET, HEAD/public/api/v1/exports/{id}/download
pub:exports:status600GET, HEAD/public/api/v1/exports/{id}
pub:webhooks600ANY/public/api/v1/webhooks*
pub:attribute-source:write40POST, PUT, PATCH, DELETE/public/api/v1/attribute-source*
pub:signature-requests40ANY/public/api/v1/signature-requests*
pub:other800ANY/public/api/v1* (any other Public API endpoint)

SCIM API Buckets (base path /scim/v2)

BucketLimit (RPM)MethodsPath (* as a wildcard)
scim:users:read-write600GET, HEAD, PUT, POST/scim/v2/users*
scim:users:write50PATCH, DELETE/scim/v2/users*
scim:groups:read200GET, HEAD/scim/v2/groups*
scim:groups:write50POST, PUT, PATCH, DELETE/scim/v2/groups*
scim:other800ANY/scim/v2* (any other SCIM endpoint)

MCP Buckets (requests made via the Ironclad MCP server)

BucketLimit (RPM)MethodsPath (* as a wildcard)
mcp:other80ANY/mcp*

In addition to the per-bucket limits above, Ironclad enforces a company-wide cap of 4,500 requests per minute across all API requests combined — including SCIM — regardless of bucket.

Rate limit response headers

Every response from an endpoint that enforces a rate limit includes the following headers:

HeaderDescription
X-RateLimit-LimitThe maximum number of requests allowed for the matched bucket in the current window.
X-RateLimit-RemainingThe number of requests you have left in the current window.
X-RateLimit-ResetThe time (as a Unix epoch timestamp, in seconds) when the current window resets.

If a request is rejected for exceeding the limit (429 Too Many Requests), the response also includes:

HeaderDescription
Retry-AfterThe number of seconds to wait before retrying the request.

Strategies for Handling API Rate Limits

If your integration is hitting rate limits (HTTP 429 responses) against Ironclad's Public API or SCIM API, here are four approaches. We'd recommend combining at least the first two, with caching and webhooks layered on top wherever applicable to your integration's use case.

1. Spread requests out over time

If your integration performs bulk operations (e.g., syncing many users, or scanning many workflows), avoid firing all requests as fast as possible in a tight burst. Add pacing/delay between calls so your request rate stays comfortably under the limit, and spread bulk jobs out over a longer window rather than compressing them into a minute or two. Keep in mind that rate limits are often shared across all integrations your organization runs against the same API, so leaving some headroom below the limit (rather than targeting it exactly) helps avoid contention with other traffic.

2. Handle 429 responses with retries and backoff

Even with good pacing, your integration should treat a 429 as an expected, recoverable condition — not a failure to surface to an end user or drop silently. In increasing order of robustness:

  • Simplest: On a 429, wait a fixed interval (e.g., 30-60 seconds) and retry.
  • Better: Check the response for a Retry-After header (or equivalent rate-limit header, if provided) and wait exactly that long before retrying, rather than guessing.
  • Most robust: Use exponential backoff — if a retry also gets a 429, wait progressively longer before the next attempt (with a reasonable maximum), rather than retrying at a fixed interval indefinitely.

Whichever approach you choose, make sure retries are actually implemented end-to-end — a request that fails with a 429 should still eventually succeed, not be lost.

3. Cache data that doesn't change often

Before re-fetching data, consider whether your integration already has a recent, still-valid copy of it. Reference data, configuration/schema information, and any records you've already retrieved and haven't been told have changed are good candidates for a local cache with a reasonable TTL. This avoids repeatedly requesting the same data on every run, cutting overall request volume without changing your integration's logic. When a webhook (see below) tells you something changed, invalidate the relevant cache entry rather than waiting for the TTL to expire, so you're not working from stale data.

4. Use webhooks instead of polling

The most effective way to stay well under rate limits is to avoid unnecessary requests in the first place. If your integration is polling an endpoint on a schedule to detect changes (e.g., "has anything changed since last time I checked?"), check whether Ironclad offers a webhook for that event instead. Webhooks push a notification to your integration only when something actually changes, so you can replace routine polling (and any "full re-scan" logic that re-fetches everything on a schedule) with targeted, on-demand requests triggered only by relevant events. See the full list of available webhooks here. This typically reduces request volume dramatically compared to polling, and eliminates the periodic bursts that come from scanning an entire dataset on a fixed schedule.

👍

Summary

Pace your requests, retry gracefully on 429s (ideally with backoff), cache what you don't need to re-fetch, and replace polling with webhooks wherever one is available for the data you need.

Other limits

Notwithstanding the limits specified in this document, the system might still limit requests if it detects an unusual spike in requests from the account, for example, in a denial of service attack. Please see our API Terms of Use for more information.