Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
tqdm adds a live progress meter to Python loops, manual tasks, notebooks, asynchronous work, and shell pipelines. Wrap an iterable with tqdm(...) for a quick bar; use a manual bar when you need to report completed work yourself. The latest release listed on PyPI as checked August 18, 2026, is 4.70.0, uploaded July 27, 2026. PyPI package and release details
What does tqdm do?
tqdm is a Python library and command-line utility for displaying progress as work runs. It can wrap an iterable without changing the normal iteration pattern, count manually reported work, and integrate with common tools such as Jupyter, Pandas, and asyncio. When it can determine a total, it estimates the completion percentage, elapsed time, remaining time, and processing rate.
Progress output normally goes to stderr, leaving stdout available for program output and shell pipelines. It reports activity in the process where it runs; it is not a job queue, profiler, distributed monitor, durable task tracker, or web dashboard. See the core API documentation.
Install tqdm
Use python -m pip to install into the Python interpreter selected by python, especially if your machine has multiple Python installations:
#1 Best Overall
python -m pip install tqdm
The project also documents pip install tqdm and conda install -c conda-forge tqdm. Official project documentation
Check the installed version with:
python -c "import tqdm; print(tqdm.__version__)"
For a reproducible environment, pin the version you intend to use. The version below was current as checked on August 18, 2026; it should not be treated as permanently current:
python -m pip install "tqdm==4.70.0"
Add a bar to a Python loop
Import tqdm and wrap the iterable:
from tqdm import tqdm
import time
for item in tqdm(range(100), desc="Processing"):
time.sleep(0.05)
process(item)
When the iterable has a length, tqdm can infer the total. The bar typically shows the completed and total counts, percentage, elapsed time, estimated time remaining, and rate. An ETA is an estimate based on observed throughput, not a completion promise.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Common display options include desc for a label, unit for the counting unit, total for an explicit expected count, leave to retain or remove a completed bar where supported, disable to suppress it, position to assign a display row, and ncols to set width. For example:
for chunk in tqdm(chunks, desc="Uploading", unit="chunk", leave=False):
upload(chunk)
Use trange(n) when iterating over a numeric range; it is shorthand for tqdm(range(n)):
from tqdm import trange
for i in trange(100, desc="Items"):
work(i)
See the API reference for the full set of options.
Track progress manually
Use a manual bar when work does not map cleanly to one iterable, or when the amount completed differs from one loop iteration. Set a total in the same unit you will pass to update():
from tqdm import tqdm
with tqdm(total=100, desc="Uploading", unit="MB") as bar:
for chunk in chunks:
upload(chunk)
bar.update(len(chunk))
The context manager closes the bar reliably, including when an exception interrupts the block. If you create a bar without with, call bar.close() when finished.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →For a stream with no known length, omit the total and update as items are consumed:
Rank #2
with tqdm(desc="Reading", unit="items") as bar:
for item in stream:
consume(item)
bar.update(1)
Without a total, the bar can show counts and rate but cannot give a meaningful percentage or ETA. The same limitation applies to infinite or otherwise unmeasurable work.
Use tqdm with generators and streams
Generators commonly have no length, so a wrapped generator may show counts without a percentage:
def records():
yield from source()
for record in tqdm(records(), desc="Reading records"):
process(record)
If another part of the program knows the expected count, pass it explicitly:
for record in tqdm(records(), total=expected_records):
process(record)
The supplied total must describe the same work counted by iteration. A guessed or incorrect total makes percentages and ETAs misleading. For byte streams, ensure the total counts the same bytes that pass through the bar.
Choose the right output for notebooks
In a terminal script, from tqdm import tqdm is the usual import. Use tqdm.notebook when you explicitly want the notebook widget display:
from tqdm.notebook import tqdm
for item in tqdm(items, desc="Notebook work"):
process(item)
For code that may run in either a notebook or a terminal, use the automatic frontend selection:
from tqdm.auto import tqdm
tqdm.auto is a convenience, not a guarantee that every notebook frontend renders identically. A notebook bar may remain in the cell where it was created rather than follow later output; for long-lived bars, resetting or delaying display can help. The project distinguishes tqdm.notebook and tqdm.auto in its documentation.
Free tools Windows power users keep installed
One-click scans. No signup required.
Show progress for Pandas operations
Register the progress methods before calling them:
import pandas as pd
from tqdm import tqdm
tqdm.pandas(desc="Applying")
df["result"] = df["value"].progress_apply(expensive_function)
The integration also provides progress variants for mapping and grouped operations. The bar counts calls to the applied function; it does not reveal how far that function has progressed internally. It also does not parallelize Pandas. If a vectorized operation can replace row-by-row work, that may be preferable, and for very fast functions the display overhead can be noticeable. Details are in the project documentation.
Track asyncio work
For an asynchronous source, wrap the asynchronous iterator with the asyncio version:
from tqdm.asyncio import tqdm
import asyncio
async def main():
async for item in tqdm(async_source(), desc="Async work"):
await process(item)
asyncio.run(main())
For a collection of awaitables, tqdm.asyncio also supplies a gather wrapper:
from tqdm.asyncio import tqdm
results = await tqdm.gather(
fetch_one(),
fetch_two(),
fetch_three(),
desc="Fetching",
)
The module also supports progress over asyncio.as_completed(). Its asyncio documentation describes the available wrappers. The project warns that breaking out of an asynchronous iterator is not currently caught automatically; arrange explicit cleanup or context-manager handling when a loop may end early. Project documentation
Handle nested and parallel progress bars
Nested loops
Use leave=False for a temporary inner bar and assign fixed rows with position if bars need separate terminal lines:
from tqdm.auto import trange
for epoch in trange(3, desc="Epochs"):
for batch in trange(100, desc="Batches", leave=False):
train(batch)
dynamic_ncols=True can adapt the bar to terminal width. Nested displays may still be difficult to read in redirected logs, CI output, terminals without carriage-return support, or notebook frontends with incompatible rendering.
Multiprocessing
Decide what the bar represents. A single bar around input consumed by a pool measures submitted or completed tasks depending on where it is updated; one bar per worker shows worker-level activity but can make shared terminal output hard to read. A parent-process bar is often the clearest choice.
When worker bars are useful, coordinate terminal writes with positions and a shared lock, following the project’s multiprocessing pattern:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
from multiprocessing import Pool, RLock, freeze_support
from tqdm import trange, tqdm
def worker(n):
for _ in trange(1000, desc=f"Worker {n}", position=n):
pass
if __name__ == "__main__":
freeze_support()
tqdm.set_lock(RLock())
with Pool(
initializer=tqdm.set_lock,
initargs=(tqdm.get_lock(),),
) as pool:
pool.map(worker, range(4))
The current release history for 4.70.0 records changes to process_map and thread_map, including worker defaults, timeout and buffer support, ETA calculation, and an interpreter_map addition. Consult the release history for version-specific details. Progress display does not make multiprocessing safe: synchronization, exceptions, shutdown, ordering, and shared state remain application responsibilities.
Keep messages from disrupting the bar
Ordinary print() output can overwrite or split an active bar. Use tqdm.write() for a message that should be displayed cleanly:
from tqdm import tqdm
tqdm.write("Checkpoint saved")
For logging output, the project provides a redirect helper:
from tqdm.contrib.logging import logging_redirect_tqdm
with logging_redirect_tqdm():
logger.info("A message that should not overwrite the bar")
The project also documents redirecting standard output and error; restore redirected streams after the bar closes and follow the documented order when combining stream and logging redirects. Project documentation
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteUse tqdm in a shell pipeline
The command-line utility can pass standard input through while showing progress separately. For a simple countable stream:
seq 1000000 | python -m tqdm > /dev/null
For a byte-oriented archive stream, provide the expected number of bytes:
tar -czf - data/
| tqdm --bytes --total "$(du -sb data/ | cut -f1)"
> backup.tar.gz
The percentage and ETA require a total that matches the bytes actually passing through the bar. These examples use Unix-style commands such as seq, du, and cut; they are not portable to every shell or operating system, and Windows users may need PowerShell equivalents. The project README documents CLI use.
Control refresh rate and overhead
Every refresh writes output, so a bar that redraws too often can add overhead or make output noisy, particularly around very fast operations. Set mininterval to limit how often it refreshes:
Recommended Free Tools
for item in tqdm(items, mininterval=0.5):
fast_operation(item)
miniters controls the minimum iteration interval between refreshes. Use disable=True to turn off display, leave=False to remove completed bars where supported, and dynamic_ncols=True to adapt to terminal width. For example, suppress bars when standard error is not an interactive terminal:
Best Value
import sys
from tqdm import tqdm
show_progress = sys.stderr.isatty()
for item in tqdm(items, disable=not show_progress):
process(item)
The maintainers report approximately 60 nanoseconds per iteration for the standard implementation and approximately 80 nanoseconds for the GUI variant, compared with approximately 800 nanoseconds for the referenced ProgressBar implementation. These are project-reported figures, not an independent benchmark. Actual overhead depends on refresh frequency, terminal, output destination, iterable speed, and program structure. PyPI project description
Troubleshoot inaccurate or messy bars
No bar appears
Check whether disable=True is set, output is redirected or captured, the iterable is empty, the program exits before a refresh, or the frontend does not support the selected rendering. In a terminal, forcing refreshes can help diagnose a short loop:
for item in tqdm(items, disable=False, mininterval=0):
process(item)
In a notebook, try from tqdm.notebook import tqdm if the standard display is unsuitable.
The percentage is wrong or never reaches 100%
Check whether total matches the actual number of updates and whether each update(n) uses the same unit as the total. Updating twice for one logical item, processing multiple records in one iteration, or using an incorrect generator length can all distort the result.
The ETA jumps around
Estimated time remaining depends on observed rate. It can be unstable when early items are unusually slow, item costs vary, I/O pauses occur, work completes in bursts across workers, or the total is only a guess. Use a unit that reflects completed work and treat ETA as an estimate.
Output is garbled or fills logs
Replace active-loop print() calls with tqdm.write(), use logging_redirect_tqdm() for logs, and set position plus a shared lock when coordinating worker bars. For noninteractive output, increase refresh spacing or disable the bar; for example, tqdm(items, mininterval=1, leave=False).
A Pandas bar is slower than expected
A progress display does not optimize the operation. Prefer a vectorized Pandas operation when available, and increase mininterval if each applied function is very fast.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsWhen should you choose another tool?
Use tqdm when you need immediate local feedback in a script, terminal, notebook, or pipeline and can define what counts as completed work. Its integrations and small adoption step make it useful for loops, manual tasks, and common Python workflows.
Choose a different category when the requirement goes beyond a local progress meter:
- Persistent history or remote job status: use a task-tracking or workflow system that stores state beyond the process lifetime.
- Distributed-worker monitoring, searchable metrics, or alerts: use an observability or metrics platform.
- Performance diagnosis: use a profiler or tracing system rather than treating iteration rate as a diagnosis.
- Scheduling, retries, and resumable execution: use a workflow orchestrator.
- Rich terminal layouts: consider a terminal UI library suited to multi-panel displays.
Other progress-display options include progressbar2, Rich progress, and alive-progress; native framework tools may fit PyTorch, Keras, or Dask workloads more naturally. Compare the required output and integration rather than assuming one option is universally faster or better.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →

