Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.clarky.ai/llms.txt

Use this file to discover all available pages before exploring further.

Every API key has its own request budget, applied independently per key. Limits are split between read and write operations to keep heavy reads from starving writes (and vice versa).

Limits

OperationMethodsLimit
ReadsGET, HEAD120 requests/minute per key
WritesPOST, PATCH, DELETE60 requests/minute per key
Limits are enforced on a rolling 60-second window. When you hit the limit, subsequent requests return 429 Too Many Requests until the window resets.

Rate limit headers

Every response — successful or rate-limited — includes headers that describe your current budget:
X-RateLimit-Limit
integer
Maximum requests allowed in the current window for this method class (read or write).
X-RateLimit-Remaining
integer
Requests remaining in the current window.
X-RateLimit-Reset
integer
Unix timestamp (seconds since epoch) when the current window resets.
Example response headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1735689600

What a 429 looks like

When you exceed the limit, you’ll get a 429 with a standard error envelope and a Retry-After header (in seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735689600
Content-Type: application/json

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 30 seconds."
  }
}
1

Respect Retry-After

On a 429, wait at least the number of seconds in the Retry-After header before retrying. This is the simplest correct strategy.
2

Use exponential backoff with jitter

For longer-running jobs, layer exponential backoff on top of Retry-After. Add random jitter so a fleet of workers doesn’t synchronize and thunder back together.
3

Throttle proactively

Watch X-RateLimit-Remaining. If it’s getting close to zero, slow down before you hit the limit — it’s much cheaper than recovering from a 429.
4

Parallelize across keys for very large jobs

If you genuinely need throughput beyond the per-key limit, create multiple keys (one per worker) and shard your work across them. Limits are per-key, so independent keys give you linear scaling. Reach out to support if you need a higher per-key limit.

Example: handling 429 in Node.js

async function fetchWithBackoff(url, options, attempt = 0) {
  const res = await fetch(url, options);

  if (res.status === 429) {
    const retryAfter = parseInt(res.headers.get("Retry-After") ?? "5", 10);
    const jitter = Math.random() * 500;
    const delayMs = retryAfter * 1000 + jitter;

    if (attempt >= 5) throw new Error("Too many retries");

    await new Promise((r) => setTimeout(r, delayMs));
    return fetchWithBackoff(url, options, attempt + 1);
  }

  return res;
}
Don’t retry every error blindly — only retry 429 and 5xx responses. Retrying 4xx errors (other than 429) wastes budget and won’t change the outcome.
For bulk loads, the POST /contacts/upsert endpoint accepts up to 200 contacts in a single request. Prefer it over a loop of individual POST /contacts calls — one upsert costs one write against your budget.