Skip to main content

Python CI PyPI - Version GitHub Release

FASTAPI-CRONS

Effortlessly schedule and manage your background tasks.


Built with the tools and technologies:

FastAPI Crons โ€“ Developer Guide

Welcome to the official guide for using fastapi_crons, a high-performance, developer-friendly cron scheduling extension for FastAPI. This library enables you to define, monitor, and control scheduled background jobs using simple decorators and provides CLI tools, web-based monitoring, and SQLite-based job tracking.


๐Ÿš€ Features

  • Native integration with FastAPI using from fastapi import FastAPI, Crons
  • Define cron jobs with decorators
  • Async + sync job support
  • SQLite job state persistence
  • CLI for listing and managing jobs
  • Automatic monitoring endpoint (/crons)
  • Named jobs, tags, and metadata
  • Easy to plug into any FastAPI project

๐Ÿ“ฆ Installation

pip install fastapi-crons

That includes the web dashboard. Optional extras add the pluggable backends:

pip install fastapi-crons[sqlalchemy]  # SQLAlchemy state/lock backends
pip install fastapi-crons[sqlmodel]    # SQLModel state/lock backends
pip install fastapi-crons[otel]        # OpenTelemetry tracing

๐Ÿ› ๏ธ Quick Start

1. Setup FastAPI with Crons

from fastapi import FastAPI
from fastapi_crons import Crons, get_cron_router

app = FastAPI()
crons = Crons(app)

# Mount the management endpoints under a prefix. Without one they are served
# from the application root, where `GET /{job_name}` shadows your own routes.
app.include_router(get_cron_router(), prefix="/crons")

@app.get("/")
def root():
    return {"message": "Hello from FastAPI"}

2. Define Cron Jobs

@crons.cron("*/5 * * * *", name="print_hello")
def print_hello():
    print("Hello! I run every 5 minutes.")

@crons.cron("0 0 * * *", name="daily_task", tags=["rewards"])
async def run_daily_task():
    # Distribute daily rewards or any async task
    await some_async_function()

Cron Expression overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ minute (0 - 59)
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ hour (0 - 23)
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ day of the month (1 - 31)
โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ month (1 - 12)
โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ day of the week (0 - 6) (Sunday to Saturday)
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
* * * * *

Examples:

  • * * * * *: Every minute
  • */15 * * * *: Every 15 minutes
  • 0 * * * *: Every hour
  • 0 0 * * *: Every day at midnight
  • 0 0 * * 0: Every Sunday at midnight

๐Ÿ–ฅ๏ธ Cron Monitoring Endpoint

Once included, visit:

GET /crons

You'll get a full list of jobs with:

  • name
  • expr (cron expression)
  • tags
  • last_run (from SQLite)
  • next_run

๐Ÿ“Š Web Dashboard

A prebuilt web UI for browsing jobs and their run history ships with the package โ€” no extra needed. Mount the cron router and visit /dashboard underneath whatever prefix you chose:

GET /dashboard

See examples/dashboard/app.py for a runnable setup.

Note: the dashboard exposes job names, schedules and run history, and ships with no authentication of its own. Put it behind your own auth or network controls before mounting it on a public deployment.


๐Ÿงฉ SQLite Job State Tracking

We use SQLite (via aiosqlite) to keep a persistent record of when each job last ran. This allows observability and resilience during restarts.

๐Ÿ—„๏ธ SQLAlchemy State Backend

For projects already using SQLAlchemy or SQLModel with PostgreSQL or MySQL, you can reuse your existing database connection instead of SQLite.

pip install fastapi-crons[sqlalchemy]
# or
pip install fastapi-crons[sqlmodel]
from sqlalchemy.ext.asyncio import create_async_engine
from fastapi_crons import Crons
from fastapi_crons.state.sqlalchemy import SQLAlchemyStateBackend

engine  = create_async_engine("postgresql+asyncpg://user:pass@host/db")
backend = SQLAlchemyStateBackend(engine)
crons   = Crons(app, state_backend=backend)

Works with sync engines too:

from sqlalchemy import create_engine
engine  = create_engine("postgresql://user:pass@host/db")
backend = SQLAlchemyStateBackend(engine)

Alembic Integration

# env.py
from fastapi_crons.state.sqlalchemy import cron_metadata
target_metadata = [Base.metadata, cron_metadata]

๐Ÿ”’ SQLAlchemy Lock Backend

pip install fastapi-crons[sqlalchemy]
from fastapi_crons.locking.sqlalchemy import SQLAlchemyLockBackend
from fastapi_crons.locking import DistributedLockManager
from fastapi_crons import CronConfig

engine  = create_async_engine("postgresql+asyncpg://...")
backend = SQLAlchemyLockBackend(engine)
manager = DistributedLockManager(backend, CronConfig())
crons   = Crons(app, lock_manager=manager)

For PostgreSQL, advisory locks are available as a lighter alternative (no table required):

from fastapi_crons.locking.sqlalchemy import PostgreSQLAdvisoryLockBackend

backend = PostgreSQLAdvisoryLockBackend(engine)

Table:

CREATE TABLE IF NOT EXISTS job_state (
    name TEXT PRIMARY KEY,
    last_run TEXT
);

Configuration

By default, job state is stored in a SQLite database named cron_state.db in the current directory. You can customize the database path:

from fastapi_crons import Crons, SQLiteStateBackend

# Custom database path
state_backend = SQLiteStateBackend(db_path="/path/to/my_crons.db")
crons = Crons(state_backend=state_backend)

๐Ÿ‘ฅ Running Multiple Workers

Every worker registers the same jobs, so when a job becomes due exactly one worker must execute it. Point all workers at a shared lock backend and that is guaranteed: each scheduled tick is claimed atomically, and the worker that wins the claim is the only one that runs it.

export CRON_ENABLE_DISTRIBUTED_LOCKING=true
export CRON_REDIS_URL=redis://localhost:6379/0
# ...or wire a backend explicitly (a URL works, a client is not required)
from fastapi_crons import Crons, CronConfig, DistributedLockManager
from fastapi_crons.locking import RedisLockBackend

config  = CronConfig()
manager = DistributedLockManager(RedisLockBackend(config.redis_url), config)
crons   = Crons(app, lock_manager=manager)

No Redis? The SQLAlchemy lock backend coordinates through your existing database instead:

from fastapi_crons.locking.sqlalchemy import SQLAlchemyLockBackend

manager = DistributedLockManager(SQLAlchemyLockBackend(engine), CronConfig())

Two things to know:

  • The default SQLiteStateBackend is per-machine. Workers spread across hosts need Redis or the SQLAlchemy state backend.
  • Lock keys are namespaced fastapi_crons:lock: so they cannot collide with anything else in a shared Redis. Override with CRON_LOCK_KEY_PREFIX.

A job that takes longer than its own interval does not queue up: ticks that elapse while it runs are coalesced, and it resumes at the next scheduled time.


๐Ÿงต Async + Thread Execution

The scheduler supports both async and sync job functions Jobs can be:

  • async def โ†’ run in asyncio loop
  • def โ†’ run safely in background thread using await asyncio.to_thread(...)

๐Ÿงช CLI Support

# List all registered jobs
fastapi-crons list

# Manually run a specific job
# -i imports the module that registers your jobs (repeatable)
fastapi-crons run-job <job_name> -i myapp.jobs

# Show overall system status
fastapi-crons status

# Inspect / change configuration
fastapi-crons config-show
fastapi-crons config-set <key> <value>

# Run the scheduler outside of a FastAPI app
fastapi-crons start-scheduler -i myapp.jobs

# See every command and its options
fastapi-crons --help

๐Ÿงฉ Advanced Features

  • Distributed locking via Redis
  • Retry policies
  • Manual run triggers via HTTP
  • Admin dashboard with metrics

Job Tags

You can add tags to jobs for better organization:

@cron_job("*/5 * * * *", tags=["maintenance", "cleanup"])
async def cleanup_job():
    # This job has tags for categorization
    pass

โš™๏ธ Architecture Overview

FastAPI App
โ”‚
โ”œโ”€โ”€ Crons()
โ”‚   โ”œโ”€โ”€ Registers decorated jobs
โ”‚   โ”œโ”€โ”€ Starts background scheduler (async)
โ”‚
โ”œโ”€โ”€ SQLite Backend
โ”‚   โ”œโ”€โ”€ Tracks last run for each job
โ”‚
โ”œโ”€โ”€ /crons endpoint
โ”‚   โ”œโ”€โ”€ Shows current job status (with timestamps)
โ”‚
โ””โ”€โ”€ CLI Tool
    โ”œโ”€โ”€ List jobs / Run manually

๐Ÿง  Contributing

We welcome PRs and suggestions! If you'd like this added to FastAPI officially, fork the repo, polish it, and submit to FastAPI with a clear integration proposal.


๐Ÿ›ก๏ธ Error Handling

  • Each job has an isolated error handler
  • Errors are printed and don't block scheduler
  • Future: Add error logging / alert hooks

๐Ÿ“„ License

Licence


Need help? Reach out:

Email me

github

Read Documentation at:

Documentation

๐Ÿ’ฌ Credits

Made with โค๏ธ by Mehar Umar.
Designed to give developers freedom, flexibility, and control when building production-grade FastAPI apps.


Download files

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

Source Distribution

fastapi_crons-2.5.0.tar.gz (328.3 kB view details)

Uploaded Source

Built Distribution

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

fastapi_crons-2.5.0-py3-none-any.whl (318.3 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_crons-2.5.0.tar.gz.

File metadata

  • Download URL: fastapi_crons-2.5.0.tar.gz
  • Upload date:
  • Size: 328.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.20

File hashes

Hashes for fastapi_crons-2.5.0.tar.gz
Algorithm Hash digest
SHA256 414b4029b0390baddfb27cfc64b92734edf01f19e0070b836b63b45561ff1480
MD5 18669d6b0a9996682e3d486e8605b309
BLAKE2b-256 3a0dbfa84de5889a2735ac98173fb8fe2917be212b0766e9cd57b896738276dc

See more details on using hashes here.

File details

Details for the file fastapi_crons-2.5.0-py3-none-any.whl.

File metadata

  • Download URL: fastapi_crons-2.5.0-py3-none-any.whl
  • Upload date:
  • Size: 318.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.20

File hashes

Hashes for fastapi_crons-2.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e7728798ebd56f1cd95c81b22d1ee8ae5a58964764c0005468a599456f91f0c
MD5 8224e119f5f6e6c34384259cb83316a7
BLAKE2b-256 543332c59f65d60d7c415fa1dd1029167e6a9a2dbcd6e6762e71761d2cbc1176

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 Sentry Error logging StatusPage Status page