Get started / Errors and retries
Errors and retries.
Every error has a status, a machine-readable type and a request_id. Your agent can decide what to do from the type alone.
Error shape
{
"error": {
"type": "window_full",
"message": "Search window full. 0 of 7,500 records left.",
"meter": "search_records_5h", "resets_at": "2026-11-08T15:40:00Z", "charged": 0
},
"request_id": "req_8f21c0"
} Error types
| Status | Type | What to do | Retry? |
|---|
400 | invalid_request | Fix the request. Don't retry as is. | Don't retry |
401 | unauthorized | Check the key. Stop the run. | Don't retry |
402 | free_records_exhausted | Free records are gone. Ask the user to upgrade. | Don't retry |
403 | forbidden | The key's access level doesn't allow this tool. | Don't retry |
404 | not_found | Nothing to enrich for that input. Move on. | Don't retry |
409 | idempotency_conflict | Same Idempotency-Key, different body. | Don't retry |
429 | window_full | Wait for Retry-After seconds, then retry. | Retry |
429 | weekly_limit | Weekly records limit reached. Resume after resets_at, or use an async job. | Later |
429 | too_many_concurrent | Wait for a running search to finish, then retry. | Retry |
500 | internal_error | Retry with backoff, same Idempotency-Key. | Retry |
503 | unavailable | Retry with backoff. Check status.leadsets.ai. | Retry |
| Header | Meaning |
|---|
| X-Leadsets-Records-Remaining | Records left in this tool's five-hour window |
| X-Leadsets-Requests-Remaining | Requests left in this tool's five-hour window |
| X-Leadsets-Weekly-Remaining | Records left in this tool's weekly limit |
| X-Leadsets-Free-Remaining | Free plan only: free records left |
| Retry-After | On 429: seconds until the window resets |
Retrying safely
Send an Idempotency-Key on every paid call. A retry with the same key returns the first result and never counts the records twice. Keys last 24 hours.
const res = await ls.people.search(query, { idempotencyKey });
// The SDK retries 429 and 5xx for you, honouring Retry-After.
// Doing it yourself:
if (err.status === 429 && err.type === "window_full") {
await sleep(err.retryAfter * 1000);
return retry(query, { idempotencyKey }); // never double-charges
} Records only when data comes back. Empty results and errors use no records. Every request counts toward the request window, hits and misses alike.