How to Build an Idempotency-Key POST Handler in Python

Python HTTP server mock that accepts POST requests and deduplicates them using an Idempotency-Key header, returning the same response for repeated calls.

Medium Python 3.9+ Aug 9, 2026 API design & gRPC 14 views 0 copies

Python code

38 lines
Python 3.9+
import hashlib
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse


class MockAPI(BaseHTTPRequestHandler):
    responses = {}

    def do_POST(self):
        length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(length).decode("utf-8")
        idempotency_key = self.headers.get("Idempotency-Key")

        if not idempotency_key:
            self.send_error(400, "Missing Idempotency-Key header")
            return

        cache_key = (self.path, idempotency_key)
        if cache_key in self.responses:
            response_data = self.responses[cache_key]
            status = 200
        else:
            digest = hashlib.sha256(body.encode()).hexdigest()[:8]
            response_data = {"id": digest, "received": body, "cached": False}
            self.responses[cache_key] = response_data
            status = 201

        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps(response_data).encode())


if __name__ == "__main__":
    server = HTTPServer(("localhost", 8000), MockAPI)
    print("Mock API running on port 8000")
    server.serve_forever()

Output

stdout
Mock API running on port 8000

# First POST (no cache)
HTTP/1.0 201 Created
Content-Type: application/json

{"id": "a1b2c3d4", "received": "{\"name\": \"Ada\"}", "cached": false}

# Second POST with same key
HTTP/1.0 200 OK
Content-Type: application/json

{"id": "a1b2c3d4", "received": "{\"name\": \"Ada\"}", "cached": false}

How it works

The handler reads the Idempotency-Key header and uses it as a cache key along with the request path. On the first POST it computes an 8-character SHA-256 digest of the body, stores the response, and returns 201. On subsequent POSTs with the same key, it returns the stored response with status 200, ensuring clients don't apply duplicate effects. Because the cache is a class-level dict, it persists across requests in a single process — exactly what a mock server needs for testing idempotent API behavior.

Common mistakes

  • Forgetting to set Content-Length when sending the response, causing clients to hang.
  • Using a new dict per request, so idempotency never works.
  • Not decoding the request body before hashing, which produces inconsistent digests.

Variations

  1. Use Flask or FastAPI with a decorator to implement idempotency keys on a real API.
  2. Store responses in a database or Redis instead of an in-memory dict for multi-process deployments.

Real-world use cases

  • Mocking a payment gateway to verify duplicate charges are prevented in test suites.
  • Simulating a REST API that must return the same resource for retries on network failures.
  • Building a stub server to test client idempotency handling in integration tests.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from API design & gRPC

Related tutorials and quizzes for this topic.