NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1213 most downloaded on PyPI
RQ is a simple, lightweight, library for creating background jobs, and processing them.
Last release 1 months ago
30 Aug 2026
Ships fairly regularly
a new release about every 5 weeks
Nearly every release is documented
notes for 54 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
13 years old
98 releases · first in 2013
Added CronJobRegistry so cron job histories can be monitored without an active CronScheduler. Thanks @selwin!
CronJobRegistry so cron job histories can be monitored without an active CronScheduler. Thanks @selwin!CronScheduler.heartbeat() now re-creates its Redis hash if they expire. Thanks @selwin!CronJob now keeps a history of jobs it created, accessible via cron_job.get_job_ids(). Thanks @selwin!
CronJob now keeps a history of jobs it created, accessible via cron_job.get_job_ids(). Thanks @selwin!RQScheduler now acquire and refresh locks before enqueueing. Thanks @selwin!CronJob now has a name. Thanks @selwin!rq worker-pool now supports custom exception handlers. Thanks @njits030!rq worker-pool now honors the DICT_CONFIG logging setting from config files. Thanks @razchiriac!Execution now stores the name of the worker running it. Thanks @selwin!create_cron() multiple times creates duplicate jobs. Thanks @selwin!SpawnWorker compatibility with redis-py >= 8.1. Thanks @b3n4kh!One column per quarter.
Added webhook notifications to notify external end points when a job finishes or fails. Thanks @selwin!
Job.create() now supports retry argument. Thanks @sethuvishal !
Job.create() now supports retry argument. Thanks @sethuvishal!redis-py >= 8. Thanks @selwin!Job.create() now supports retry argument. Thanks @sethuvishal!redis-py >= 8. Thanks @selwin!Added json and pickle shorthand aliases for serializers. These can now be used when creating queues/workers and with the --serializer CLI option. Than
json and pickle shorthand aliases for serializers. These can now be used when creating queues/workers and with the --serializer CLI option. Thanks @selwin!SpawnWorker does not use user supplied serializer. Thanks @selwin!Queue.parse_args() now returns a EnqueueArgs named tuple. Thanks @libmilos-so!DeferredJobRegistry. Thanks @selwin!SpawnWorker now uses repr() when reconstructing worker, job and queue identifiers in child processes. Thanks @selwin!Added support for unique jobs. Passing unique=True with job_id prevents duplicate jobs from being enqueued or scheduled. Thanks @selwin!
unique=True with job_id prevents duplicate jobs from being enqueued or scheduled. Thanks @selwin!Result now stores execution metadata (execution_id, execution_started_at and execution_ended_at). Thanks @selwin!Retry now supports enqueue_at_front=True, allowing retried jobs to be requeued at the front of the queue. Thanks @crazillagodzilla!job_id values may only contain letters, numbers, underscores and dashes. Thanks @selwin!DeferredJobRegistry is now scored by job creation time. Thanks @selwin!CLIENT LIST is not supported. Thanks @selwin and @djmaze!Formal support for Python 3.14. Thanks @selwin!
CronScheduler monitoring, you can now monitor each CronJob's latest and next scheduled enqueue time. Thanks @selwin!job.get_status() now reports the correct final state inside on_success / on_failure callbacks. Thanks @Fridayai700!Updated CronScheduler to accept job_timeout instead of timeout argument. Thanks @selwin !
CronScheduler to accept job_timeout instead of timeout argument. Thanks @selwin!CronScheduler.heartbeat() does not properly extend the key's TTL. Thanks @selwin!Nothing published for this version
Nothing published for this version
Worker will now automatically choose TimerDeathPenalty if UnixSignalDeathPenalty is not available. Thanks @selwin !
Worker will now automatically choose TimerDeathPenalty if UnixSignalDeathPenalty is not available. Thanks @selwin!CREATED Job status for jobs that are not enqueued not deferred. Thanks @selwin!Worker can now import Job and Queue classes from string. Thanks @selwin!Group.cleanup(). Thanks @dixoncrews-gdl!Added rq cron CLI command. Thanks @selwin!
rq cron CLI command. Thanks @selwin!job.cancel(remove_from_dependencies=True). Thanks @Marishka17!WorkerPool now accepts queue_class argument. Thanks @amonsh1!
WorkerPool now accepts queue_class argument. Thanks @amonsh1!redis-py=6.0.0. Thanks @selwin and @terencehonles!Don't log job description when log_job_description is set to False. Thanks @danilopeixoto!
log_job_description is set to False. Thanks @danilopeixoto!pubsub_thread may die in the background. Thanks @ankush!Fixes an issue running RQ on Windows. Thanks @selwin!
Added the feature to repeat jobs. Thanks @selwin!
Added SpawnWorker that uses multiprocessing.spawn to spawn worker processes. This makes RQ usable in operating systems without os.fork() like Windows.
SpawnWorker that uses multiprocessing.spawn to spawn worker processes. This makes RQ usable in operating systems without os.fork() like Windows. Thanks @selwin!StartedJobRegistry.cleanup() now properly creates job results. Thanks @OlegZv!WorkerPool status is never set to STARTED. Thanks @taleinat!Worker.monitor_work_horse() now properly handles InvalidJobOperation. Thanks @fancyweb!queue.enqueue_many now always registers the queue in RQ's queue registry. Thanks @eswolinsky3241!job.id must not contain :. Thanks @sanurielf!
job.id must not contain :. Thanks @sanurielf!job.ended_at should be set when job is run synchronously. Thanks @alexprabhat99!Group.all() now properly handles non existing group. Thanks @eswolinsky3241!ruff instead of black as formatter. Thanks @hongquan!Nothing published for this version
Fixed a bug that may cause jobs from intermediate queue to be moved to FailedJobRegistry. Thanks @selwin!
Added worker_pool.get_worker_process() to make WorkerPool easier to extend. Thanks @selwin!
worker_pool.get_worker_process() to make WorkerPool easier to extend. Thanks @selwin!Nothing published for this version
Fixed a bug that may cause a crash when cleaning intermediate queue. Thanks @selwin!
Nothing published for this version
Fixes a crash that happens if Redis connection uses SSL. Thanks @tchapi!
job.meta() is loaded using the wrong serializer. Thanks @gabriels1234!Added WorkerPool (beta) that manages multiple workers in a single CLI. Thanks @selwin!
WorkerPool (beta) that manages multiple workers in a single CLI. Thanks @selwin!Callback class that allows more flexibility in declaring job callbacks. Thanks @ronlut!--dequeue-strategy option to RQ's CLI. Thanks @ccrvlh!--max-idle-time option to RQ's worker CLI. Thanks @ronlut!--maintenance-interval option to RQ's worker CLI. Thanks @ronlut!rq info CLI command. Thanks @iggeehu!queue.enqueue_jobs() now properly account for job dependencies. Thanks @sim6!TimerDeathPenalty now properly handles negative/infinite timeout. Thanks @marqueurs404!Added work_horse_killed_handler argument to Worker. Thanks @ronlut!
work_horse_killed_handler argument to Worker. Thanks @ronlut!result_ttl is -1. Thanks @sim6!dequeue_timeout ignores worker_ttl. Thanks @ronlut!job.return_value() instead of job.result when processing callbacks. Thanks @selwin!Worker code more easily extendable. Thanks @lowercase00!RQ now stores multiple job execution results. This feature is only available on Redis >= 5.0 Redis Streams. Please refer to the docs for more info. Th
at_front argument when jobs are scheduled. Thanks @gabriels1234!job.worker_name after job is finished. Thanks @eswolinsky3241!queue.enqueue_many() now supports on_success and on on_failure arguments. Thanks @y4n9squared!
queue.enqueue_many() now supports on_success and on on_failure arguments. Thanks @y4n9squared!enqueue_at_front to Dependency() objects to put dependent jobs at the front when they are enqueued. Thanks @jtfidje!SETNAME command. Thanks @yilmaz-burak!Nothing published for this version
Failure callbacks are now properly called when job is run synchronously. Thanks @ericman93!
result_ttl=0. Thanks @selwin!ssl_cert_reqs argument to be passed to Redis. Thanks @mgcdanny!job.cancel() should also remove itself from registries. Thanks @joshcoden!daemon mode. Thanks @mik3y!FAILED and failure callbacks are now properly called when job is run synchronously. Thanks @ericman93!result_ttl=0. Thanks @selwin!ssl_cert_reqs argument to be passed to Redis. Thanks @mgcdanny!job.cancel() should also remove itself from registries. Thanks @joshcoden!daemon mode. Thanks @mik3y!You can now enqueue jobs from CLI. Docs here. Thanks @rpkak!
CanceledJobRegistry to keep track of canceled jobs. Thanks @selwin!cancel_job(job_id, enqueue_dependents=True) allows you to cancel a job while enqueueing its dependents. Thanks @joshcoden!job.get_meta() to fetch fresh meta value directly from Redis. Thanks @aparcar!job.exc_info. Thanks @selwin!Added success and failure callbacks. You can now do queue.enqueue(foo, on_success=do_this, on_failure=do_that). Thanks @selwin!
queue.enqueue(foo, on_success=do_this, on_failure=do_that). Thanks @selwin!queue.enqueue_many() to enqueue many jobs in one go. Thanks @joshcoden!Jobs that fail due to hard shutdowns are now retried. Thanks @selwin!
Scheduler now works with custom serializers. Thanks @alella!You can now declare multiple job dependencies. Thanks @skieffer and @thomasmatecki for laying the groundwork for multi dependency support in RQ.
RoundRobinWorker and RandomWorker classes to control how jobs are dequeued from multiple queues. Thanks @bielcardona!--serializer option to rq worker CLI. Thanks @f0cker!STOPPED job status so that you can differentiate between failed and manually stopped jobs. Thanks @dralley!clean_worker_registry() now works in batches of 1,000 jobs to prevent modifying too many keys at once. Thanks @AxeOfMen and @TheSneak!Added job.worker_name attribute that tells you which worker is executing a job. Thanks @selwin!
job.worker_name attribute that tells you which worker is executing a job. Thanks @selwin!send_stop_job_command() that tells a worker to stop executing a job. Thanks @selwin!JSONSerializer as an alternative to the default pickle based serializer. Thanks @JackBoreczky!RQScheduler running on Redis with ssl=True. Thanks @BobReid!Worker now properly releases scheduler lock when run in burst mode. Thanks @selwin!
Workers now listen to external commands via pubsub. The first two features taking advantage of this infrastructure are send_shutdown_command() and sen
send_shutdown_command() and send_kill_horse_command(). Thanks @selwin!job.last_heartbeat property that's periodically updated when job is running. Thanks @theambient!Fixed a deprecated import. Thanks @elmaghallawy!
FailedJobRegistry. Thanks @selwin!Fixes for Redis server version parsing. Thanks @selwin!
Failed jobs can now be retried. Thanks @selwin!
hset() on redis-py >= 3.5.0. Thanks @aparcar!Added job.get_position() and queue.get_job_position(). Thanks @aparcar!
job.get_position() and queue.get_job_position(). Thanks @aparcar!job.requeue() now returns the modified job. Thanks @ericatkin!Reverted changes to hmset command which causes workers on Redis server < 4 to crash. Thanks @selwin!
hmset command which causes workers on Redis server < 4 to crash. Thanks @selwin!Avoid deprecation warnings on redis-py >= 3.5.0. Thanks @bbayles!
pickle.HIGHEST_PROTOCOL for backward compatibility reasons. Thanks @bbayles!Custom serializer is now supported. Thanks @solababs!
delay() now accepts job_id argument. Thanks @grayshirt!--sentry-ca-certs and --sentry-debug parameters to rq worker CLI. Thanks @kichawa!StartedJobRegistry are given an exception info. Thanks @selwin!delay() now accepts job_id argument. Thanks @grayshirt!--sentry-ca-certs and --sentry-debug parameters to rq worker CLI. Thanks @kichawa!StartedJobRegistry are given an exception info. Thanks @selwin!Support for infinite job timeout. Thanks @theY4Kman!
__main__ file so you can now do python -m rq.cli. Thanks @bbayles!job_id is now passed to logger during failed jobs. Thanks @smaccona!queue.enqueue_at() and queue.enqueue_in() now supports explicit args and kwargs function invocation. Thanks @selwin!Job.fetch() now properly handles unpickleable return values. Thanks @selwin!
enqueue_at() and enqueue_in() now sets job status to scheduled. Thanks @coolhacker170597!
enqueue_at() and enqueue_in() now sets job status to scheduled. Thanks @coolhacker170597!RQScheduler logging configuration. Thanks @FlorianPerucki!This release also contains an alpha version of RQ's builtin job scheduling mechanism. Thanks @selwin!
--verbose or --quiet CLI arguments should override --logging-level. Thanks @zyt312074545!rq info where it doesn't show workers for empty queues. Thanks @zyt312074545!queue.enqueue_dependents() on custom Queue classes. Thanks @van-ess0!RQ and Python versions are now stored in job metadata. Thanks @eoranged!failure_ttl argument to job decorator. Thanks @pax0r!Added max_jobs to Worker.work and --max-jobs to rq worker CLI. Thanks @perobertson!
max_jobs to Worker.work and --max-jobs to rq worker CLI. Thanks @perobertson!--disable-job-desc-logging to rq worker now does what it's supposed to do. Thanks @janierdavila!StartedJobRegistry now properly handles jobs with infinite timeout. Thanks @macintoshpie!rq info CLI command now cleans up registries when it first runs. Thanks @selwin!procname with setproctitle. Thanks @j178!timeout argument on queue.enqueue() has been deprecated in favor of job_timeout. Thanks @selwin!
Backward incompatible changes:
job.status has been removed. Use job.get_status() and job.set_status() instead. Thanks @selwin!
FailedQueue has been replaced with FailedJobRegistry:
get_failed_queue() function has been removed. Please use FailedJobRegistry(queue=queue) instead.move_to_failed_queue() has been removed.RQ's custom job exception handling mechanism has also changed slightly:
FailedJobRegistry) can be disabled by doing Worker(disable_default_exception_handler=True).Worker names are now randomized. Thanks @selwin!
timeout argument on queue.enqueue() has been deprecated in favor of job_timeout. Thanks @selwin!
Sentry integration has been reworked:
RQ_SENTRY_DSN environment variable instead of SENTRY_DSN before instantiating Sentry integrationFixed Worker.total_working_time accounting bug. Thanks @selwin!
Compatibility with Redis 3.0. Thanks @dash-rai!
job_timeout argument to queue.enqueue(). This argument will eventually replace timeout argument. Thanks @selwin!job_id argument to BaseDeathPenalty class. Thanks @loopbio!SimpleWorker. Thanks @selwin!date_format and log_format arguments to Worker and rq worker CLI. Thanks @shikharsg!The async keyword argument will still work, but raises a DeprecationWarning. Thanks @dchevell!
async is a keyword in Python 3.7,
Queue(async=False) has been changed to Queue(is_async=False). The async
keyword argument will still work, but raises a DeprecationWarning. Thanks @dchevell!Worker now periodically sends heartbeats and checks whether child process is still alive while performing long running jobs. Thanks @Kriechi!
Worker now periodically sends heartbeats and checks whether child process is still alive while performing long running jobs. Thanks @Kriechi!Job.create now accepts timeout in string format (e.g 1h). Thanks @theodesp!worker.main_work_horse() should exit with return code 0 even if job execution fails. Thanks @selwin!job.delete(delete_dependents=True) will delete job along with its dependents. Thanks @olingerc!@job decorator now accepts description, meta, at_front and depends_on kwargs. Thanks @jlucas91 and @nlyubchich!
@job decorator now accepts description, meta, at_front and depends_on kwargs. Thanks @jlucas91 and @nlyubchich!Worker.all(queue=queue) and Worker.count(queue=queue).job.data and job.exc_info are now stored in compressed format in Redis.Fixed an issue where worker.refresh() may fail when birth_date is not set. Thanks @vanife!
worker.refresh() may fail when birth_date is not set. Thanks @vanife!Fixed an issue where worker.refresh() may fail when upgrading from previous versions of RQ.
worker.refresh() may fail when upgrading from previous versions of RQ.Worker statistics! Worker now keeps track of last_heartbeat, successful_job_count, failed_job_count and total_working_time. Thanks @selwin!
Worker statistics! Worker now keeps track of last_heartbeat, successful_job_count, failed_job_count and total_working_time. Thanks @selwin!Worker now sends heartbeat during suspension check. Thanks @theodesp!queue.delete() method to delete Queue objects entirely from Redis. Thanks @theodesp!--logging-level option to command line scripts. Thanks @jiajunhuang!Fixed an issue where job.save() may fail with unpickleable return value.
job.save() may fail with unpickleable return value.Replace job.id with Job instance in local _job_stack . Thanks @katichev!
job.id with Job instance in local _job_stack . Thanks @katichev!job.save() no longer implicitly calls job.cleanup(). Thanks @katichev!StopRequested worker.heartbeat(). Thanks @fate0!FailedQueue has been moved to rq.handlers.move_to_failed_queue. Thanks @yaniv-g!--path parameter. Thanks @kirill and @sjtbham!job.dependency slightly more efficient. Thanks @liangsijian!FailedQueue now returns jobs with the correct class. Thanks @amjith!Your coding agent can read these notes before it upgrades. Set up the MCP server →