Rate limits
What happens when you exceed the rate limit and how to back off correctly.
The API limits how many requests you can make in a given window. When you exceed it, requests are rejected with 429 until the window resets.
When you're rate limited
A rate-limited request returns 429 Too Many Requests. Unlike other errors, the body is not the standard error envelope; it contains a single message field:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json{
"message": "<error message>"
}Exceeding the per-second rate and exceeding your daily quota both return this status and body shape. The message text is human-readable and may change, so branch on the 429 status code instead. Only 429 indicates rate limiting — 5xx responses are server errors, handled separately under Recommended backoff.
Limits
Limits apply per API key, based on the plan assigned to your key. Rate is the sustained number of requests per second; burst is the maximum number of requests accepted at the same instant.
| Plan | Rate (requests/second) | Burst |
|---|---|---|
| Basic | 10 | 5 |
| Pro | 20 | 10 |
| Enterprise | 50 | 15 |
Quota plans also cap the total number of requests per calendar day. The quota resets daily at 00:00 UTC:
| Plan | Rate (requests/second) | Burst | Daily quota |
|---|---|---|---|
| Basic (quota) | 10 | 1 | 10,000 |
| Pro (quota) | 10 | 1 | 25,000 |
| Enterprise (quota) | 10 | 1 | 100,000 |
Recommended backoff
Handle 429 (and retryable 5xx) responses as follows:
- Exponential backoff with jitter. Double the wait each attempt and add a small random offset so retries don't cluster.
- Cap retries. Give up after a few attempts and surface the error rather than retrying forever.
async function withRetry(request: () => Promise<Response>, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await request();
if (res.status !== 429 && res.status < 500) return res;
const backoff = 2 ** attempt * 500 + Math.random() * 250; // exponential + jitter
await new Promise((r) => setTimeout(r, backoff));
}
throw new Error("Exhausted retries");
}If your key is on a quota plan and you keep getting 429 after backing off, you've likely used your daily quota. Retrying won't help; requests will succeed again once the quota resets at 00:00 UTC.
Avoiding rate limits
- Batch where you can. Submit multiple credentials in one
POST /v2/credentialscall instead of one request per credential, but stay within the batch size limits or you'll getinvalid_batch_size/max_batch_size_exceeded(see Errors). - Prefer webhooks over polling. Register a webhook so Propelus notifies you when work completes, instead of repeatedly polling for results.
- Space out pagination. When iterating large result sets, add a short delay between pages (see Pagination).
Pagination
How list endpoints page their results and how to iterate through every page.
Verify Credentials Batch POST
Perform one-time verification checks on healthcare licenses, certifications, and registrations. Looking to enroll a credential in ongoing monitoring? See [here](/monitoring/CreateProfessionalMonitoring). Review the request and response bodies below to perform primary source verifications. An important note, submitted profession codes must match those in the Propelus system. To view those codes, utilize the [Get Profession Status](/professions/getProfessions) endpoint.