Getting Started
Quick Start
A running scheduler with two jobs and a management API, in four steps.
1. Attach the scheduler
Passing your app to Crons registers the startup and shutdown handlers that run the scheduler alongside your API.
from fastapi import FastAPI
from fastapi_crons import Crons, get_cron_router
app = FastAPI(title="My App")
crons = Crons(app)
# Optional: management endpoints under /api
app.include_router(get_cron_router(), prefix="/api")2. Define some jobs
The decorator takes a standard five-field cron expression. Async functions are awaited on the event loop; plain functions run in a worker thread so they never block it.
@crons.cron("*/5 * * * *", name="cleanup")
async def cleanup_task():
# Runs every 5 minutes.
await purge_expired_sessions()
return "cleanup complete"
@crons.cron("0 0 * * *", name="daily_report", tags=["reporting"])
def generate_daily_report():
# Runs at midnight, in a worker thread.
build_report()
return "report generated"3. Run the app
uvicorn app:app --reload4. Inspect the jobs
With the router mounted, the registered jobs and their next run times are available over HTTP:
curl http://127.0.0.1:8000/api/[
{
"name": "cleanup",
"expr": "*/5 * * * *",
"tags": [],
"last_run": "2026-07-27T10:35:00+00:00",
"next_run": "2026-07-27T10:40:00+00:00",
"hooks": {"before_run": 0, "after_run": 0, "on_error": 0},
"config": {"max_retries": null, "retry_delay": null, "timeout": null}
}
]Or from the terminal, without starting a server:
python -m fastapi_crons.cli list-jobsThat is the whole setup
From here, add hooks for logging and alerting, turn on distributed locking before you scale past one replica, or install the dashboard to watch jobs in a browser.
Before you deploy more than one replica
Every replica runs every job
The scheduler lives in your application process, so N replicas means N executions of each job unless you enable distributed locking.