Python tqdm: Add Progress Bars to Scripts

Published on: July 10, 2026
Reading time: 6 minutes
Barra de progresso com tqdm para scripts Python

Long-running scripts should tell users that work is still happening. Without feedback, a file conversion, download, data cleanup, or automation task may look frozen even when it is working normally. The Python tqdm library adds a progress bar to loops with very little code.

This guide covers installation, basic iterable wrapping, labels and units, manual updates, unknown totals, file processing, nested bars, pandas integration, downloads, logging, and production-friendly options. The examples use terminal progress bars, but tqdm also supports notebook and GUI-oriented integrations.

Progress indicators are especially useful in Python automation scripts and command-line tools. The Python CLI with argparse guide later in this batch shows how to combine progress with user-supplied options.

What Is tqdm?

tqdm wraps an iterable and displays completed items, total items, elapsed time, processing speed, and an estimated remaining time when a total is known. Its name comes from an abbreviation associated with progress in Arabic and the Spanish phrase meaning “I love you,” but in Python code you simply import tqdm.

The official tqdm documentation provides API details, and the tqdm PyPI page provides installation and release information.

Install tqdm

Create or activate a virtual environment, then install the package with the interpreter that will run your script:

python -m pip install tqdm

Verify the installation:

python -c "import tqdm; print(tqdm.__version__)"

The pip installation guide explains why python -m pip helps avoid installing into the wrong environment.

Your First Progress Bar

Wrap an iterable with tqdm():

import time
from tqdm import tqdm

for item in tqdm(range(100)):
    time.sleep(0.03)

The sleep call only simulates work. In a real program, the loop might resize an image, parse a file, call an API, or process one database record.

Add a Description

for item in tqdm(range(50), desc="Processing records"):
    time.sleep(0.04)

A short description tells the user what the bar represents. Keep it stable; changing a long label every iteration can make terminal output difficult to read.

Show Meaningful Units

The unit parameter replaces the generic iteration label:

files = ["a.csv", "b.csv", "c.csv"]

for filename in tqdm(files, desc="Converting", unit="file"):
    time.sleep(0.4)

For bytes, add unit_scale=True and often unit_divisor=1024:

total_bytes = 10_000_000

with tqdm(
    total=total_bytes,
    unit="B",
    unit_scale=True,
    unit_divisor=1024,
    desc="Writing",
) as progress:
    for chunk_size in [2_000_000] * 5:
        time.sleep(0.2)
        progress.update(chunk_size)

Use tqdm with Lists, Generators, and Files

Any iterable can be wrapped. A list normally provides its length automatically:

names = ["Ava", "Noah", "Mia", "Leo"]

for name in tqdm(names, desc="Greeting"):
    print(name)

A generator may not expose a total. Pass one when it is known:

def squares(limit):
    for number in range(limit):
        yield number * number


for value in tqdm(squares(1000), total=1000, desc="Calculating"):
    pass

The Python itertools guide covers other lazy iteration patterns that pair well with progress reporting.

Manual Progress Updates

Not every workflow is a simple for loop. Create a bar with total and call update(amount) after each completed unit.

import time
from tqdm import tqdm

steps = ["read", "validate", "transform", "save"]

with tqdm(total=len(steps), desc="Pipeline", unit="step") as progress:
    for step in steps:
        time.sleep(0.5)
        progress.set_postfix(current=step)
        progress.update(1)

The context manager closes the bar even if the block exits early. Update only after the corresponding work is complete, or the display may overstate progress.

Display Changing Metrics

set_postfix() adds values to the right side of the bar:

errors = 0

with tqdm(range(100), desc="Validating") as progress:
    for number in progress:
        if number % 17 == 0:
            errors += 1

        progress.set_postfix(errors=errors)

Frequent formatting has a cost. Update detailed metrics periodically when the loop body is extremely fast.

Write Messages Without Breaking the Bar

Ordinary print() output can collide visually with a live progress bar. Use tqdm.write() for occasional messages:

from tqdm import tqdm

for number in tqdm(range(20), desc="Checking"):
    if number == 7:
        tqdm.write("Special case found at 7")

For permanent records, use the Python logging module. A progress bar is temporary interface feedback; logs are durable diagnostic history.

Control Refresh Frequency

tqdm automatically limits screen updates. Important options include:

  • mininterval: minimum time between display refreshes.
  • miniters: minimum completed iterations between refreshes.
  • maxinterval: maximum time between refreshes in dynamic adjustment.
  • disable: turn output off.
  • leave: keep or remove the completed bar.
  • dynamic_ncols: adapt width to the terminal.
for item in tqdm(
    range(1_000_000),
    mininterval=0.5,
    dynamic_ncols=True,
    leave=False,
):
    result = item * 2

A bar may be unnecessary for a loop that finishes instantly. Use progress output for work where users benefit from status information.

Disable Progress in Non-Interactive Runs

Continuous integration, redirected output, and log files may not need animated bars. One simple approach checks whether the output stream is connected to a terminal:

import sys
from tqdm import tqdm

for item in tqdm(
    range(100),
    disable=not sys.stderr.isatty(),
):
    pass

You can also make a CLI flag such as --no-progress and pass its value to disable.

Nested Progress Bars

Use position when displaying more than one bar:

import time
from tqdm import tqdm

for batch in tqdm(range(3), desc="Batches", position=0):
    for item in tqdm(
        range(20),
        desc=f"Batch {batch + 1}",
        position=1,
        leave=False,
    ):
        time.sleep(0.02)

Nested bars can be helpful, but too many levels create clutter. Prefer one overall bar and one current-task bar.

Process Files with pathlib

from pathlib import Path
from tqdm import tqdm

source = Path("documents")
text_files = list(source.glob("*.txt"))

for path in tqdm(text_files, desc="Reading", unit="file"):
    content = path.read_text(encoding="utf-8")
    word_count = len(content.split())
    print(path.name, word_count)

Converting the glob result to a list gives tqdm a total. For a huge directory tree, materializing every path may use unnecessary memory; count in a separate pass only when an accurate total is worth the cost. The Python pathlib guide covers safe path operations.

Use tqdm with pandas

tqdm can register progress_apply() with pandas:

import pandas as pd
from tqdm.auto import tqdm

tqdm.pandas(desc="Normalizing")

data = pd.DataFrame({"name": ["  Ava ", "NOAH", " mia"]})
data["clean_name"] = data["name"].progress_apply(
    lambda value: value.strip().title()
)

print(data)

Use vectorized pandas operations when possible because they are often faster than apply(). A progress bar improves visibility; it does not make an inefficient operation faster.

Use tqdm.auto for Terminals and Notebooks

tqdm.auto selects an appropriate frontend in many environments:

from tqdm.auto import tqdm

for item in tqdm(range(100)):
    pass

In Jupyter environments, widget support may require the notebook environment and related packages to be configured correctly.

Download a File with a Progress Bar

The following example uses urllib.request from the standard library. It reads the response in chunks and updates by the number of bytes written.

from pathlib import Path
from urllib.request import urlopen
from tqdm import tqdm


def download_file(url, destination, chunk_size=64 * 1024):
    destination = Path(destination)

    with urlopen(url, timeout=30) as response:
        total = int(response.headers.get("Content-Length", 0))

        with destination.open("wb") as file, tqdm(
            total=total or None,
            unit="B",
            unit_scale=True,
            unit_divisor=1024,
            desc=destination.name,
        ) as progress:
            while chunk := response.read(chunk_size):
                file.write(chunk)
                progress.update(len(chunk))


# Use a URL you trust and a suitable destination path.
# download_file("https://example.com/file.zip", "file.zip")

A missing Content-Length header means the total is unknown. The bar can still show transferred bytes and speed without a percentage.

Complete Project: File Processing CLI

This script counts lines in text files and supports an optional quiet mode.

import argparse
import sys
from pathlib import Path
from tqdm import tqdm


def count_lines(path):
    with path.open("r", encoding="utf-8", errors="replace") as file:
        return sum(1 for _ in file)


def find_text_files(directory, recursive=False):
    pattern = "**/*.txt" if recursive else "*.txt"
    return sorted(directory.glob(pattern))


def build_parser():
    parser = argparse.ArgumentParser(
        description="Count lines in text files."
    )
    parser.add_argument("directory", type=Path)
    parser.add_argument(
        "--recursive",
        action="store_true",
        help="Search subdirectories.",
    )
    parser.add_argument(
        "--no-progress",
        action="store_true",
        help="Disable the progress bar.",
    )
    return parser


def main():
    args = build_parser().parse_args()

    if not args.directory.is_dir():
        raise SystemExit(f"Not a directory: {args.directory}")

    files = find_text_files(args.directory, args.recursive)
    disable_progress = args.no_progress or not sys.stderr.isatty()
    total_lines = 0

    for path in tqdm(
        files,
        desc="Counting",
        unit="file",
        disable=disable_progress,
    ):
        total_lines += count_lines(path)

    print(f"Files: {len(files)}")
    print(f"Lines: {total_lines}")


if __name__ == "__main__":
    main()

Run it with:

python line_counter.py documents --recursive

Common Mistakes

  • Wrapping enumerate(items) without passing a total when tqdm cannot infer one. Prefer enumerate(tqdm(items)) or provide total=len(items).
  • Calling update() with the final total instead of the amount completed since the previous update.
  • Using print() repeatedly while a bar is active.
  • Creating a new progress bar inside every iteration.
  • Materializing a huge generator only to calculate its length.
  • Leaving animated output enabled in a file or non-interactive log.
  • Assuming a progress bar improves processing performance.

Frequently Asked Questions

Is tqdm part of the standard library?

No. Install it from PyPI with python -m pip install tqdm.

Why does my bar have no percentage?

A percentage requires a known total. Pass total when you know the expected amount.

Can tqdm show bytes?

Yes. Use unit="B", unit_scale=True, and update by the number of bytes processed.

Can I use it in Jupyter?

Yes. tqdm.auto or tqdm.notebook can provide notebook-oriented output when widget support is available.

How do I hide the bar?

Pass disable=True, or connect it to a CLI option or terminal detection.

Does tqdm work with multiprocessing or asyncio?

The project provides helpers and patterns for concurrent and asynchronous tasks. Coordinate output carefully because several workers writing to one terminal can become difficult to read.

Conclusion

Python tqdm makes long operations easier to understand without changing their core logic. Wrap a known iterable for the simplest case, use a manual total for chunked work, label the unit clearly, and disable animated output where it does not belong.

A good progress bar reports real completed work and remains separate from durable logs. With those rules, tqdm improves command-line usability while keeping processing code readable.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Uso do módulo random para gerar valores aleatórios em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python random Module: Complete Beginner Guide

    Learn Python's random module: generate numbers, choose items, shuffle lists, sample data, use seeds, and know when to use secrets.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Manipulação moderna de arquivos usando pathlib em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python pathlib: Manage Files and Paths Easily

    Learn Python pathlib to create, inspect, read, write, rename, move, and delete files and folders with clean cross-platform code.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Manipulação de arquivos e pastas usando módulo os em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python os Module: Manage Files and Folders

    Learn the Python os module to list, create, rename, move, and remove files and folders, work with paths, and read

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Uso do Counter do collections para contar elementos em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python Counter: Count Items with collections

    Learn Python Counter to count items, find common values, update totals, compare counts, and solve practical frequency problems.

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    Como evitar KeyError usando defaultdict em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Avoid Python KeyError with defaultdict

    Learn how defaultdict prevents Python KeyError, when to use default_factory, how it compares with dict.get, setdefault, Counter, and common mistakes.

    Ler mais

    Tempo de leitura: 7 minutos
    28/05/2026
    Introdução ao módulo itertools para iniciantes em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python itertools: Practical Beginner Guide

    Learn Python itertools with practical examples of count, cycle, repeat, chain, combinations, permutations, groupby, tee, and memory-efficient iteration.

    Ler mais

    Tempo de leitura: 8 minutos
    28/05/2026