Menu

Concepts

Rate limits

Limits per class, daily and hourly caps, concurrency, RateLimit headers, 429 versus 503 and backoff.

Limits keep one runaway script from hurting you or anyone else. Every request must fit its key's budget and its organization's budget for its class of operation; a few operations also count against a daily or hourly cap. The numbers are the same on every plan.

Per minute, by class

Each operation belongs to one class (the reference names it). Budgets are per rolling minute and shared by every server, so spreading requests across connections does not raise them. The burst column is how many requests a key can send back to back before the per second pace applies.

ClassCoversPer keyPer organizationBurst per key
readGET lists and reads6001,20020
writesingle resource POST, PATCH, PUT and DELETE, and the lead upsert6001,20020
heavyimports, exports, bulk previews and confirmations, enrolling by filter, A/B analytics, spintax proposals, job result downloads20303
sendoperations that send a message to a lead601205
discovery_quotediscovery quotes30305
discovery_spendstarting discovery runs, resolving handles, promoting candidates10203

Most requests cost one unit, whatever their filters or batch size: a lead list with search costs 1 and a batch upsert costs 1 per request. Analytics reads cost 5. The table at the end lists every operation's class and cost.

Daily and hourly caps

WhatCapRefusal
Leads created through the API100,000 per organization per dayLEADS_DAILY_LIMIT_429
Job payload (imports and exports)100 MiB per organization per dayJOB_PAYLOAD_QUOTA_413
Discovery quotes1,000 per organization per dayDISCOVERY_QUOTE_DAILY_LIMIT_429
Messages the API sends into one conversation10 per hourCONVERSATION_RATE_LIMITED_429

These caps count a rolling window: 24 hours, or one hour for messages into one conversation. They come on top of your plan's own limits (DM caps, discovery tokens, custom fields, import size), which the API applies exactly as the app does.

Concurrency

Besides the rate, a key and an organization may have only a few requests in flight at once on each server:

LimitRequests
Requests in flight per key2
Requests in flight per organization2
heavy requests in flight2
send requests in flight2

A request over these answers 503 with Retry-After. Send requests in sequence or with low parallelism; there is nothing to gain from more.

Headers

Every response describes the most constrained budget that applied:

http
RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 41
RateLimit-Policy: "key-write";q=600;w=60
  • RateLimit-Limit: the budget of that bucket.
  • RateLimit-Remaining: what is left in it.
  • RateLimit-Reset: seconds until its window ends.
  • RateLimit-Policy: which bucket it is, its quota q and its window w in seconds.
  • Retry-After: on every 429 and 503, the seconds to wait.

429 or 503

StatusMeansDo
429 API_RATE_LIMITED_429Your key or organization used its budget.Slow down. Wait Retry-After, then continue at a lower pace.
503 API_OVERLOADED_503Our capacity: the fleet is busy or your requests are too parallel.Wait Retry-After with jitter and retry. Lower your concurrency.

Backing off

Respect Retry-After first. When it is absent, back off exponentially with full jitter:

js
async function callWithRetry(request, attempts = 5) {
  for (let i = 0; ; i++) {
    const res = await request();
    if (res.status !== 429 && res.status !== 503) return res;
    if (i === attempts - 1) return res;
    const retryAfter = Number(res.headers.get("retry-after"));
    const base = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** i;
    await new Promise((r) => setTimeout(r, base + Math.random() * base));
  }
}

Reuse the same Idempotency-Key on every retry of a write (see Idempotency).

Fair share

When the whole platform is busy, each active organization's per minute budget shrinks to an equal share of the remaining capacity, so a single heavy user cannot crowd out the others. A refusal for this reason is a 429 whose RateLimit-Policy names fair-share; it lifts as soon as the load drops.

The five original endpoints

GET /v1/me, GET /v1/campaigns, POST /v1/leads, GET /v1/leads/lookup and POST /v1/campaign-leads, the endpoints the Clay integration has always used, keep the limits they have always had. A request to them is never refused for concurrency or for platform load, and none of the class budgets above applies. A row-by-row Clay table that worked before keeps working.

LimitPaceBurst
Per key10 per second20
Per workspace20 per second20

Over these, the answer is the same 429 these endpoints have always sent, with Retry-After.

Requests without a valid key

LimitValue
Requests without a valid key, per IP address120 per minute
Failed authentications per IP address before a block20 per 10 minutes, then blocked for 15 minutes

These limits apply only to traffic that does not authenticate. A request with a valid key is never throttled or blocked because of its IP address, so shared egress addresses (Clay, Zapier, Make, n8n, hosted agents) keep working whatever other tenants on them do.

Every operation

OperationClassCostAlso counts against
analytics.funnelread5
analytics.overviewread5
analytics.setterread5
campaigns.getread1
campaigns.listoriginal endpoint1
campaigns.statsread5
conversations.drafts.listread1
conversations.getread1
conversations.listread1
custom_fields.listread1
discovery.balanceread1
discovery.candidates.listread1
discovery.icp_profiles.listread1
discovery.modesread1
discovery.runs.getread1
discovery.runs.listread1
enrollments.getread1
enrollments.listread1
folders.listread1
jobs.getread1
jobs.listread1
leads.enrollments.listread1
leads.getread1
leads.listread1
leads.lookuporiginal endpoint1
me.getoriginal endpoint1
reps.excluded.listread1
reps.getread1
reps.listread1
reps.setter_mode.getread1
setter.connections.getread1
setter.knowledge.listread1
setter.persona.getread1
setter.variants.getread1
setter.variants.listread1
setter.variants.versions.listread1
suppressions.listread1
tags.listread1
templates.getread1
templates.listread1
templates.statsread5
templates.validateread1
templates.versions.listread1
webhooks.deliveries.listread1
webhooks.endpoints.getread1
webhooks.endpoints.listread1
webhooks.events.listread1
campaign_leads.upsert_enrolloriginal endpoint1leads_created_day
campaigns.archivewrite1
campaigns.clonewrite1
campaigns.createwrite1
campaigns.deletewrite1
campaigns.pausewrite1
campaigns.resumewrite1
campaigns.startwrite1
campaigns.steps.replacewrite1
campaigns.updatewrite1
conversations.archivewrite1
conversations.drafts.dismisswrite1
conversations.mark_readwrite1
conversations.setter.handwrite1
conversations.setter.resumewrite1
conversations.starwrite1
conversations.unarchivewrite1
conversations.unstarwrite1
custom_fields.createwrite1
custom_fields.deletewrite1
custom_fields.updatewrite1
discovery.recipes.createwrite1
discovery.runs.cancelwrite1
discovery.runs.preview_decisionwrite1
enrollments.createwrite1
enrollments.pausewrite1
enrollments.removewrite1
enrollments.resumewrite1
folders.createwrite1
folders.deletewrite1
folders.updatewrite1
jobs.cancelwrite1
leads.deletewrite1
leads.tags.addwrite1
leads.tags.removewrite1
leads.updatewrite1
leads.upsertoriginal endpoint1leads_created_day
reps.excluded.addwrite1
reps.excluded.removewrite1
reps.setter_mode.setwrite1
setter.knowledge.createwrite1
setter.knowledge.deletewrite1
setter.knowledge.updatewrite1
setter.persona.setwrite1
setter.variants.archivewrite1
setter.variants.createwrite1
setter.variants.promotewrite1
setter.variants.publishwrite1
setter.variants.start_testwrite1
setter.variants.updatewrite1
suppressions.addwrite1leads_created_day
suppressions.removewrite1
templates.createwrite1
templates.deletewrite1
templates.updatewrite1
webhooks.deliveries.redeliverwrite1
webhooks.endpoints.createwrite1
webhooks.endpoints.deletewrite1
webhooks.endpoints.rotate_secretwrite1
webhooks.endpoints.testwrite1
webhooks.endpoints.updatewrite1
analytics.ab_comparisonheavy1
jobs.resultheavy1
leads.bulk.confirmheavy1
leads.bulk.previewheavy1payload_day
leads.exports.createheavy1payload_day
leads.imports.createheavy1leads_created_day, payload_day
setter.booking.previewheavy1
setter.testheavy1
templates.spintax.proposeheavy1
conversations.drafts.approvesend1conversation_send
discovery.quotediscovery_quote1dq_day
discovery.candidates.promotediscovery_spend1leads_created_day
discovery.handles.resolvediscovery_spend1
discovery.runs.creatediscovery_spend1dq_day
Note: The numbers above are the defaults. They are platform wide and change only with an entry in the changelog.
Rate limits | DM Hero Developers