[{"data":1,"prerenderedAt":1004},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002F":1002},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":201,"body":312,"description":953,"extension":954,"meta":955,"navigation":444,"path":202,"seo":1000,"stem":203,"__hash__":1001},"content\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex.md",{"type":313,"value":314,"toc":941},"minimark",[315,319,326,350,359,364,367,371,382,386,391,541,545,660,664,774,778,810,814,909,913,937],[316,317,201],"h1",{"id":318},"fastapi-rate-limiting-with-redis-and-slowapi",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,333,336,339,347],"ul",{},[330,331,332],"li",{},"SlowAPI provides decorator-based rate limits for FastAPI routes.",[330,334,335],{},"Back it with Redis so the limit is shared across all workers.",[330,337,338],{},"Use a custom key function to limit by API key rather than IP.",[330,340,341,342,346],{},"Exceeded limits return HTTP 429 with ",[343,344,345],"code",{},"Retry-After",".",[330,348,349],{},"Apply broad and per-route limits together for layered protection.",[320,351,352,353,358],{},"This guide is the library-based path from ",[354,355,357],"a",{"href":356},"\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002F","Rate Limiting and Throttling",". Read that page for why the counter must be shared.",[360,361,363],"h2",{"id":362},"the-problem-this-solves","The Problem This Solves",[320,365,366],{},"You want rate limiting without hand-rolling counters, and it must be correct across many workers. SlowAPI gives you a decorator API, and Redis storage makes the limit authoritative across the whole deployment.",[360,368,370],{"id":369},"prerequisites","Prerequisites",[327,372,373,379],{},[330,374,375,378],{},[343,376,377],{},"slowapi"," installed and a Redis instance reachable from every worker.",[330,380,381],{},"A FastAPI app constructed via a factory.",[360,383,385],{"id":384},"step-by-step-implementation","Step-by-Step Implementation",[387,388,390],"h3",{"id":389},"_1-create-the-limiter-with-a-key-function-and-redis-storage","1. Create the limiter with a key function and Redis storage",[392,393,398],"pre",{"className":394,"code":395,"language":396,"meta":397,"style":397},"language-python shiki shiki-themes github-light","# app\u002Flimiter.py\nfrom slowapi import Limiter\nfrom starlette.requests import Request\n\n\ndef client_key(request: Request) -> str:\n    # Limit by API key when present, else by client IP.\n    return request.headers.get(\"x-api-key\") or request.client.host\n\n\nlimiter = Limiter(key_func=client_key, storage_uri=\"redis:\u002F\u002Flocalhost:6379\")\n","python","",[343,399,400,409,426,439,446,451,471,477,499,504,509],{"__ignoreMap":397},[401,402,405],"span",{"class":403,"line":404},"line",1,[401,406,408],{"class":407},"sAwPA","# app\u002Flimiter.py\n",[401,410,412,416,420,423],{"class":403,"line":411},2,[401,413,415],{"class":414},"sD7c4","from",[401,417,419],{"class":418},"sgsFI"," slowapi ",[401,421,422],{"class":414},"import",[401,424,425],{"class":418}," Limiter\n",[401,427,429,431,434,436],{"class":403,"line":428},3,[401,430,415],{"class":414},[401,432,433],{"class":418}," starlette.requests ",[401,435,422],{"class":414},[401,437,438],{"class":418}," Request\n",[401,440,442],{"class":403,"line":441},4,[401,443,445],{"emptyLinePlaceholder":444},true,"\n",[401,447,449],{"class":403,"line":448},5,[401,450,445],{"emptyLinePlaceholder":444},[401,452,454,457,461,464,468],{"class":403,"line":453},6,[401,455,456],{"class":414},"def",[401,458,460],{"class":459},"s7eDp"," client_key",[401,462,463],{"class":418},"(request: Request) -> ",[401,465,467],{"class":466},"sYu0t","str",[401,469,470],{"class":418},":\n",[401,472,474],{"class":403,"line":473},7,[401,475,476],{"class":407},"    # Limit by API key when present, else by client IP.\n",[401,478,480,483,486,490,493,496],{"class":403,"line":479},8,[401,481,482],{"class":414},"    return",[401,484,485],{"class":418}," request.headers.get(",[401,487,489],{"class":488},"sYBdl","\"x-api-key\"",[401,491,492],{"class":418},") ",[401,494,495],{"class":414},"or",[401,497,498],{"class":418}," request.client.host\n",[401,500,502],{"class":403,"line":501},9,[401,503,445],{"emptyLinePlaceholder":444},[401,505,507],{"class":403,"line":506},10,[401,508,445],{"emptyLinePlaceholder":444},[401,510,512,515,518,521,525,527,530,533,535,538],{"class":403,"line":511},11,[401,513,514],{"class":418},"limiter ",[401,516,517],{"class":414},"=",[401,519,520],{"class":418}," Limiter(",[401,522,524],{"class":523},"sqxcx","key_func",[401,526,517],{"class":414},[401,528,529],{"class":418},"client_key, ",[401,531,532],{"class":523},"storage_uri",[401,534,517],{"class":414},[401,536,537],{"class":488},"\"redis:\u002F\u002Flocalhost:6379\"",[401,539,540],{"class":418},")\n",[387,542,544],{"id":543},"_2-register-it-on-the-app","2. Register it on the app",[392,546,548],{"className":394,"code":547,"language":396,"meta":397,"style":397},"from slowapi import _rate_limit_exceeded_handler\nfrom slowapi.errors import RateLimitExceeded\nfrom slowapi.middleware import SlowAPIMiddleware\n\nfrom app.limiter import limiter\n\n\ndef create_app():\n    app = FastAPI()\n    app.state.limiter = limiter\n    app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)\n    app.add_middleware(SlowAPIMiddleware)   # Applies default limits globally.\n    return app\n",[343,549,550,561,573,585,589,601,605,609,619,629,638,643,652],{"__ignoreMap":397},[401,551,552,554,556,558],{"class":403,"line":404},[401,553,415],{"class":414},[401,555,419],{"class":418},[401,557,422],{"class":414},[401,559,560],{"class":418}," _rate_limit_exceeded_handler\n",[401,562,563,565,568,570],{"class":403,"line":411},[401,564,415],{"class":414},[401,566,567],{"class":418}," slowapi.errors ",[401,569,422],{"class":414},[401,571,572],{"class":418}," RateLimitExceeded\n",[401,574,575,577,580,582],{"class":403,"line":428},[401,576,415],{"class":414},[401,578,579],{"class":418}," slowapi.middleware ",[401,581,422],{"class":414},[401,583,584],{"class":418}," SlowAPIMiddleware\n",[401,586,587],{"class":403,"line":441},[401,588,445],{"emptyLinePlaceholder":444},[401,590,591,593,596,598],{"class":403,"line":448},[401,592,415],{"class":414},[401,594,595],{"class":418}," app.limiter ",[401,597,422],{"class":414},[401,599,600],{"class":418}," limiter\n",[401,602,603],{"class":403,"line":453},[401,604,445],{"emptyLinePlaceholder":444},[401,606,607],{"class":403,"line":473},[401,608,445],{"emptyLinePlaceholder":444},[401,610,611,613,616],{"class":403,"line":479},[401,612,456],{"class":414},[401,614,615],{"class":459}," create_app",[401,617,618],{"class":418},"():\n",[401,620,621,624,626],{"class":403,"line":501},[401,622,623],{"class":418},"    app ",[401,625,517],{"class":414},[401,627,628],{"class":418}," FastAPI()\n",[401,630,631,634,636],{"class":403,"line":506},[401,632,633],{"class":418},"    app.state.limiter ",[401,635,517],{"class":414},[401,637,600],{"class":418},[401,639,640],{"class":403,"line":511},[401,641,642],{"class":418},"    app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)\n",[401,644,646,649],{"class":403,"line":645},12,[401,647,648],{"class":418},"    app.add_middleware(SlowAPIMiddleware)   ",[401,650,651],{"class":407},"# Applies default limits globally.\n",[401,653,655,657],{"class":403,"line":654},13,[401,656,482],{"class":414},[401,658,659],{"class":418}," app\n",[387,661,663],{"id":662},"_3-apply-per-route-limits","3. Apply per-route limits",[392,665,667],{"className":394,"code":666,"language":396,"meta":397,"style":397},"from fastapi import Request\n\nfrom app.limiter import limiter\n\n\n@router.post(\"\u002Fexports\")\n@limiter.limit(\"5\u002Fminute\")          # Tighter limit on an expensive endpoint.\nasync def create_export(request: Request) -> dict[str, str]:\n    return {\"status\": \"queued\"}\n",[343,668,669,680,684,694,698,702,715,731,755],{"__ignoreMap":397},[401,670,671,673,676,678],{"class":403,"line":404},[401,672,415],{"class":414},[401,674,675],{"class":418}," fastapi ",[401,677,422],{"class":414},[401,679,438],{"class":418},[401,681,682],{"class":403,"line":411},[401,683,445],{"emptyLinePlaceholder":444},[401,685,686,688,690,692],{"class":403,"line":428},[401,687,415],{"class":414},[401,689,595],{"class":418},[401,691,422],{"class":414},[401,693,600],{"class":418},[401,695,696],{"class":403,"line":441},[401,697,445],{"emptyLinePlaceholder":444},[401,699,700],{"class":403,"line":448},[401,701,445],{"emptyLinePlaceholder":444},[401,703,704,707,710,713],{"class":403,"line":453},[401,705,706],{"class":459},"@router.post",[401,708,709],{"class":418},"(",[401,711,712],{"class":488},"\"\u002Fexports\"",[401,714,540],{"class":418},[401,716,717,720,722,725,728],{"class":403,"line":473},[401,718,719],{"class":459},"@limiter.limit",[401,721,709],{"class":418},[401,723,724],{"class":488},"\"5\u002Fminute\"",[401,726,727],{"class":418},")          ",[401,729,730],{"class":407},"# Tighter limit on an expensive endpoint.\n",[401,732,733,736,739,742,745,747,750,752],{"class":403,"line":479},[401,734,735],{"class":414},"async",[401,737,738],{"class":414}," def",[401,740,741],{"class":459}," create_export",[401,743,744],{"class":418},"(request: Request) -> dict[",[401,746,467],{"class":466},[401,748,749],{"class":418},", ",[401,751,467],{"class":466},[401,753,754],{"class":418},"]:\n",[401,756,757,759,762,765,768,771],{"class":403,"line":501},[401,758,482],{"class":414},[401,760,761],{"class":418}," {",[401,763,764],{"class":488},"\"status\"",[401,766,767],{"class":418},": ",[401,769,770],{"class":488},"\"queued\"",[401,772,773],{"class":418},"}\n",[360,775,777],{"id":776},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,779,780,794,804],{},[330,781,782,789,790,793],{},[323,783,784,785,788],{},"The ",[343,786,787],{},"request"," parameter."," SlowAPI's decorator needs the ",[343,791,792],{},"Request"," in the signature; omitting it raises at call time.",[330,795,796,799,800,803],{},[323,797,798],{},"Proxies."," Behind a load balancer, ",[343,801,802],{},"request.client.host"," may be the proxy; read the forwarded client IP from a trusted header.",[330,805,806,809],{},[323,807,808],{},"Default vs route limits."," A global default plus per-route overrides can interact; verify the effective limit on each route.",[360,811,813],{"id":812},"verification","Verification",[392,815,817],{"className":394,"code":816,"language":396,"meta":397,"style":397},"def test_limit_returns_429(client):\n    for _ in range(5):\n        assert client.post(\"\u002Fexports\").status_code == 200\n    blocked = client.post(\"\u002Fexports\")\n    assert blocked.status_code == 429\n    assert \"Retry-After\" in blocked.headers\n",[343,818,819,829,851,870,883,896],{"__ignoreMap":397},[401,820,821,823,826],{"class":403,"line":404},[401,822,456],{"class":414},[401,824,825],{"class":459}," test_limit_returns_429",[401,827,828],{"class":418},"(client):\n",[401,830,831,834,837,840,843,845,848],{"class":403,"line":411},[401,832,833],{"class":414},"    for",[401,835,836],{"class":418}," _ ",[401,838,839],{"class":414},"in",[401,841,842],{"class":466}," range",[401,844,709],{"class":418},[401,846,847],{"class":466},"5",[401,849,850],{"class":418},"):\n",[401,852,853,856,859,861,864,867],{"class":403,"line":428},[401,854,855],{"class":414},"        assert",[401,857,858],{"class":418}," client.post(",[401,860,712],{"class":488},[401,862,863],{"class":418},").status_code ",[401,865,866],{"class":414},"==",[401,868,869],{"class":466}," 200\n",[401,871,872,875,877,879,881],{"class":403,"line":441},[401,873,874],{"class":418},"    blocked ",[401,876,517],{"class":414},[401,878,858],{"class":418},[401,880,712],{"class":488},[401,882,540],{"class":418},[401,884,885,888,891,893],{"class":403,"line":448},[401,886,887],{"class":414},"    assert",[401,889,890],{"class":418}," blocked.status_code ",[401,892,866],{"class":414},[401,894,895],{"class":466}," 429\n",[401,897,898,900,903,906],{"class":403,"line":453},[401,899,887],{"class":414},[401,901,902],{"class":488}," \"Retry-After\"",[401,904,905],{"class":414}," in",[401,907,908],{"class":418}," blocked.headers\n",[360,910,912],{"id":911},"related-reading","Related Reading",[327,914,915,923],{},[330,916,917,920,921,346],{},[323,918,919],{},"Up to the topic:"," ",[354,922,357],{"href":356},[330,924,925,920,928,932,933,346],{},[323,926,927],{},"Related guides:",[354,929,931],{"href":930},"\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002F","Per-User Token Bucket Throttling"," and ",[354,934,936],{"href":935},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","Middleware Implementation",[938,939,940],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":397,"searchDepth":411,"depth":411,"links":942},[943,944,945,950,951,952],{"id":362,"depth":411,"text":363},{"id":369,"depth":411,"text":370},{"id":384,"depth":411,"text":385,"children":946},[947,948,949],{"id":389,"depth":428,"text":390},{"id":543,"depth":428,"text":544},{"id":662,"depth":428,"text":663},{"id":776,"depth":411,"text":777},{"id":812,"depth":411,"text":813},{"id":911,"depth":411,"text":912},"Add rate limiting to FastAPI with SlowAPI backed by Redis: install and wire the limiter, per-route limits, a shared Redis store for multi-worker correctness, and 429 handling.","md",{"slug":956,"type":957,"breadcrumb":958,"datePublished":970,"dateModified":971,"howto":972,"faq":990},"fastapi-rate-limiting-with-redis-slowapi","long_tail",[959,962,965,967],{"label":960,"path":961},"Home","\u002F",{"label":963,"path":964},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":966,"path":356},"Rate Limiting & Throttling",{"label":968,"path":969},"Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002F","2026-02-27","2026-06-18",{"name":973,"steps":974},"Add rate limiting to FastAPI with SlowAPI and Redis",[975,978,981,984,987],{"name":976,"text":977},"Install and create the limiter","Create a SlowAPI Limiter with a key function and a Redis storage URI.",{"name":979,"text":980},"Register it on the app","Attach the limiter to app.state and add the 429 exception handler.",{"name":982,"text":983},"Decorate routes with limits","Apply per-route limits with the limit decorator.",{"name":985,"text":986},"Use Redis storage","Point storage at Redis so the limit is shared across all workers.",{"name":988,"text":989},"Return a proper 429","Ensure exceeded limits return 429 with a Retry-After header.",[991,994,997],{"q":992,"a":993},"Why does SlowAPI need Redis storage in production?","SlowAPI's default in-memory storage counts per process, so with multiple workers each one tracks only its own traffic and the global limit is not enforced. Pointing storage at Redis gives every worker one shared counter, so a client's limit holds across the whole deployment rather than per worker.",{"q":995,"a":996},"How do I rate limit by API key instead of IP address?","Provide a custom key function to the Limiter that returns the API key from the request, falling back to the client IP when no key is present. The limiter then buckets by that identity, so authenticated clients are limited per key rather than per shared IP.",{"q":998,"a":999},"Does SlowAPI work with async FastAPI routes?","Yes. SlowAPI integrates with FastAPI and Starlette and works with async routes. Use an async-compatible Redis storage backend so the limiter's store access does not block the event loop, consistent with keeping all I\u002FO on the hot path asynchronous.",{"title":201,"description":953},"q86q9RjI199MHBwtc9Z5awapi3BCR1x9I26fcqPPSYU",[1003,1003],null,1781809863674]