Schedule Python tasks with APScheduler 3.x
Choose triggers and scheduler types, persist job definitions, and handle missed runs and single-process coordination in APScheduler 3.x.
APScheduler runs Python functions at a chosen time or interval. It can schedule API calls, reports, notifications, and maintenance work, and persist job definitions in a database.
This article uses APScheduler 3.x. Version 4 has different APIs and architecture; do not combine its examples with this code.
When it fits
Use APScheduler when application code needs dynamic schedules: synchronize data hourly, produce a daily report, check a service every five minutes, or send a one-time reminder.
It runs within a Python process and does not require a message broker. That makes a small deployment simpler than a distributed queue, but the scheduler's host and process still need to stay healthy.
Start with an interval
pip install 'APScheduler>=3.11,<4' sqlalchemy
from apscheduler.schedulers.blocking import BlockingScheduler
def sync_data():
print("Starting data synchronization...")
sched = BlockingScheduler()
sched.add_job(sync_data, "interval", minutes=30)
sched.start()
BlockingScheduler occupies the calling thread. BackgroundScheduler starts a background thread, so the application must keep the process alive. Do not start an independent scheduler in every worker of a multi-worker web deployment.
Choose a trigger
| Trigger | Registration example | Use |
|---|---|---|
interval |
add_job(fn, "interval", hours=2) |
Repeated interval |
cron |
add_job(fn, "cron", day_of_week="mon", hour=3) |
Calendar schedule |
date |
add_job(fn, "date", run_date="2026-12-01 09:00") |
One-time execution |
The cron trigger accepts seconds and calendar expressions such as an ordinal weekday. Set the time zone explicitly when wall-clock time matters; APScheduler's cron fields are not a promise of identical semantics to every system-cron implementation.
Choose a scheduler
BlockingScheduler: scheduling is the main program.BackgroundScheduler: a background thread inside a long-lived synchronous process.AsyncIOScheduler: an asyncio application, including one that uses FastAPI or aiohttp.- Framework-specific schedulers such as
TornadoSchedulerandTwistedScheduler: integrations for those event loops.
Use the scheduler and executor appropriate to the callable. A coroutine function requires compatible asynchronous execution; a background thread scheduler does not automatically await arbitrary coroutine functions.
Persist job definitions
The default memory store loses its definitions when the process stops. A SQLAlchemy job store persists them:
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.schedulers.background import BackgroundScheduler
sched = BackgroundScheduler(jobstores={
"default": SQLAlchemyJobStore(url="sqlite:///jobs.db")
})
sched.start()
This is configuration within a process that remains alive, not a complete standalone program. Persistent jobs need importable functions and serializable arguments. If startup code registers a job repeatedly, use a stable id and replace_existing=True.
Three settings address different problems:
max_instances=1limits concurrent executions of one job within the scheduler.coalesce=Truecombines eligible missed execution times into one run.misfire_grace_timelimits how late an execution may start relative to its scheduled time.
These controls do not make a job store into a distributed queue or guarantee recovery of every interrupted business operation.
Production considerations
Make task effects idempotent. In 3.x, do not share a job store between independent schedulers: use a dedicated scheduler process or another supported coordination architecture.
Implement timeouts and retries at the appropriate task or execution layer. They are not automatically provided by persisting the schedule.
Record the job ID, scheduled time, actual start, duration, and outcome. Keep business state for pending and incomplete work. If you use Guance log detection, choose a completion window that includes allowed runtime and collection delay, and test both a failed run and a late run.
FAQ
Should I use APScheduler or Celery?
APScheduler suits scheduled triggers in a controlled process. Celery provides distributed task execution with broker and worker infrastructure. They can work together: APScheduler triggers a job that submits durable work to Celery.
What survives a process crash?
A persistent store preserves job definitions, not the stack or intermediate state of an executing function. Recovery requires task-level checkpoints or a safe way to retry.
How do I run only one scheduler across several machines?
Enable it in one designated replica or run a dedicated single-replica scheduling service with an explicit failover procedure. The 3.x job store does not provide interprocess coordination.
See the APScheduler 3.x user guide.
Related reading
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan