Rate limiting

Customer 360 applies a per-key, per-minute sliding-window rate limit. Each API key has its own limit (default 120 requests per minute), tracked independently, so one caller's traffic never throttles another. When a key exceeds its limit, the request returns 429 Too Many Requests with rate-limit headers.


The per-key limit

Every key carries a rateLimitPerMinute, set at mint time and defaulting to 120. Requests are counted in a sliding 60-second window per key. When the count reaches the limit, further requests in that window are rejected until older entries age out.

If a high-throughput integration needs a higher ceiling, ask the admin to mint the key with a larger limit. Batching helps too: a single POST /v1/events carries up to 500 events, so a producer rarely needs many requests per minute.

Quick reference

DetailValue
AlgorithmSliding window (60s)
ScopePer API key
Default limit120 requests / minute
Response code429 Too Many Requests
HeadersX-RateLimit-Limit, X-RateLimit-Remaining

The 429 response

When a key is over its limit, the service returns 429 with a detail message stating the limit, plus two response headers so a client can back off intelligently.

  • Name
    X-RateLimit-Limit
    Type
    header
    Description

    The key's configured per-minute limit.

  • Name
    X-RateLimit-Remaining
    Type
    header
    Description

    Requests remaining in the current window. 0 on a 429.

429 response body

{
  "detail": "Rate limit exceeded (120/min)"
}

429 response headers

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0

A best-effort limit

The limiter is in-memory per instance. Customer 360 runs on Cloud Run, which scales to multiple instances, so the effective global limit is roughly rateLimitPerMinute times the number of running instances, not a hard fleet-wide cap. It is best-effort abuse-dampening, not an exact global throttle.

In practice, design around the published limit but do not rely on it for precise global throttling. If you need a hard, exact limit, that is a later phase; for now the interim guarantee is per-instance.


Handling 429 responses

On a 429, back off and retry with increasing delays and a little jitter. Because ingest is idempotent on (source, eventId), a retry after a 429 can never double-store: if the first attempt did land, the retry comes back duplicate.

  1. First retry: wait 2 seconds
  2. Second retry: wait 4 seconds
  3. Third retry: wait 8 seconds
  4. Stop after a few attempts and surface the error

Add 0 to 500 ms of random jitter to each delay so many clients do not retry at the same instant.

Backoff on 429

import requests, time, random

def post_with_backoff(payload, headers, max_retries=4):
    delay = 2
    for _ in range(max_retries):
        r = requests.post(
            "https://c360-api.lioncapventures.com/v1/events",
            json=payload, headers=headers,
        )
        if r.status_code != 429:
            return r
        time.sleep(delay + random.uniform(0, 0.5))
        delay *= 2
    raise Exception("Still rate limited after retries")

Was this page helpful?