Concepts
Rate limits
Rate limits protect the API and your own spend. They are separate from credits: a limit says how much work can run at once, not whether you can afford it.
Default limits
These limits apply to every organization unless SeekOut or one of your organization admins has set a lower one for it. Every key and workspace in an organization shares them.
| Limit | Default | What is counted | When it is reached |
|---|---|---|---|
| Request rate | 25 requests per second | Every authenticated /v1 request from the organization | 429 rate_limited |
| Requests in flight | 25 at once | Requests that have started and not yet finished | 429 rate_limited |
| Running contact-retrieval jobs | 2 | Jobs that have been accepted and have not finished | 429 contact_job_concurrency_cap |
| Webhook replays | 10 per endpoint per hour | Replays of one endpoint's dead-lettered deliveries | 429 webhook_replay_cap |
The request rate is a strict one-second window with no burst allowance on top: at most 25 requests are admitted in any one second, and a request that is refused does not use up part of the window. Each key, key family and application is held to the same 25 per second inside the organization's total, so spreading calls across more keys does not raise the rate.
What a 429 means
429 is raised on admission, before useful work happens. Honor the Retry-After header and retry the same request. For unlock, reuse the same idempotency key — no credits were spent.
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 1
X-Request-Id: 00000000-0000-4000-8000-000000000000
Cache-Control: private, no-store
{
"type": "urn:seekout:developer-api:problem:rate_limited",
"title": "Rate limited",
"status": 429,
"code": "rate_limited",
"instance": "urn:seekout:developer-api:request:00000000-0000-4000-8000-000000000000",
"request_id": "00000000-0000-4000-8000-000000000000"
}- The body is an
application/problem+jsondocument withtype,title,status,code,instanceandrequest_id. Branch oncode, not on the title. This body has nodetailmember and no retry field: the wait is in the header. Retry-Afteris a whole number of seconds, never less than1. When the request rate is what refused you it is the time until the oldest request in the window ages out, so it is1.- When the in-flight limit refused you,
Retry-Afteris the time until the oldest in-flight request would expire on its own, which can be as long as five minutes. A slot is free again as soon as any request finishes, so a short back-off with a cap is also safe. X-Request-Idechoes the id you sent or the one SeekOut made; quote it to support.
Refusals specific to contacts and webhooks
Contact retrieval, batch job creation, the Jobs API reads and the cancel, and every webhook route each carry their own request rate limit, and two of them refuse for a reason worth handling separately.
| Code | What happened | What to do |
|---|---|---|
contact_job_concurrency_cap | Your organization already has two contact-retrieval jobs running. This is a concurrency cap, not a rate: waiting a moment is not enough if the running jobs are long. Retry-After is 30 | Wait for Retry-After, or for a running job to finish, then create the job again |
webhook_replay_cap | More than 10 replays of one endpoint's dead-lettered deliveries in the last hour | Fix the receiver first, then replay. Wait for Retry-After |
Neither is a charge and neither leaves state behind. A job that was refused at the cap never existed, so there is nothing to cancel and nothing to reconcile.
Search quotas
Search also limits bulk extraction with three further 429 codes: preview_quota_exceeded, distinct_query_cap and delivery_quota_exceeded. They count unique people or distinct queries rather than requests per second, and each response carries Retry-After: 1. If one persists after a retry, contact support with the X-Request-Id.
Read your limits
Limits in the console and GET /v1/operational-limits (scope limits.v1:read) report the limits that are enforced for your organization, for each operation and for each application, key family and key. An organization that has set no limit of its own is held to the defaults in the table above, so every entry reports those until a limit is set.
| Field | What it is |
|---|---|
requests_per_second | Requests admitted per second, in the strict one-second window. 25 unless a lower organization-wide rate has been set, which the api.request_baseline.v1 entry for the organization reports. |
requests_per_minute | An exact cap on requests in any trailing minute, or null when no per-minute cap applies. An organization that has set no limit has none, so this is null; only a custom limit makes it a number. |
max_concurrency | Requests in flight at once. 25 by default; a custom limit sets its own. |
lease_ttl_seconds | How long an in-flight request holds its slot if it is never released. A Retry-After on an in-flight refusal can be this long. |
status | active, or denied when SeekOut has blocked the operation. A denied entry reports no figures. |
source | override while a custom limit is in force: a per-minute cap, a block, or an organization-wide rate below 25 per second. inherited and cleared both mean the defaults apply. |
Each entry describes one operation on its own. Every request is also held to the api.request_baseline.v1 entry, so the limit a request meets is the stricter of its operation's entry and the baseline entry for the same organization, application, key family or key. An operation's entry can read 25 per second with no per-minute cap while the baseline entry holds a cap, a lower organization-wide rate or a block; read both.
Organization admins can lower a limit from the console by setting a per-minute cap on an operation, for one key, one key family, one application or the whole organization. A cap must be below standard_requests_per_minute, and it is the only thing that sets requests_per_minute. The standard, maximum_requests_per_minute, suggested_requests_per_minute and inherited_ceiling_ref describe the ceilings a cap is measured against. They are not limits in force. A cap can only be lowered here; raising the request rate or the in-flight limit is a conversation with SeekOut.
429 means wait; 402 means the work is not funded. Adding credits does not raise a rate limit, and lowering a rate limit does not change what an operation costs.