Idempotency
Safely retry failed requests without creating duplicates. Idempotency keys ensure exactly-once processing for your API calls.
How It Works
Include Key in Request
Add the Idempotency-Key header with a unique value for each logical operation.
First Request Processed
We process the request normally and store the response keyed by your idempotency key.
Retry Returns Cached
Subsequent requests with the same key return the original response without reprocessing.
// Include Idempotency-Key header for safe retries
const response = await fetch('https://api.justkalm.com/v2/valuate', {
method: 'POST',
headers: {
'Authorization': 'Bearer jk_live_xxx',
'Idempotency-Key': 'order_12345_valuation', // Your unique key
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/product',
})
});
// Safe to retry with same Idempotency-Key
// Will return cached response, not create duplicateGenerating Keys
Idempotency keys should be unique per logical operation. Use UUIDs or derive keys from your business logic.
Key Guidelines
Alphanumeric, dashes, underscores
Don't reuse keys across different requests
order_id, user_id, timestamp for debugging
import { v4 as uuidv4 } from 'uuid';
// Generate a unique key per logical operation
const idempotencyKey = uuidv4();
// Or derive from your business logic
const idempotencyKey = `order_${orderId}_item_${itemId}_${timestamp}`;
// SDK handles this automatically
const client = new JustKalm({ apiKey: 'jk_live_xxx' });
const valuation = await client.valuate({
url: 'https://example.com/product',
}, {
idempotencyKey: idempotencyKey, // Optional: SDK generates if not provided
});Retry Logic
async function valuateWithRetry(url: string, maxRetries = 3) {
const idempotencyKey = `val_${Date.now()}_${Math.random().toString(36)}`;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('https://api.justkalm.com/v2/valuate', {
method: 'POST',
headers: {
'Authorization': 'Bearer jk_live_xxx',
'Idempotency-Key': idempotencyKey, // Same key for all retries
'Content-Type': 'application/json',
},
body: JSON.stringify({ url }),
});
if (response.ok) {
return await response.json();
}
// Don't retry 4xx errors (except 429)
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
throw new Error(`Request failed: ${response.status}`);
}
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
}
}
}When to Retry
- • 429 Too Many Requests
- • 500 Internal Server Error
- • 502 Bad Gateway
- • 503 Service Unavailable
- • Network timeouts
- • Connection errors
- • 400 Bad Request
- • 401 Unauthorized
- • 403 Forbidden
- • 404 Not Found
- • 422 Unprocessable Entity
Response Headers
Every response includes headers to indicate idempotency status:
| Header | Value | Description |
|---|---|---|
| Idempotency-Key | your-key-here | Echo of your request key |
| X-Idempotent-Replayed | true | false | Whether this is a cached response |
| X-Request-Id | req_abc123 | Original request ID (for support) |
Best Practices
Use SDK Auto-Generation
Our SDKs automatically generate and manage idempotency keys. Override only when you need business-specific keys.
Store Keys Client-Side
Keep track of in-flight request keys. If your app crashes, you can resume with the same key.
Include Request Hash
For extra safety, include a hash of the request body in your key to detect accidental misuse.
Avoid Key Collisions
Never use sequential IDs alone. Include user ID, timestamp, or random suffix to ensure uniqueness.
Build Reliable Integrations
Combine idempotency with our error handling guide for bulletproof API calls.