How to add a delay in Python
Learn how to delay in Python. This guide covers methods, tips, real-world uses, and debugging common errors to pause your code effectively.

To pause a Python script is a common need for tasks like rate limit control or process synchronization. Python’s time module offers simple functions like time.sleep() to manage execution timing.
In this article, you'll learn several techniques to pause your code. You'll get practical tips, see real-world applications, and receive debugging advice to implement delays effectively in your projects.
Basic delay with time.sleep()
import time
print("Starting...")
time.sleep(2) # Pauses execution for 2 seconds
print("Resumed after 2 seconds")--OUTPUT--Starting...
Resumed after 2 seconds
The time.sleep() function is the most straightforward way to pause your script. In the example, time.sleep(2) halts the program's execution for exactly two seconds before it continues to the final print() statement. This function is part of Python's standard time module, so you just need to import it to get started.
It's important to know that time.sleep() is a blocking call, which means it freezes the current thread of execution completely. While the script is sleeping, it won't perform any other tasks on that thread. The function accepts an integer or a float, so you can specify fractional seconds like time.sleep(0.5) for more granular control.
Alternative basic delay techniques
While time.sleep() is a solid starting point, you can also build more sophisticated delays with asynchronous tools, countdown timers, and function decorators.
Using asyncio.sleep() for asynchronous delays
import asyncio
async def main():
print("Starting...")
await asyncio.sleep(2) # Non-blocking sleep
print("Resumed after 2 seconds")
asyncio.run(main())--OUTPUT--Starting...
Resumed after 2 seconds
For asynchronous code, asyncio.sleep() is the non-blocking alternative to time.sleep(). It's a crucial tool for concurrent applications where you don't want to halt all operations during a pause.
- The
await asyncio.sleep(2)call pauses the current task without freezing the entire program. - While one task waits, the event loop can run other tasks, making your application more efficient.
- Once the two-second delay is complete, the program resumes the paused task right where it left off.
Creating a simple countdown timer
import time
def countdown(seconds):
for i in range(seconds, 0, -1):
print(f"{i}...")
time.sleep(1)
print("Done!")
countdown(3)--OUTPUT--3...
2...
1...
Done!
You can create a countdown timer by combining a for loop with time.sleep(). The countdown() function demonstrates this by iterating backward from a given number of seconds, printing each step along the way.
- The loop uses
range(seconds, 0, -1)to count down from the starting number to one. - Inside the loop,
time.sleep(1)creates the one-second interval between each number.
This technique is perfect for adding simple, user-facing timers to console applications or scripts.
Using decorators for function delays
import time
from functools import wraps
def delay_execution(seconds):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
time.sleep(seconds)
return func(*args, **kwargs)
return wrapper
return decorator
@delay_execution(2)
def greet():
print("Hello, world!")
greet()--OUTPUT--Hello, world!
Decorators offer a clean way to add behavior to functions without modifying their code. The delay_execution decorator wraps a function to introduce a pause. By applying @delay_execution(2) to the greet function, you're telling Python to wait two seconds before actually running it.
- The decorator intercepts the call to
greet(). - It executes
time.sleep()first. - Once the delay is over, it proceeds to run the original function's code.
This pattern keeps your delay logic separate and reusable across your project.
Advanced delay and timing techniques
When your application needs more than a simple pause, advanced techniques involving threading, events, and performance counters offer greater precision and responsiveness.
Non-blocking delays with threading
import threading
import time
def delayed_task():
print("Task will execute after 2 seconds")
timer = threading.Timer(2.0, delayed_task)
timer.start()
print("Main thread continues executing...")--OUTPUT--Main thread continues executing...
Task will execute after 2 seconds
The threading.Timer class lets you run a task after a delay without blocking your main program. It schedules a function to run on a new thread, so your script can continue with other work immediately.
- The
threading.Timer(2.0, delayed_task)object prepares thedelayed_taskfunction to run after two seconds. - Calling
timer.start()begins the countdown in the background. - Your main thread doesn't wait and continues its execution, which is why you see its message print first.
Using event-driven delays
import threading
event = threading.Event()
print("Starting...")
threading.Thread(target=lambda: event.wait(2) or print("Resumed after 2 seconds")).start()
print("Main thread continues...")--OUTPUT--Starting...
Main thread continues...
Resumed after 2 seconds
Event-driven delays use threading.Event to coordinate actions between threads without blocking your main program. An Event object works like a simple flag that one thread can set and others can wait for, making it great for synchronization.
- In this code, a new thread calls
event.wait(2), which pauses that specific thread for two seconds. - The main thread isn't affected and continues its tasks immediately, which is why its message prints first.
- Once the two-second timeout expires, the waiting thread resumes and prints its completion message.
Precise timing with performance counters
import time
start = time.perf_counter()
time.sleep(2)
elapsed = time.perf_counter() - start
print(f"Elapsed time: {elapsed:.6f} seconds")--OUTPUT--Elapsed time: 2.000123 seconds
For tasks that demand high precision, time.perf_counter() is your best tool. It returns a high-resolution time value that's ideal for measuring short durations. The code captures the time before and after time.sleep(2), then calculates the difference to find the actual elapsed time.
- This function is perfect for benchmarking code or measuring how long operations take.
- The elapsed time might be slightly over two seconds, as
time.sleep()guarantees a pause of at least the specified duration, not an exact one.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. While learning individual techniques is useful, Agent 4 helps you move faster by building complete applications from a simple description.
Instead of piecing together timing functions, you can describe the app you want to build and let the Agent take it from idea to working product:
- A rate-limiting utility that uses
time.sleep()to control API call frequency and stay within usage limits. - A scheduled task runner that executes a function after a specific delay using
threading.Timer, perfect for sending automated notifications. - A polite web scraper that uses
asyncio.sleep()to add non-blocking delays between requests and avoid getting blocked.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
When using delays in Python, you might run into a few common challenges, from cumulative drift to unresponsive scripts.
Dealing with cumulative delays in time.sleep() loops
A common pitfall with time.sleep() is cumulative delay drift in loops. If your loop performs work in addition to sleeping, the total time for each iteration will be the sleep duration plus the execution time of your code.
- Over many iterations, this small extra time adds up, causing your timing to drift significantly from the intended schedule.
- This is especially problematic in applications requiring long-term rhythmic execution, like a data logger that needs to sample every second on the second.
Fixing KeyboardInterrupt handling with time.sleep()
Another challenge is that time.sleep() can make your script unresponsive. Because it's a blocking call, it can prevent your program from immediately responding to a KeyboardInterrupt (Ctrl+C).
- If you try to stop a script during a long sleep, it won't exit until the sleep duration has elapsed.
- You can manage this by wrapping your sleep call in a
try...except KeyboardInterruptblock, allowing you to catch the signal and exit gracefully.
Debugging timer precision with time.sleep()
Finally, don't assume time.sleep() is perfectly precise. The function guarantees your code will pause for at least the specified duration, but it can be slightly longer.
- The operating system's scheduler determines when your code resumes, so system load can introduce small, unpredictable variations.
- As mentioned earlier, you can use
time.perf_counter()to measure the actual elapsed time and see how much it deviates from your requested delay.
Dealing with cumulative delays in time.sleep() loops
When you use time.sleep() in a loop, it's easy to forget that other operations also take time. This oversight can cause your loop's timing to drift, as the total duration of each cycle becomes longer than just the sleep interval. The following code demonstrates how these small delays accumulate, resulting in a noticeable timing error over just a few iterations.
import time
start = time.time()
for i in range(3):
print(f"Iteration {i}")
time.sleep(1)
# Do some processing
time.sleep(1)
print(f"Total time: {time.time() - start:.2f} seconds")
Each loop iteration takes over two seconds because it contains two separate time.sleep(1) calls. This extra processing time accumulates, causing the drift. The next example shows how to maintain a consistent interval by accounting for this.
import time
start = time.time()
target_interval = 2 # Total time each iteration should take
for i in range(3):
iteration_start = time.time()
print(f"Iteration {i}")
# Do some processing
time.sleep(1)
elapsed = time.time() - iteration_start
remaining = max(0, target_interval - elapsed)
time.sleep(remaining)
print(f"Total time: {time.time() - start:.2f} seconds")
To fix timing drift, you can calculate the sleep duration dynamically. This solution sets a target_interval for each loop cycle. It then measures how long your processing takes and sleeps only for the remaining time needed to hit that target, ensuring each iteration maintains a consistent rhythm.
- The
max(0, ...)function prevents errors if your processing takes longer than the interval itself. - This approach is crucial for tasks like polling an API or collecting sensor data where precise timing matters.
Fixing KeyboardInterrupt handling with time.sleep()
A long time.sleep() duration can make your script feel frozen. It won't respond to a KeyboardInterrupt (Ctrl+C) until the pause is complete, making it difficult to stop gracefully. The following code demonstrates how a script can get stuck.
import time
print("Starting a long process...")
try:
time.sleep(30) # Very long sleep
print("Process completed")
except:
print("An error occurred")
Although this code uses a try...except block, it won't catch a KeyboardInterrupt until the 30-second pause from time.sleep(30) is complete. The following example demonstrates a more responsive way to handle user interruptions during a pause.
import time
print("Starting a long process...")
try:
time.sleep(30) # Very long sleep
print("Process completed")
except KeyboardInterrupt:
print("Process interrupted by user")
except Exception as e:
print(f"An error occurred: {e}")
This solution works because it specifically catches the KeyboardInterrupt exception. When you press Ctrl+C, Python raises this exception. The try...except block intercepts it, allowing your script to print a message and exit cleanly instead of getting stuck.
- You should use this pattern for any script with long pauses, as it ensures you can always stop it manually.
Debugging timer precision with time.sleep()
While time.sleep() is great for general pauses, it's not built for high-precision timing. The operating system's scheduler can introduce small, unpredictable delays. The following code attempts to create several 10ms delays to show how much the actual pause can vary.
import time
def precise_delay(seconds):
time.sleep(seconds)
# Attempting precise 10ms delays
for _ in range(5):
start = time.time()
precise_delay(0.01)
print(f"Actual delay: {(time.time() - start)*1000:.2f}ms")
The output shows the actual delay from time.sleep(0.01) isn't consistent. The operating system adds unpredictable overhead, making the pause unreliable for precision timing. The following example offers a more accurate way to manage short delays.
import time
def precise_delay(seconds):
start = time.perf_counter()
while time.perf_counter() - start < seconds:
pass # Busy waiting for very short intervals
# More precise 10ms delays
for _ in range(5):
start = time.perf_counter()
precise_delay(0.01)
print(f"Actual delay: {(time.perf_counter() - start)*1000:.2f}ms")
This solution, known as a busy-wait loop, offers greater precision by continuously checking a high-resolution clock with time.perf_counter() until the specified duration has passed. It avoids yielding control to the OS, giving you tighter control over the timing.
- This method is more accurate for very short delays where
time.sleep()is unreliable. - Use it cautiously, as it consumes 100% CPU. It’s best for brief, critical timing tasks where precision is non-negotiable.
Real-world applications
Beyond debugging timing issues, these delay methods are essential for creating applications that interact respectfully with external services.
Using time.sleep() for API rate limiting
Using time.sleep() is a straightforward way to add a polite pause between API calls, ensuring your script respects rate limits and avoids overwhelming a server.
import time
import random
def api_request(endpoint):
# Simulate API call
print(f"Requesting data from {endpoint}")
return f"Data from {endpoint}"
# Make 3 rate-limited API requests
for i in range(3):
response = api_request(f"api/endpoint/{i}")
print(f"Received: {response}")
time.sleep(1) # Limit to 1 request per second
This code shows how to manage the frequency of repeated tasks. The script simulates making three consecutive API requests inside a for loop.
- After each simulated request, the
time.sleep(1)function is called. - This intentionally pauses the script's execution for one second before the next loop iteration begins.
This technique is essential for controlling how often your code interacts with external services, preventing you from sending too many requests in a short amount of time and potentially getting blocked.
Building a polite web scraper with adaptive delays
You can build a more sophisticated scraper by using randomized delays with time.sleep() to make your request patterns less predictable and more respectful to the server.
import time
import random
def fetch_page(url):
print(f"Fetching content from {url}")
# Simulate network latency
processing_time = random.uniform(0.1, 0.5)
time.sleep(processing_time)
return f"Content from {url}"
websites = ["example.com/page1", "example.com/page2", "example.com/page3"]
for site in websites:
content = fetch_page(site)
print(f"Processed {len(content)} characters")
# Adaptive delay based on site response time
delay = random.uniform(1.0, 3.0)
print(f"Waiting {delay:.2f}s before next request")
time.sleep(delay)
This script demonstrates how to build a polite web scraper by varying the time between requests. It iterates through a list of websites, fetching each one and then pausing for a random duration before moving to the next.
- The
random.uniform(1.0, 3.0)function generates a random float, creating an unpredictable delay between one and three seconds. - Calling
time.sleep()with this random value makes the scraper’s behavior appear more human-like. This helps avoid triggering anti-bot measures on servers and is a common courtesy when scraping websites.
Get started with Replit
Put these timing techniques into practice with Replit Agent. Describe what you want to build, like “a script that checks an API every 5 seconds” or “a simple pomodoro timer using time.sleep()”.
Replit Agent writes the code, tests for errors, and helps you deploy your application. 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.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

.png)
.png)
.png)