Rate limits and retries
- Per key
- 60 requests per minute
- Per IP
- 300 requests per minute
- Idempotency window
- 24 hours
- Client timeout
- At least 60 seconds
Rate limits
- 60 requests per minute per API key, shared across all four endpoints. A burst of
verify-emailcalls uses the same budget aswork-email. - 300 requests per minute per IP address, checked before the key. It only matters when many keys share one outbound IP.
Going over either limit returns 429 with the code rate_limited. Nothing is charged.
To stay under the limit on a large list, pace your client rather than firing everything at once — one request a second per key keeps you exactly at the ceiling. If you need more throughput, talk to us in the Slack community before spreading load over extra keys.
Idempotency
Network calls fail in the middle. Without protection, retrying a lookup that actually succeeded charges you twice. Send an Idempotency-Key header to make retries safe:
curl https://api.syncgtm.com/v1/api/enrich/mobile \
-H "Authorization: Bearer $SYNCGTM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-contact-48213-mobile" \
-d '{"linkedin_url": "https://www.linkedin.com/in/janedoe"}'The key is any string of 1–255 characters that you choose. Make it unique per logical lookup — a UUID, or something derived from your own record ID.
| What happens | Result |
|---|---|
| First request with a key | Runs normally and is charged normally |
| Same key, same body, after it succeeded (within 24 hours) | The first response is returned again. Not charged. |
| Same key, same body, while the first is still running | 409 idempotency_conflict — wait and retry |
| Same key, different body | 409 idempotency_conflict — a key belongs to one request |
| Same key after the first attempt failed | Runs again as a new request |
| Same key after 24 hours | Runs again as a new request |
Keys are scoped to your organization, so two organizations using the same string never collide. The header is optional; without it every request is independent.
Do not reuse one idempotency key across different people. A different body under a used key is rejected with 409, and the same body returns the cached answer — never a fresh lookup.
Timeouts
Waterfall lookups call external providers in sequence, so most answer in a few seconds but a slow one can take much longer. Set your HTTP client timeout to at least 60 seconds.
If a lookup runs past the server deadline you get 504 timeout and any credits held for it are refunded. Retry with the same Idempotency-Key.
A retry loop that does the right thing
async function enrich(path, body, idempotencyKey) {
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch(`https://api.syncgtm.com/v1/api/enrich/${path}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SYNCGTM_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(90_000),
});
if (res.ok) return res.json();
const { error } = await res.json();
const retryable = [409, 429, 500, 504].includes(res.status);
if (!retryable) throw new Error(`${res.status} ${error.code}: ${error.message}`);
// 1s, 2s, 4s, 8s, 16s
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
}
throw new Error("Gave up after 5 attempts");
}A 409 is only worth retrying when it says the request is still in progress. A 409 for a different body will fail the same way every time — that is a bug in how the key was chosen.