{"id":233,"library":"APScheduler","title":"APScheduler","description":"Advanced Python Scheduler — schedule Python functions to run at specified times or intervals. Current stable version is 3.11.2. A completely rewritten 4.x is in pre-release with a different API (apscheduler.Scheduler instead of BackgroundScheduler, etc.) — 4.x is NOT installed by pip install APScheduler. Two concurrent stable lines: use 3.x for production. The 3.x vs 4.x API confusion is the #1 footgun.","status":"active","version":"3.11.2","language":"python","source_language":"en","source_url":"https://apscheduler.readthedocs.io/en/3.x/","tags":["scheduling","cron","interval","background-jobs","periodic-tasks"],"install":[{"cmd":"pip install APScheduler","lang":"bash","label":"Installs 3.x stable (NOT 4.x)"},{"cmd":"pip install APScheduler[sqlalchemy]","lang":"bash","label":"With SQLAlchemy persistent job store"},{"cmd":"pip install APScheduler[redis]","lang":"bash","label":"With Redis job store"}],"dependencies":[{"reason":"Required for timezone support. Installed automatically.","package":"tzlocal>=2.0","optional":false},{"reason":"Required for timezone handling. Installed automatically.","package":"pytz","optional":false},{"reason":"Required for SQLAlchemyJobStore. Install via APScheduler[sqlalchemy].","package":"sqlalchemy>=1.4","optional":true},{"reason":"Required for RedisJobStore. Install via APScheduler[redis].","package":"redis","optional":true}],"imports":[{"note":"pip install APScheduler installs 3.x. The 4.x API (from apscheduler import Scheduler) requires a pre-release install. They are completely different — 4.x removed BackgroundScheduler, BlockingScheduler, AsyncIOScheduler in favor of a single Scheduler class.","wrong":"# 4.x API (pre-release, NOT installed by pip install APScheduler):\nfrom apscheduler import Scheduler  # ImportError on 3.x install\nwith Scheduler() as scheduler:\n    scheduler.add_schedule(my_function, IntervalTrigger(seconds=30))\n    scheduler.run_until_stopped()","symbol":"BackgroundScheduler","correct":"from apscheduler.schedulers.background import BackgroundScheduler\nfrom apscheduler.triggers.cron import CronTrigger\nfrom apscheduler.triggers.interval import IntervalTrigger\n\n# 3.x API — the current stable API\nscheduler = BackgroundScheduler()\n\n# Add jobs\nscheduler.add_job(my_function, 'interval', seconds=30)\nscheduler.add_job(my_function, CronTrigger(hour=9, minute=0))  # 9am daily\nscheduler.add_job(my_function, 'cron', hour=9, minute=0)       # same\n\n# Must call start() explicitly\nscheduler.start()\n\n# Shutdown cleanly\nimport atexit\natexit.register(lambda: scheduler.shutdown())"}],"quickstart":{"code":"from apscheduler.schedulers.background import BackgroundScheduler\nfrom datetime import datetime\nimport time\n\ndef job_function():\n    print(f'Job ran at {datetime.now()}')\n\n# BackgroundScheduler runs in a daemon thread\nscheduler = BackgroundScheduler()\n\n# Interval trigger\nscheduler.add_job(job_function, 'interval', seconds=10, id='my_job')\n\n# Cron trigger (every day at 9:30am)\nscheduler.add_job(job_function, 'cron', hour=9, minute=30)\n\n# Date trigger (one-off)\nfrom datetime import timedelta\nscheduler.add_job(\n    job_function,\n    'date',\n    run_date=datetime.now() + timedelta(minutes=1)\n)\n\n# Must start explicitly\nscheduler.start()\nprint('Scheduler started. Press Ctrl+C to exit.')\n\ntry:\n    while True:\n        time.sleep(1)\nexcept KeyboardInterrupt:\n    scheduler.shutdown()","lang":"python","description":"3.x stable API. BackgroundScheduler runs in a thread. Always call start() and shutdown()."},"warnings":[{"fix":"For production: pip install APScheduler installs stable 3.x. For 4.x pre-release: pip install 'APScheduler>=4.0a1'. The 3.x and 4.x APIs are completely incompatible.","message":"APScheduler 4.x (a complete rewrite) is in pre-release and has a completely different API. pip install APScheduler installs 3.x — NOT 4.x. 4.x docs and tutorials showing from apscheduler import Scheduler will raise ImportError on 3.x installs.","severity":"breaking","affected_versions":"4.x pre-release vs 3.x stable"},{"fix":"Always call scheduler.start() after adding jobs. For Flask/Django: call it in app startup, not module level.","message":"scheduler.start() must be called explicitly. Creating a scheduler and adding jobs without calling start() does nothing — jobs never run.","severity":"gotcha","affected_versions":"3.x"},{"fix":"Configure a persistent job store: scheduler = BackgroundScheduler(jobstores={'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')})","message":"Jobs are lost on restart unless a persistent job store (SQLAlchemy, Redis, MongoDB) is configured. The default MemoryJobStore holds jobs only in RAM.","severity":"gotcha","affected_versions":"3.x"},{"fix":"Run the scheduler in a single dedicated process. Use a persistent job store with coalesce=True and max_instances=1. Or use a task queue (Celery, RQ) for multi-process environments.","message":"Running multiple processes each with their own scheduler (e.g. gunicorn with multiple workers) causes duplicate job execution. APScheduler 3.x has no inter-process coordination.","severity":"gotcha","affected_versions":"3.x"},{"fix":"Web apps: use BackgroundScheduler. Standalone scheduler scripts: use BlockingScheduler. Async apps: use AsyncIOScheduler.","message":"BlockingScheduler blocks the main thread — use it only when the scheduler IS the application. BackgroundScheduler runs in a daemon thread and is suitable for running alongside a web app.","severity":"gotcha","affected_versions":"3.x"},{"fix":"Always set timezone: BackgroundScheduler(timezone='UTC') or use pytz: BackgroundScheduler(timezone=pytz.timezone('America/New_York'))","message":"All times are naive (timezone-unaware) unless you configure a timezone. Scheduled jobs may run at wrong times during DST transitions.","severity":"gotcha","affected_versions":"3.x"}],"env_vars":null,"last_verified":"2026-05-12T12:09:51.137Z","next_check":"2026-06-27T00:00:00.000Z","problems":[{"fix":"Rename or remove the 'apscheduler.py' file in your project directory to avoid naming conflicts with the APScheduler package.","cause":"This error occurs when a script named 'apscheduler.py' in the project directory shadows the APScheduler package, causing import conflicts.","error":"ModuleNotFoundError: No module named 'apscheduler.schedulers'; 'apscheduler' is not a package"},{"fix":"Replace 'add_cron_job' with 'add_job' and specify 'trigger='cron'' in your code.","cause":"The 'add_cron_job' method was removed in APScheduler 3.x; the correct method to add cron jobs is 'add_job' with 'trigger='cron''.","error":"AttributeError: 'BackgroundScheduler' object has no attribute 'add_cron_job'"},{"fix":"Install APScheduler using 'pip install apscheduler'.","cause":"This error indicates that the APScheduler package is not installed in the Python environment.","error":"ImportError: No module named 'apscheduler'"},{"fix":"Import the specific scheduler class from its correct submodule, such as `from apscheduler.schedulers.background import BackgroundScheduler` or `from apscheduler.schedulers.blocking import BlockingScheduler`.","cause":"Users are attempting to import the `Scheduler` class directly from the `apscheduler` module, which was a pattern in APScheduler 2.x but is incorrect for the 3.x series. In APScheduler 3.x, scheduler classes are located in specific submodules.","error":"AttributeError: module 'apscheduler' has no attribute 'Scheduler'"},{"fix":"Pass the function reference without parentheses: `scheduler.add_job(my_function, 'interval', seconds=5)`.","cause":"The function intended to be scheduled is being called immediately when passed to `add_job` (e.g., `my_function()`) instead of passing a reference to the function itself (`my_function`). This results in the return value of `my_function()` being passed, which is likely not a callable object.","error":"TypeError: func must be callable"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.28,"mem_mb":7.7,"disk_size":"18.5M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.29,"mem_mb":7.7,"disk_size":"22.3M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.29,"mem_mb":7.7,"disk_size":"43.2M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.2,"mem_mb":7.7,"disk_size":"19M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.19,"mem_mb":7.7,"disk_size":"23M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.19,"mem_mb":7.7,"disk_size":"42M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.4,"mem_mb":8.9,"disk_size":"20.4M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.39,"mem_mb":8.9,"disk_size":"25.0M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.39,"mem_mb":8.9,"disk_size":"48.3M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.31,"mem_mb":8.9,"disk_size":"21M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.32,"mem_mb":8.9,"disk_size":"25M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.34,"mem_mb":8.9,"disk_size":"47M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.63,"mem_mb":12,"disk_size":"12.3M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.57,"mem_mb":12,"disk_size":"16.7M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.57,"mem_mb":12,"disk_size":"39.7M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":12,"disk_size":"13M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":12,"disk_size":"17M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":12,"disk_size":"38M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.6,"mem_mb":12.4,"disk_size":"11.9M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.58,"mem_mb":12.4,"disk_size":"16.3M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.58,"mem_mb":12.4,"disk_size":"39.1M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":12.4,"disk_size":"12M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.55,"mem_mb":12.4,"disk_size":"17M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.55,"mem_mb":12.4,"disk_size":"38M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.27,"mem_mb":7.5,"disk_size":"18.0M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.27,"mem_mb":7.5,"disk_size":"21.2M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.28,"mem_mb":7.5,"disk_size":"41.8M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.22,"mem_mb":7.5,"disk_size":"18M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"redis","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.21,"mem_mb":7.5,"disk_size":"22M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"sqlalchemy","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.23,"mem_mb":7.5,"disk_size":"41M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":-1},{"runtime":"python:3.10-slim","exit_code":-1},{"runtime":"python:3.11-alpine","exit_code":-1},{"runtime":"python:3.11-slim","exit_code":-1},{"runtime":"python:3.12-alpine","exit_code":-1},{"runtime":"python:3.12-slim","exit_code":-1},{"runtime":"python:3.13-alpine","exit_code":-1},{"runtime":"python:3.13-slim","exit_code":-1},{"runtime":"python:3.9-alpine","exit_code":-1},{"runtime":"python:3.9-slim","exit_code":-1}]}}