Python progress bars

How to make a progress bar in Python

The quickest Python progress bar is tqdm: run pip install tqdm, then write for x in tqdm(items):. Version 4.70.1 needs Python 3.8 or newer. If you can't install packages, a short standard library function redraws one line with a carriage return and a flush. rich, alive-progress, click and progressbar2 are the other common picks.

5 min readtqdm and progress bars

At a glance

  • Fastest: pip install tqdm, then for x in tqdm(items): (tqdm 4.70.1, Python 3.8+).
  • No dependencies: write "\r" plus the bar to stdout and flush; a short version is below.
  • rich 15.0.0 has rich.progress.track(); click 8.5.0 has click.progressbar().
  • In a log file or pipe, a carriage-return bar becomes one long line of redraws, which some log viewers show as many lines.

The fastest answer: tqdm

tqdm is the default choice for most Python scripts. You import tqdm from tqdm, wrap your existing loop and it prints a working bar with a percentage, count, ETA and rate.

Python
# pip install tqdm
from time import sleep
from tqdm import tqdm

for item in tqdm(range(100), desc="working"):
    sleep(0.02)

Version 4.70.1 came out on 2026-09-11 under the MPL-2.0 AND MIT licences, runs on Python 3.8+ and has about 31,400 GitHub stars. Beyond basic loops it includes Jupyter widgets, progress_apply for pandas and process_map for worker pools. If your script might run in a cron job or write to a file, pass disable=None to turn the bar off when output isn't a terminal. The tqdm guide covers the parameters in detail.

A progress bar with only the standard library

A text progress bar works by redrawing the same line over and over. You write a carriage return ("\r") to move the cursor back to the start of the line, write the updated bar, then flush stdout so the terminal shows it right away. To size the bar, shutil.get_terminal_size() gives you the terminal width and falls back to 80 columns.

Python
import shutil
import sys
import time

def progress(done, total):
    width = max(10, shutil.get_terminal_size().columns - 20)
    filled = int(width * done / total)
    bar = "#" * filled + "-" * (width - filled)
    sys.stdout.write(f"\r[{bar}] {done}/{total} {done / total:4.0%}")
    sys.stdout.flush()
    if done == total:
        sys.stdout.write("\n")

for i in range(1, 101):
    time.sleep(0.02)
    progress(i, 100)

A short function like this is fine when you can't run pip install, but it won't handle a few things for you:

  • ETA: it shows how far along you are, not how much time is left.
  • Logs that are not a terminal: in a log file or a pipe, the carriage-return redraws pile up on one huge line or show as many separate lines.
  • Nested bars: an outer and an inner loop overwrite the same line and garble the output.

rich: coloured bars and several tasks

rich is a whole terminal formatting library, and progress bars are one feature in it. It's at version 15.0.0 (released 2026-04-12), uses the MIT licence, supports Python 3.9+ and has about 57,700 GitHub stars.

For a one-liner that feels like tqdm, import track from rich.progress and wrap your loop:

Python
import time
from rich.progress import track

for item in track(range(100), description="Working..."):
    time.sleep(0.02)

When you need several tasks on screen at once, use the Progress class instead of track.

alive-progress, click and progressbar2

Depending on what else your script does, one of these three can fit better than tqdm or rich.

alive-progress

alive-progress (3.3.0, released 2025-07-20, MIT licence, Python 3.9+, about 6,300 GitHub stars) focuses on animated bars and spinners. You can wrap an iterable with alive_it(items), or open a context manager with with alive_bar(total) as bar: and call bar() once per item.

Python
import time
from alive_progress import alive_bar

items = range(100)
with alive_bar(len(items)) as bar:
    for item in items:
        time.sleep(0.02)
        bar()

click

If your script is already a click command line tool, you don't need another dependency to show progress. click 8.5.0 (2026-08-26, BSD-3-Clause, Python 3.10+) has a built-in bar:

Python
import time
import click

with click.progressbar(range(100), label="Working") as bar:
    for item in bar:
        time.sleep(0.02)

progressbar2

progressbar2 is at version 4.6.0 (2026-08-14), under the BSD-3-Clause licence for Python 3.10+, with about 880 GitHub stars. The install name is progressbar2, but the import name in your code is progressbar. It can also show several bars at once with MultiBar.

Python
import time
import progressbar

for item in progressbar.progressbar(range(100)):
    time.sleep(0.02)

Which library to pick

Here is how the options compare on version, licence, Python requirement and where each one fits best:

LibraryLatest versionInstallLicencePythonGood for
tqdm4.70.1 (2026-09-11)pip install tqdmMPL-2.0 AND MIT3.8+Any loop, pandas, notebooks, worker pools
rich15.0.0 (2026-04-12)pip install richMIT3.9+Coloured output, several tasks at once
alive-progress3.3.0 (2025-07-20)pip install alive-progressMIT3.9+Animated bars and spinners
click8.5.0 (2026-08-26)pip install clickBSD-3-Clause3.10+Bars inside a click command line tool
progressbar24.6.0 (2026-08-14)pip install progressbar2BSD-3-Clause3.10+Custom widget layouts, several bars
Standard libraryBuilt inNothingPSFAny Python 3Scripts that can't add dependencies

If you just want a bar that works everywhere, pick tqdm. If you already use rich for coloured output or click for command line arguments, use the bar you already have installed. For a longer comparison with enlighten and spinners, see tqdm alternatives.

See the bar when you are away from the terminal

A terminal bar is only useful while you're looking at that terminal. When a job runs on a server or overnight, you need the progress somewhere else.

FAQ

How do I show a progress bar without installing anything?

Write a short standard library function that prints a carriage return ("\r") to move the cursor back to the start of the line, prints the updated bar and flushes stdout. shutil.get_terminal_size() fits the bar to the window and falls back to 80 columns.

Which progress bar library should I use for a simple script?

Use tqdm (pip install tqdm), which wraps any loop with for x in tqdm(items):. If your script already uses rich or click, rich.progress.track or click.progressbar gives you a bar without adding a package.

How do I show progress for a file download?

Use tqdm with unit="B", unit_scale=True and unit_divisor=1024, set total from the Content-Length header and call bar.update(len(chunk)) for each chunk you write. The bar then shows sizes in KB or MB and the transfer speed.

Why does my progress bar print a new line on every update?

The bar redraws its line with a carriage return, and not everything handles that: a log file or pipe stores every redraw on one long line, which some log viewers split into many lines, and a print() inside the loop pushes the bar down a line each time. In tqdm, use tqdm.write() instead of print(), and pass disable=None to turn the bar off when output isn't a terminal.

Do these progress bars work in Jupyter notebooks?

tqdm has a widget bar for Jupyter: import from tqdm.auto and install ipywidgets in the kernel's environment. rich's progress also works in notebooks with pip install "rich[jupyter]", but auto-refresh is off there, so you call refresh() or pass refresh=True to update().

Check on long runs from your phone

Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.

Get a live linkpip install tqdm-live

Sources