Third-party data APIs (Apollo, Clearbit, Hunter, People Data Labs, ZoomInfo) enforce rate limits contractually. Ignoring 429 responses and retrying without backoff burns your monthly API quota in minutes, crashes enrichment pipelines, and may violate ToS. OWASP A05 (Security Misconfiguration) includes failure to honor upstream service constraints. Key cycling — rotating through multiple API keys to exceed per-account limits — is an explicit ToS violation that can result in account termination and, for GDPR-regulated data, loss of the legal basis for processing contacts sourced through that API.
High because key cycling to bypass rate limits violates vendor ToS and can invalidate the lawful basis for data collected through the API, while unhandled 429s crash enrichment pipelines and destroy data freshness.
Wrap all third-party data API calls in a retry wrapper that reads Retry-After headers and backs off exponentially. Remove any key-rotation arrays — one API key per provider, no exceptions.
// src/lib/api/retry.ts
export async function callDataApi<T>(
fn: () => Promise<T>,
retries = 3
): Promise<T> {
for (let attempt = 0; attempt < retries; attempt++) {
try {
return await fn()
} catch (err: any) {
if (err.status === 429) {
const wait = parseInt(err.headers?.['retry-after'] ?? '60', 10)
await sleep(wait * 1000)
continue
}
throw err
}
}
throw new Error('Max retries exceeded')
}
Search the codebase for arrays of API keys assigned to the same provider (APOLLO_API_KEYS, CLEARBIT_KEYS, etc.) and remove them entirely.
ID: data-sourcing-provenance.legal-sourcing.api-rate-limit-compliance
Severity: high
What to look for: Enumerate all third-party data API integrations (LinkedIn Sales Navigator, Apollo.io, Clearbit, Hunter, Snov, People Data Labs, ZoomInfo, etc.). Count every API client found. For each, check that the code: reads and respects Retry-After or X-RateLimit-Remaining headers from API responses, implements exponential backoff on 429 responses, and does not use multiple API keys to circumvent per-key rate limits (key cycling). API key cycling to bypass rate limits does not count as pass.
Pass criteria: Count all API clients and report the ratio: "N of N API clients handle 429 responses." 100% of API client code handles 429 responses gracefully (backoff, retry), reads rate limit headers, and does not implement key cycling. There is no code that rotates through an array of API keys to exceed the provider's per-account rate limit.
Fail criteria: API calls are made in tight loops without reading rate limit headers or handling 429 responses. Or there is evidence of API key cycling to circumvent provider rate limits (an array of API keys used round-robin for the same provider).
Skip (N/A) when: The system makes no calls to third-party data APIs.
Detail on fail: Example: "Apollo API client does not handle 429 responses — will crash on rate limit" or "Code found cycling through 3 LinkedIn API keys to circumvent rate limits".
Remediation: Implement proper rate limit handling:
async function callDataApi<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
for (let attempt = 0; attempt < retries; attempt++) {
try {
return await fn()
} catch (err) {
if (err.status === 429) {
const retryAfter = parseInt(err.headers?.['retry-after'] ?? '60', 10)
await sleep(retryAfter * 1000)
continue
}
throw err
}
}
throw new Error('Max retries exceeded')
}