Idempotency
Send an Idempotency-Key with every verification so network retries return the original result and are never billed twice.
Networks fail. A request can time out after Speedmap has already run the check, and a naive retry would run (and bill) it again. An idempotency key makes retries safe: Speedmap remembers the first result for that key and returns it again.
Add the header to every POST to a product endpoint:
Idempotency-Key: 4f9d3c1e-8a2b-4c7d-9e0f-1a2b3c4d5e6fcurl -X POST http://localhost:4000/v1/verify/account \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"account_number": "0123456789",
"bank_code": "058",
"match": {
"name": "Clarke Griffin"
}
}'Rules
- Format. Any string of 8 to 128 characters. A UUID v4 is ideal. Anything else returns
400 invalid_idempotency_key. - Scope. Keys are scoped to your workspace and mode: the same key in sandbox and live refers to two different requests.
- Same key, same body. You get the original verification back, with the same
reference_id, and you are not charged again. If the original is still pending you get202and the pending verification. - Same key, different body. Rejected with
409 idempotency_key_reused. Generate a new key for every new check. - Failed attempts release the key. If the first attempt failed (for example
503 verification_unavailableduring a provider outage), the key is released and the retry runs the check again. A failed attempt is never billed. - Where it applies. Product endpoints (
POST /v1/verify/*andPOST /v1/signals). Bulk jobs assign a key to every row for you.
409 Conflict
{
"error": {
"code": "idempotency_key_reused",
"message": "Idempotency-Key was already used with a different request"
}
}A retry loop
Generate the key once per logical check, outside the retry loop, and reuse it on every attempt:
async function verifyWithRetry(path, body, attempts = 4) {
const key = crypto.randomUUID(); // one key for every attempt
for (let i = 0; i < attempts; i++) {
const res = await fetch(`${process.env.SPEEDMAP_API_URL}${path}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SPEEDMAP_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify(body),
}).catch(() => null); // network error: retry
if (res && res.status < 500 && res.status !== 429) return res.json();
const wait = Number(res?.headers.get("Retry-After")) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
}
throw new Error("Speedmap unavailable after retries");
}Questions? Email developers@speedmap.ng. This page is also available as Markdown.