[{"data":1,"prerenderedAt":1009},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002F":1007},[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":147,"body":312,"description":960,"extension":961,"meta":962,"navigation":524,"path":148,"seo":1005,"stem":149,"__hash__":1006},"content\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex.md",{"type":313,"value":314,"toc":947},"minimark",[315,319,326,349,358,363,366,370,378,382,482,486,491,612,616,698,702,815,819,844,848,913,916,920,943],[316,317,147],"h1",{"id":318},"fastapi-backgroundtasks-vs-celery-vs-arq",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,337,340,343,346],"ul",{},[330,331,332,336],"li",{},[333,334,335],"code",{},"BackgroundTasks"," is in-process and fire-and-forget — fine only when losing a job is acceptable.",[330,338,339],{},"Celery and ARQ are durable queues with retries, scheduling, and independent scaling.",[330,341,342],{},"ARQ is async-native and lightweight; Celery is mature with a broad ecosystem.",[330,344,345],{},"Durable workers run as separate processes with their own database pools.",[330,347,348],{},"Whatever you choose, make jobs idempotent so retries are safe.",[320,350,351,352,357],{},"This comparison operationalizes ",[353,354,356],"a",{"href":355},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002F","Background Task Processing",". Read that page for the in-process versus queued model.",[359,360,362],"h2",{"id":361},"the-problem-this-solves","The Problem This Solves",[320,364,365],{},"\"Run this after responding\" has three very different answers depending on whether the work can be lost, must be retried, or must scale independently. Choosing the wrong one means either dropped jobs or needless infrastructure.",[359,367,369],{"id":368},"prerequisites","Prerequisites",[327,371,372,375],{},[330,373,374],{},"A FastAPI app with deferred work to run.",[330,376,377],{},"Redis available if you adopt ARQ or Celery-on-Redis.",[359,379,381],{"id":380},"the-comparison","The Comparison",[383,384,385,403],"table",{},[386,387,388],"thead",{},[389,390,391,395,397,400],"tr",{},[392,393,394],"th",{},"Capability",[392,396,335],{},[392,398,399],{},"ARQ",[392,401,402],{},"Celery",[404,405,406,420,431,444,455,468],"tbody",{},[389,407,408,412,415,418],{},[409,410,411],"td",{},"Durability across restarts",[409,413,414],{},"No",[409,416,417],{},"Yes",[409,419,417],{},[389,421,422,425,427,429],{},[409,423,424],{},"Automatic retries",[409,426,414],{},[409,428,417],{},[409,430,417],{},[389,432,433,436,438,441],{},[409,434,435],{},"Scheduling (cron)",[409,437,414],{},[409,439,440],{},"Yes (cron jobs)",[409,442,443],{},"Yes (beat)",[389,445,446,449,451,453],{},[409,447,448],{},"Separate scaling",[409,450,414],{},[409,452,417],{},[409,454,417],{},[389,456,457,460,463,465],{},[409,458,459],{},"Async-native",[409,461,462],{},"Runs on the loop",[409,464,417],{},[409,466,467],{},"Newer support",[389,469,470,473,476,479],{},[409,471,472],{},"Setup cost",[409,474,475],{},"None",[409,477,478],{},"Low",[409,480,481],{},"Moderate",[359,483,485],{"id":484},"step-by-step-choosing","Step-by-Step: Choosing",[487,488,490],"h3",{"id":489},"_1-fire-and-forget-loss-acceptable-backgroundtasks","1. Fire-and-forget, loss acceptable → BackgroundTasks",[492,493,498],"pre",{"className":494,"code":495,"language":496,"meta":497,"style":497},"language-python shiki shiki-themes github-light","from fastapi import BackgroundTasks\n\n\n@app.post(\"\u002Faudit\")\nasync def audit(event: dict, tasks: BackgroundTasks) -> dict[str, str]:\n    tasks.add_task(write_audit_line, event)   # Lost on crash — acceptable here.\n    return {\"status\": \"ok\"}\n","python","",[333,499,500,519,526,531,548,581,591],{"__ignoreMap":497},[501,502,505,509,513,516],"span",{"class":503,"line":504},"line",1,[501,506,508],{"class":507},"sD7c4","from",[501,510,512],{"class":511},"sgsFI"," fastapi ",[501,514,515],{"class":507},"import",[501,517,518],{"class":511}," BackgroundTasks\n",[501,520,522],{"class":503,"line":521},2,[501,523,525],{"emptyLinePlaceholder":524},true,"\n",[501,527,529],{"class":503,"line":528},3,[501,530,525],{"emptyLinePlaceholder":524},[501,532,534,538,541,545],{"class":503,"line":533},4,[501,535,537],{"class":536},"s7eDp","@app.post",[501,539,540],{"class":511},"(",[501,542,544],{"class":543},"sYBdl","\"\u002Faudit\"",[501,546,547],{"class":511},")\n",[501,549,551,554,557,560,563,567,570,573,576,578],{"class":503,"line":550},5,[501,552,553],{"class":507},"async",[501,555,556],{"class":507}," def",[501,558,559],{"class":536}," audit",[501,561,562],{"class":511},"(event: ",[501,564,566],{"class":565},"sYu0t","dict",[501,568,569],{"class":511},", tasks: BackgroundTasks) -> dict[",[501,571,572],{"class":565},"str",[501,574,575],{"class":511},", ",[501,577,572],{"class":565},[501,579,580],{"class":511},"]:\n",[501,582,584,587],{"class":503,"line":583},6,[501,585,586],{"class":511},"    tasks.add_task(write_audit_line, event)   ",[501,588,590],{"class":589},"sAwPA","# Lost on crash — acceptable here.\n",[501,592,594,597,600,603,606,609],{"class":503,"line":593},7,[501,595,596],{"class":507},"    return",[501,598,599],{"class":511}," {",[501,601,602],{"class":543},"\"status\"",[501,604,605],{"class":511},": ",[501,607,608],{"class":543},"\"ok\"",[501,610,611],{"class":511},"}\n",[487,613,615],{"id":614},"_2-durable-async-native-arq","2. Durable + async-native → ARQ",[492,617,619],{"className":494,"code":618,"language":496,"meta":497,"style":497},"async def sync_invoice(ctx: dict, invoice_id: str) -> None:\n    await push_to_accounting(invoice_id)      # Retried by ARQ on failure.\n\n\nclass WorkerSettings:\n    functions = [sync_invoice]\n    max_tries = 5\n",[333,620,621,648,659,663,667,677,688],{"__ignoreMap":497},[501,622,623,625,627,630,633,635,638,640,643,645],{"class":503,"line":504},[501,624,553],{"class":507},[501,626,556],{"class":507},[501,628,629],{"class":536}," sync_invoice",[501,631,632],{"class":511},"(ctx: ",[501,634,566],{"class":565},[501,636,637],{"class":511},", invoice_id: ",[501,639,572],{"class":565},[501,641,642],{"class":511},") -> ",[501,644,475],{"class":565},[501,646,647],{"class":511},":\n",[501,649,650,653,656],{"class":503,"line":521},[501,651,652],{"class":507},"    await",[501,654,655],{"class":511}," push_to_accounting(invoice_id)      ",[501,657,658],{"class":589},"# Retried by ARQ on failure.\n",[501,660,661],{"class":503,"line":528},[501,662,525],{"emptyLinePlaceholder":524},[501,664,665],{"class":503,"line":533},[501,666,525],{"emptyLinePlaceholder":524},[501,668,669,672,675],{"class":503,"line":550},[501,670,671],{"class":507},"class",[501,673,674],{"class":536}," WorkerSettings",[501,676,647],{"class":511},[501,678,679,682,685],{"class":503,"line":583},[501,680,681],{"class":511},"    functions ",[501,683,684],{"class":507},"=",[501,686,687],{"class":511}," [sync_invoice]\n",[501,689,690,693,695],{"class":503,"line":593},[501,691,692],{"class":511},"    max_tries ",[501,694,684],{"class":507},[501,696,697],{"class":565}," 5\n",[487,699,701],{"id":700},"_3-durable-broad-ecosystem-scheduling-celery","3. Durable + broad ecosystem \u002F scheduling → Celery",[492,703,705],{"className":494,"code":704,"language":496,"meta":497,"style":497},"from celery import Celery\n\ncelery = Celery(\"app\", broker=\"redis:\u002F\u002Flocalhost:6379\u002F0\")\n\n\n@celery.task(bind=True, max_retries=5)\ndef rebuild_search_index(self, tenant_id: str) -> None:\n    # Mature retry\u002Fbackoff and beat scheduling come built in.\n    reindex(tenant_id)\n",[333,706,707,719,723,749,753,757,784,803,809],{"__ignoreMap":497},[501,708,709,711,714,716],{"class":503,"line":504},[501,710,508],{"class":507},[501,712,713],{"class":511}," celery ",[501,715,515],{"class":507},[501,717,718],{"class":511}," Celery\n",[501,720,721],{"class":503,"line":521},[501,722,525],{"emptyLinePlaceholder":524},[501,724,725,728,730,733,736,738,742,744,747],{"class":503,"line":528},[501,726,727],{"class":511},"celery ",[501,729,684],{"class":507},[501,731,732],{"class":511}," Celery(",[501,734,735],{"class":543},"\"app\"",[501,737,575],{"class":511},[501,739,741],{"class":740},"sqxcx","broker",[501,743,684],{"class":507},[501,745,746],{"class":543},"\"redis:\u002F\u002Flocalhost:6379\u002F0\"",[501,748,547],{"class":511},[501,750,751],{"class":503,"line":533},[501,752,525],{"emptyLinePlaceholder":524},[501,754,755],{"class":503,"line":550},[501,756,525],{"emptyLinePlaceholder":524},[501,758,759,762,764,767,769,772,774,777,779,782],{"class":503,"line":583},[501,760,761],{"class":536},"@celery.task",[501,763,540],{"class":511},[501,765,766],{"class":740},"bind",[501,768,684],{"class":507},[501,770,771],{"class":565},"True",[501,773,575],{"class":511},[501,775,776],{"class":740},"max_retries",[501,778,684],{"class":507},[501,780,781],{"class":565},"5",[501,783,547],{"class":511},[501,785,786,789,792,795,797,799,801],{"class":503,"line":593},[501,787,788],{"class":507},"def",[501,790,791],{"class":536}," rebuild_search_index",[501,793,794],{"class":511},"(self, tenant_id: ",[501,796,572],{"class":565},[501,798,642],{"class":511},[501,800,475],{"class":565},[501,802,647],{"class":511},[501,804,806],{"class":503,"line":805},8,[501,807,808],{"class":589},"    # Mature retry\u002Fbackoff and beat scheduling come built in.\n",[501,810,812],{"class":503,"line":811},9,[501,813,814],{"class":511},"    reindex(tenant_id)\n",[359,816,818],{"id":817},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,820,821,827,833],{},[330,822,823,826],{},[323,824,825],{},"Result backends."," If you need job results, configure a result backend; otherwise jobs are fire-and-forget even in a durable queue.",[330,828,829,832],{},[323,830,831],{},"Serialization."," Arguments are serialized to the broker; pass IDs, not large objects or ORM instances.",[330,834,835,838,839,843],{},[323,836,837],{},"Context loss."," Forward the correlation ID into the job for tracing, per ",[353,840,842],{"href":841},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F","Observability and Tracing",".",[359,845,847],{"id":846},"verification","Verification",[492,849,851],{"className":494,"code":850,"language":496,"meta":497,"style":497},"def test_audit_does_not_block_response(client):\n    resp = client.post(\"\u002Faudit\", json={\"type\": \"login\"})\n    assert resp.status_code == 200   # Returns without awaiting the task.\n",[333,852,853,863,896],{"__ignoreMap":497},[501,854,855,857,860],{"class":503,"line":504},[501,856,788],{"class":507},[501,858,859],{"class":536}," test_audit_does_not_block_response",[501,861,862],{"class":511},"(client):\n",[501,864,865,868,870,873,875,877,880,882,885,888,890,893],{"class":503,"line":521},[501,866,867],{"class":511},"    resp ",[501,869,684],{"class":507},[501,871,872],{"class":511}," client.post(",[501,874,544],{"class":543},[501,876,575],{"class":511},[501,878,879],{"class":740},"json",[501,881,684],{"class":507},[501,883,884],{"class":511},"{",[501,886,887],{"class":543},"\"type\"",[501,889,605],{"class":511},[501,891,892],{"class":543},"\"login\"",[501,894,895],{"class":511},"})\n",[501,897,898,901,904,907,910],{"class":503,"line":528},[501,899,900],{"class":507},"    assert",[501,902,903],{"class":511}," resp.status_code ",[501,905,906],{"class":507},"==",[501,908,909],{"class":565}," 200",[501,911,912],{"class":589},"   # Returns without awaiting the task.\n",[320,914,915],{},"For durable queues, assert a job is enqueued and that re-running it is idempotent.",[359,917,919],{"id":918},"related-reading","Related Reading",[327,921,922,930],{},[330,923,924,927,928,843],{},[323,925,926],{},"Up to the topic:"," ",[353,929,356],{"href":355},[330,931,932,927,935,938,939,843],{},[323,933,934],{},"Related guides:",[353,936,153],{"href":937},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002F"," and ",[353,940,942],{"href":941},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F","Async Database Sessions",[944,945,946],"style",{},"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 .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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);}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":497,"searchDepth":521,"depth":521,"links":948},[949,950,951,952,957,958,959],{"id":361,"depth":521,"text":362},{"id":368,"depth":521,"text":369},{"id":380,"depth":521,"text":381},{"id":484,"depth":521,"text":485,"children":953},[954,955,956],{"id":489,"depth":528,"text":490},{"id":614,"depth":528,"text":615},{"id":700,"depth":528,"text":701},{"id":817,"depth":521,"text":818},{"id":846,"depth":521,"text":847},{"id":918,"depth":521,"text":919},"Compare FastAPI BackgroundTasks, Celery, and ARQ for deferred work: durability, retries, scheduling, async fit, and a decision guide for choosing the right task system.","md",{"slug":318,"type":963,"breadcrumb":964,"datePublished":975,"dateModified":976,"howto":977,"faq":995},"long_tail",[965,968,971,972],{"label":966,"path":967},"Home","\u002F",{"label":969,"path":970},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":356,"path":355},{"label":973,"path":974},"BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002F","2026-02-19","2026-06-18",{"name":978,"steps":979},"Choose between BackgroundTasks, Celery, and ARQ",[980,983,986,989,992],{"name":981,"text":982},"Decide if loss is acceptable","If a dropped job on restart is harmless, BackgroundTasks suffices; otherwise you need a durable queue.",{"name":984,"text":985},"Decide if you need retries or scheduling","Durable retries, cron schedules, and rate-limited tasks require Celery or ARQ.",{"name":987,"text":988},"Match the concurrency model","Choose ARQ for async-native code and Celery for its mature ecosystem and broad integrations.",{"name":990,"text":991},"Provision the broker","Stand up Redis or RabbitMQ and a worker pool separate from the web workers.",{"name":993,"text":994},"Make jobs idempotent","Design each job so retries are safe, using a unique job key or upserts.",[996,999,1002],{"q":997,"a":998},"Can BackgroundTasks replace Celery for production work?","Only for work that is acceptable to lose. BackgroundTasks runs in the web process after the response, with no persistence, no retries, and no separate scaling. The moment a job must survive a restart, retry on failure, or run on a schedule, you need a durable queue like Celery or ARQ.",{"q":1000,"a":1001},"Should I pick Celery or ARQ for an async FastAPI app?","ARQ is async-native and integrates cleanly with an async codebase and Redis, with less ceremony. Celery has a larger ecosystem, more brokers, mature scheduling via beat, and broad community support, but its async support is comparatively newer. Pick ARQ for simplicity in async stacks and Celery when you need its breadth.",{"q":1003,"a":1004},"Do background workers share the FastAPI app's database pool?","No. Workers are separate processes, so they create their own engine and pool. Size those pools as part of your total database connection budget alongside the web workers, or you can exhaust the database when both tiers are busy.",{"title":147,"description":960},"BDHzTCeKkzGcF9iymrU-sfcJQ5qNYwt_okDkU6f1zts",[1008,1008],null,1781809863609]