Skip to main content

Production-ready idempotency keys for FastAPI, Flask, Django and any Python function. Redis & memory backends, async-first.

Project description

pyidempotent

Production-ready idempotency keys for Python backends — in 5 lines.

Stop double charges, double orders, and duplicate webhooks. Exactly-once execution for FastAPI, Flask, Django, Celery — with Redis or in-memory store.

from fastapi import FastAPI, Request
from pyidempotent import idempotent
from pyidempotent.backends.redis import RedisBackend
import redis.asyncio as redis

app = FastAPI()
backend = RedisBackend(redis.from_url("redis://localhost"))

@app.post("/pay")
@idempotent(backend=backend, ttl=24*3600)
async def pay(request: Request, amount: int):
    # will run ONCE per Idempotency-Key
    return {"status": "charged", "amount": amount}

Send Idempotency-Key: 123e4567-e89b-12d3-a456-426614174000 header. Repeat the request — you get the cached response, no second charge.


Why pyidempotent?

  • Async-first, works with sync too
  • Zero framework lock-in — pure decorator
  • Race-safe — Redis SET NX + atomic claim
  • Request fingerprinting — 422 if same key but different body
  • Processing state — returns 409 while first request still runs
  • Pluggable backends — Redis (prod), Memory (tests)
  • <400 LOC, no magic

Install

pip install pyidempotent[redis,fastapi]

Backends

# Production
from pyidempotent.backends.redis import RedisBackend
backend = RedisBackend(redis.from_url("redis://localhost"), prefix="idem:")

# Tests
from pyidempotent.backends.memory import MemoryBackend
backend = MemoryBackend()

FastAPI full example

from fastapi import FastAPI, Request, HTTPException
from pyidempotent import idempotent, IdempotencyConflict, IdempotencyProcessing
from pyidempotent.backends.redis import RedisBackend
import redis.asyncio as redis

app = FastAPI()
backend = RedisBackend(redis.from_url("redis://localhost"))

@app.exception_handler(IdempotencyConflict)
async def conflict_handler(_, exc):
    raise HTTPException(422, "Idempotency-Key already used with different payload")

@app.post("/orders")
@idempotent(backend=backend, key_header="Idempotency-Key")
async def create_order(request: Request, item: str):
    # your DB write here
    return {"order_id": "ord_123", "item": item}

How it works

  1. Extract key from header
  2. SET key {status:processing} NX EX ttl — atomic claim
  3. Run your function
  4. SET key {status:completed, response:...} EX ttl
  5. Duplicates return cached response

Fingerprint = SHA256 of function arguments (excluding Request). Prevents accidental reuse.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyidempotent-0.1.0.tar.gz (5.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyidempotent-0.1.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file pyidempotent-0.1.0.tar.gz.

File metadata

  • Download URL: pyidempotent-0.1.0.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for pyidempotent-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8d30d12ff10891c0ec41844573cd5b8685aff7c2d889070acc730c1715b24b59
MD5 4325940c6fd7a27f35a459a1e8935c41
BLAKE2b-256 0c7a9aa47796756a197666fb7f3c8475ce153beeff5f400e29f4e9422f226c2d

See more details on using hashes here.

File details

Details for the file pyidempotent-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pyidempotent-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for pyidempotent-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e457aded1acad41c7ce971a6d92f3c3ef82650536937e635dfdafab331d3ef64
MD5 cb79066c7634ad705fe66f8566d835fa
BLAKE2b-256 d8574878a01dc62e326b761bb722fb2194b6def56e21b45768f15f62551b58b2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page