How to sleep in Python
Learn how to pause your Python script. Explore methods, tips, real-world uses, and common error fixes for sleeping in Python.

Pausing a Python script is a common requirement for tasks like rate limits or process synchronization. The time.sleep() function provides a simple way to halt execution for a specified duration.
In this article, you'll explore techniques to use time.sleep() effectively. You will find practical tips, see real-world applications, and get advice on debugging to master script execution control in your projects.
Using time.sleep() for basic pauses
import time
print("Going to sleep for 2 seconds...")
time.sleep(2)
print("Woke up!")--OUTPUT--Going to sleep for 2 seconds...
Woke up!
To pause the script, you first need to import Python's built-in time module. This gives you access to its time-related functions. The key function here is time.sleep(), which suspends the execution of your script for a specified duration.
- The function takes one argument: the duration of the pause in seconds.
- In the example,
time.sleep(2)blocks the script for two full seconds before execution continues. - You can also pass a float, like
time.sleep(0.5), for sub-second delays.
Common sleep variations
Beyond the straightforward time.sleep(), Python offers more sophisticated tools for managing pauses, especially in asynchronous or multi-threaded applications.
Using asyncio.sleep() for asynchronous code
import asyncio
async def main():
print("Going to sleep asynchronously...")
await asyncio.sleep(1)
print("Async sleep completed!")
asyncio.run(main())--OUTPUT--Going to sleep asynchronously...
Async sleep completed!
For asynchronous code, using time.sleep() is a no-go because it freezes the entire program. Instead, you'll use asyncio.sleep(). This function is designed specifically for asynchronous environments and works with the async and await keywords to pause execution without blocking other operations.
- The key difference is that
asyncio.sleep()is non-blocking. It pauses the current task but allows Python's event loop to run other tasks in the meantime—making it essential for building responsive applications.
Using threading.Timer for non-blocking sleep
import threading
import time
def wake_up():
print("Timer completed!")
print("Starting a 2-second timer...")
timer = threading.Timer(2.0, wake_up)
timer.start()
print("Main thread continues running...")
time.sleep(2.5) # Wait for timer to complete--OUTPUT--Starting a 2-second timer...
Main thread continues running...
Timer completed!
For multi-threaded applications where you can't afford to block the main program, you can use threading.Timer. It schedules a function to run on a new thread after a set delay, which lets your main script continue its work without pausing.
- The
threading.Timer(2.0, wake_up)object prepares thewake_upfunction to run after a two-second delay. - Once you call
timer.start(), the timer begins in the background. The main thread isn't blocked and proceeds immediately to the next line of code.
Using threading.Event for interruptible sleep
import threading
import time
event = threading.Event()
print("Sleeping until event is set (max 3 seconds)...")
is_set = event.wait(timeout=3)
print(f"Wait completed: event was {'set' if is_set else 'not set'}")--OUTPUT--Sleeping until event is set (max 3 seconds)...
Wait completed: event was not set
For a more flexible, interruptible pause, you can use threading.Event. It's a synchronization tool that lets one thread signal an event to other threads. This is perfect for situations where you need to wait for something to happen but also want to avoid getting stuck forever.
- The
event.wait()method pauses the thread until another thread callsevent.set(). - By adding a
timeout, likeevent.wait(timeout=3), you set a maximum wait time. - The method returns
Trueif the event was set orFalseif it timed out, so your code knows why it woke up.
Advanced sleep techniques
You can also embed sleep logic into more sophisticated structures, such as context managers and decorators, or manage timeouts with the low-level signal module.
Creating a sleep context manager
import time
from contextlib import contextmanager
@contextmanager
def timed_execution(seconds):
yield
time.sleep(seconds)
with timed_execution(1):
print("This code executes immediately")
print("This code executes after the sleep")--OUTPUT--This code executes immediately
This code executes after the sleep
A context manager offers a clean way to wrap a block of code, and the @contextmanager decorator makes creating one simple. In this example, the code inside the with block runs immediately. It's only after that block finishes that the time.sleep() function is called, pausing the script.
- The
yieldkeyword is the pivot point. Code inside thewithstatement executes whereyieldis placed. - The code after
yield—in this case,time.sleep(seconds)—runs automatically upon exiting thewithblock.
Implementing a sleep decorator
import time
import functools
def sleep_after(seconds):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
time.sleep(seconds)
return result
return wrapper
return decorator
@sleep_after(1)
def greet():
return "Hello"
print(greet())
print("Printed after sleep")--OUTPUT--Hello
Printed after sleep
A decorator is a clean way to add extra behavior to a function without modifying its source code. The @sleep_after(1) decorator wraps the greet() function, but with a twist—it applies the pause after the function has already run and returned its result.
- When
greet()is called, it executes immediately. - The decorator then calls
time.sleep(1)before the program proceeds. - This pattern is perfect for tasks like rate-limiting, where you need a delay between consecutive function calls.
Using signal module for timeout control
import signal
import time
def timeout_handler(signum, frame):
raise TimeoutError("Operation timed out")
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(2) # Set alarm for 2 seconds
try:
print("Starting long operation...")
time.sleep(3) # This will be interrupted
print("This won't be printed")
except TimeoutError as e:
print(e)--OUTPUT--Starting long operation...
Operation timed out
The signal module offers a powerful, low-level way to enforce time limits, especially on blocking operations. It works by using system signals to interrupt your program's execution. This approach is particularly useful for operations that don't have a built-in timeout mechanism.
- You first register a handler function using
signal.signal()to respond to a specific signal likeSIGALRM. - Next,
signal.alarm()schedules that signal to be sent after a set number of seconds. - When the alarm triggers, your handler runs. In this example, it raises a
TimeoutErrorto break out of the long-running task.
Move faster with Replit
Replit is an AI-powered development platform where all Python dependencies come pre-installed, so you can skip setup and start coding instantly. Instead of piecing together techniques like time.sleep(), you can use Agent 4 to build complete applications from the ground up.
Describe the app you want to build, and the Agent will take it from an idea to a working product. Here are a few examples of what you could create:
- An API scraper that automatically adds delays with
time.sleep()to respect rate limits. - A background task scheduler that uses
threading.Timerto send a notification after a specific interval. - A monitoring utility that polls a service endpoint every few seconds, using
threading.Eventto wait for a status change or timeout.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
While pausing scripts is useful, you'll often run into a few common pitfalls that can trip up your code.
Forgetting to use await with asyncio.sleep()
A frequent mistake in asynchronous code is calling asyncio.sleep() without the await keyword. Doing so doesn't actually pause your program; it just creates a coroutine object and moves on, which means the intended delay never happens.
- Remember that
asyncio.sleep()is a coroutine. You must useawait asyncio.sleep()inside anasyncfunction to schedule the pause and yield control back to the event loop.
Handling interruptions during time.sleep()
The time.sleep() function isn't invincible—it can be interrupted by system signals. The most common example is when a user presses Ctrl+C, which sends a SIGINT signal and raises a KeyboardInterrupt exception, cutting the sleep short.
- To prevent your script from crashing unexpectedly, you can wrap the sleep call in a
try...except KeyboardInterruptblock. This allows you to catch the interruption and perform any necessary cleanup for a graceful exit.
Inefficient sleeping in loops
Using a fixed time.sleep() inside a loop for polling can lead to timing inaccuracies. If your loop's task takes 0.5 seconds and you sleep for 1 second, the total cycle time becomes 1.5 seconds, causing your timing to drift with each iteration.
- A more robust approach is to calculate the time remaining until the next desired execution. By subtracting the task's execution time from your target interval, you can sleep for the precise duration needed to maintain a consistent rhythm.
Forgetting to use await with asyncio.sleep()
It's easy to forget the await keyword when using asyncio.sleep(), creating a bug that's hard to spot. The script will execute without crashing, but the delay you wanted is silently skipped, potentially causing race conditions. See this error in action below.
import asyncio
async def task():
print("Starting task...")
asyncio.sleep(1) # Missing await!
print("Task completed!")
asyncio.run(task())
Without await, the asyncio.sleep(1) call creates a coroutine but never actually runs it. As a result, the script doesn't pause and prints both messages back-to-back. See how a small change fixes this issue below.
import asyncio
async def task():
print("Starting task...")
await asyncio.sleep(1) # Added await
print("Task completed!")
asyncio.run(task())
By adding the await keyword, you correctly pause the coroutine. This signals Python's event loop to suspend the task function for one second before resuming, ensuring "Task completed!" prints only after the delay. Always use await with asyncio.sleep() inside any async function to create a non-blocking pause.
Handling interruptions during time.sleep()
When a user presses Ctrl+C, it sends a KeyboardInterrupt that can stop time.sleep() mid-pause. You can catch this with a try...except block to avoid a crash. The example below shows exactly how your script responds to an interruption.
import time
print("Starting a long sleep (10 seconds)...")
try:
time.sleep(10) # Blocks for 10 seconds
print("Sleep completed")
except KeyboardInterrupt:
print("Sleep was interrupted") # Won't execute immediately
While the try...except block catches the KeyboardInterrupt, the script still terminates immediately. This abrupt exit can skip important cleanup tasks. See how to handle this more gracefully in the code below.
import time
print("Starting a long sleep (10 seconds)...")
try:
for _ in range(10):
time.sleep(1) # Sleep in smaller chunks
print("Sleep completed")
except KeyboardInterrupt:
print("Sleep was interrupted") # Executes sooner
By breaking a long pause into smaller chunks inside a loop, like time.sleep(1), your script becomes far more responsive. If a user triggers a KeyboardInterrupt by pressing Ctrl+C, the exception is caught much sooner—right after the current one-second interval finishes.
This allows your cleanup code in the except block to run almost immediately. It’s a simple change that ensures a graceful shutdown instead of an abrupt crash, giving your script time to save its state or close resources properly.
Inefficient sleeping in loops
Polling with a fixed time.sleep() in a loop often creates timing drift. The task's own execution time adds to the pause, making each cycle longer than intended. The code below demonstrates how this seemingly small inaccuracy can quickly accumulate.
import time
items = ["item1", "item2", "item3", "item4", "item5"]
for item in items:
process_time = 0.1 # Time to process each item
time.sleep(1) # Sleep 1 second between each item
print(f"Processed {item}")
Each loop cycle actually takes 1.1 seconds because the task's 0.1-second execution time adds to the one-second pause. This cumulative delay makes the timing drift. The following example shows how to compensate for this inaccuracy.
import time
items = ["item1", "item2", "item3", "item4", "item5"]
for item in items:
start = time.time()
process_time = 0.1 # Time to process each item
elapsed = time.time() - start
sleep_time = max(0, 1 - elapsed)
time.sleep(sleep_time)
print(f"Processed {item}")
To fix timing drift, you can calculate the sleep duration dynamically. Record the start time with time.time() before your task runs. After the task finishes, subtract its execution time from your target interval. Sleeping for the remaining time ensures each loop cycle stays on a consistent rhythm. This approach is crucial for any polling task where timing precision is important, preventing the small delays from accumulating over time.
Real-world applications
Now that you know the pitfalls, you can see how time.sleep() powers practical tools for API rate limiting or creating simple countdowns.
Creating a countdown timer with time.sleep()
You can build a simple countdown timer by placing time.sleep() inside a loop, creating a pause between each number displayed.
import time
def countdown(seconds):
print(f"Starting countdown: {seconds} seconds")
for remaining in range(seconds, 0, -2):
print(f"{remaining} seconds left")
time.sleep(2)
print("Countdown complete!")
countdown(6)
This countdown function creates a timed sequence using a for loop. The range(seconds, 0, -2) call is what drives the timer, generating numbers that start from the initial value and step down by two until they reach zero.
- In each iteration, the script prints the current number.
- After printing,
time.sleep(2)pauses the program for two full seconds.
This combination of looping and sleeping results in a simple, predictable timer that executes in discrete steps before printing its completion message.
Implementing API rate limiting with time.sleep()
Using time.sleep() is a straightforward way to implement rate limiting, ensuring your script respects an API's rules by pausing between requests.
import time
def fetch_with_rate_limit(urls, requests_per_second=1):
interval = 1.0 / requests_per_second
for i, url in enumerate(urls):
if i > 0: # Don't sleep before the first request
print(f"Rate limiting: waiting {interval:.1f} seconds...")
time.sleep(interval)
print(f"Fetching: {url}")
urls = ["https://api.example.com/1", "https://api.example.com/2"]
fetch_with_rate_limit(urls)
The fetch_with_rate_limit function manages how frequently your script processes a list of URLs. It calculates the required interval based on the requests_per_second argument, creating a specific delay between each action.
- The loop uses
enumerateto track the request count, and theif i > 0:condition prevents a pause before the first URL is fetched. - This ensures the delay from
time.sleep()only occurs between subsequent requests, making the process efficient by avoiding an unnecessary wait at the start.
Get started with Replit
Put these techniques into practice and build a real tool. Tell Replit Agent to “build a web scraper that respects rate limits” or “create a terminal pomodoro timer using time.sleep()”.
The Agent writes the code, tests for errors, and deploys your app directly from your browser. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



