For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
my.pivotal.appGet an API key
Get StartedGuidesAPI ReferenceSDKsChangelog
Get StartedGuidesAPI ReferenceSDKsChangelog
  • Welcome
    • Introduction
    • Quickstart
    • Authentication
  • Concepts
    • Rate limits
    • Errors
    • Pagination
    • Versioning
    • Idempotency
LogoLogo
my.pivotal.appGet an API key
On this page
  • Response headers
  • 429 responses
  • Handling 429 in client code
  • Bulk operations
  • What does NOT count
  • What DOES count
Concepts

Rate limits

A per-key sliding window. Watch the response headers.

|View as Markdown|Open in Claude|
Was this page helpful?
Edit this page
Previous

Authentication

Next

Errors

Built with

Pivotal applies a sliding-window rate limit per API key. The default is 60 requests per minute per live key. Test keys get 30/min. Workspaces on a paid plan get 600/min per key — open a ticket if you need more.

There is no global per-workspace cap on top of the per-key limit; create more keys (one per service is the usual pattern) if a single key is the bottleneck.

Response headers

Every API response — whether it succeeded, errored, or got throttled — carries the current bucket state:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window
X-RateLimit-RemainingRequests left in this window
X-RateLimit-ResetUnix epoch seconds when the window resets
Retry-After(429 only) seconds the client should wait before retrying

Example:

1HTTP/1.1 200 OK
2X-RateLimit-Limit: 60
3X-RateLimit-Remaining: 47
4X-RateLimit-Reset: 1748275869
5Content-Type: application/json

429 responses

When the bucket is empty, Pivotal returns 429 Too Many Requests with the standard error envelope:

1{
2 "error": {
3 "type": "rate_limit_error",
4 "code": "rate_limited",
5 "message": "Rate limit exceeded. Retry after 12 seconds."
6 }
7}

Retry-After tells you exactly how long to wait. Don’t parse the message — use the header.

Handling 429 in client code

Exponential backoff with jitter is the right shape. Pseudocode:

1async function call(req: Request, attempt = 0): Promise<Response> {
2 const res = await fetch(req);
3 if (res.status !== 429 || attempt >= 5) return res;
4
5 const retryAfter = Number(res.headers.get("Retry-After") ?? 1);
6 const jitter = Math.random() * 0.5; // 0–500 ms
7 await sleep((retryAfter + jitter) * 1000);
8 return call(req, attempt + 1);
9}

Two refinements worth adding:

  1. Honor X-RateLimit-Remaining — if it’s at 1 or 2, slow yourself down before you hit zero.
  2. Cap the retry depth — five tries is enough; beyond that something else is wrong and the call should bubble up.

Bulk operations

The API does not yet expose bulk endpoints. If you’re moving thousands of records, see Bulk operations for the recommended concurrency settings — running 4–8 in-flight requests against a 60/min bucket plus respecting X-RateLimit-Remaining is usually enough.

What does NOT count

  • 401 from a missing or revoked key — rejected before the rate limiter sees it.
  • The token-refresh dance for any SDK — there is no token refresh; Bearer is the whole story.

What DOES count

  • Successful 2xx responses.
  • 4xx responses (validation, not found, conflict) — they still spent a request slot.
  • 5xx responses — same. The rate limiter doesn’t know the request “shouldn’t” have failed.

This means a buggy client looping on a 404 will burn its quota fast. Add a circuit breaker around any code that fails twice in a row against the same resource.