API Idempotency Keys
Also Known As
idempotency key
Idempotency-Key header
duplicate request prevention
TL;DR
A client-generated unique key sent with non-idempotent requests — the server stores the response and returns it unchanged if the same key is received again, preventing duplicate operations.
Explanation
POST requests are not idempotent — retrying a failed payment POST could charge twice. Idempotency keys solve this: client generates a UUID per operation, sends it as Idempotency-Key header, server stores (key → response) and on retry returns the cached response without re-executing. Keys typically expire after 24 hours. Implementation: check key in Redis before processing, store result atomically with key, return stored result on duplicate. Stripe, PayPal, and all major payment APIs implement this pattern.
Diagram
sequenceDiagram
participant C as Client
participant S as Server
participant R as Redis
C->>S: POST /payments<br/>Idempotency-Key: uuid-123
S->>R: GET idempotency:uuid-123
R-->>S: null - not seen before
S->>S: Process payment
S->>R: SET idempotency:uuid-123 = response
S-->>C: 200 OK - charged
Note over C,S: Network timeout - client retries
C->>S: POST /payments<br/>Idempotency-Key: uuid-123
S->>R: GET idempotency:uuid-123
R-->>S: cached response
S-->>C: 200 OK - same response, not charged again
Watch Out
⚠ If two requests with the same idempotency key carry different payloads, you must return an error — silently accepting the second request with different data breaks the contract and causes subtle bugs.
Common Misconception
✗ Retrying with the same payload is safe because the server checks for duplicates — without idempotency keys the server has no way to know if a request is a retry or a new operation with identical data.
Why It Matters
A network timeout on a payment POST — did it succeed or not? Without idempotency keys, retrying risks charging twice; with them, the retry is safe and returns the original result.
Common Mistakes
- Using request body hash as idempotency key — two different users making the same purchase would share a key.
- Not expiring idempotency keys — storing responses forever wastes storage.
- Not making the key storage and operation atomic — a race condition between checking and storing creates duplicates.
- Idempotency keys on GET requests — GET is already idempotent; keys are only needed for state-changing operations.
Avoid When
- Do not use idempotency keys as a substitute for proper transaction handling — the key prevents duplicate requests, not concurrent ones.
- Avoid short key expiry windows for payment flows where network timeouts can cause retries hours later.
When To Use
- Use idempotency keys for any non-idempotent POST operation that could be safely retried: payments, order creation, message sending.
- Implement server-side key storage for the duration clients may reasonably retry (typically 24 hours to 7 days).
- Return the cached response immediately when a duplicate key is received — do not re-execute the operation.
Code Examples
💡 Note
The bad example processes every POST independently; a network timeout causes the client to retry and the user is charged twice — the fix deduplicates on the server using the client-supplied key.
✗ Vulnerable
// No idempotency — double-charge on retry:
POST /api/payments
{ "amount": 1000, "card": "tok_visa" }
// Network timeout after 5s — did it succeed?
// Retry:
POST /api/payments
{ "amount": 1000, "card": "tok_visa" }
// Result: charged twice
✓ Fixed
// Idempotency key — safe retry:
POST /api/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{ "amount": 1000, "card": "tok_visa" }
// Network timeout...
// Retry with SAME key:
POST /api/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
// Server: key exists, return cached 200 response
// Result: charged once, client has the receipt
// PHP server implementation:
$cached = $redis->get('idempotency:' . $key);
if ($cached) return json_decode($cached); // Return stored response
$result = $paymentGateway->charge($amount, $card);
$redis->setex('idempotency:' . $key, 86400, json_encode($result));
return $result;
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
16 Mar 2026
Edited
5 Apr 2026
Views
28
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Amazonbot 8
Perplexity 6
Ahrefs 2
Unknown AI 2
SEMrush 2
Google 1
ChatGPT 1
Also referenced
How they use it
crawler 22
Related categories
⚡
DEV INTEL
Tools & Severity
🟠 High
⚙ Fix effort: Medium
⚡ Quick Fix
Accept an Idempotency-Key header on POST endpoints — store the key and response; return the cached response for duplicate requests with the same key within a 24-hour window
📦 Applies To
PHP 7.0+
any
web
api
🔗 Prerequisites
🔍 Detection Hints
POST endpoint for payment or order creation without idempotency key support; client retries causing duplicate charges
Auto-detectable:
✗ No
semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Function
Tests: Update