A fast, minimal progress-bar library that wraps any iterable to show completion, rate, and ETA — ideal for long-running security loops like scans and brute-forcing.
tqdm (from Arabic taqaddum, "progress") adds a smart progress bar to loops by wrapping an iterable: for x in tqdm(items). It auto-calculates throughput and estimated time remaining with negligible overhead, and works in terminals, notebooks, and over parallel workers. In security tooling it gives operators feedback during host sweeps, wordlist attacks, and large file transfers that would otherwise look frozen.
pip install tqdmimport time
from tqdm import tqdm
for port in tqdm(range(1, 1025), desc="scanning", unit="port"):
time.sleep(0.001) # stand-in for a real probescanning: 100%|██████████████████| 1024/1024 [00:01<00:00, 812.44port/s]
Wrapping any iterable in tqdm() gives a progress bar with rate and ETA. It writes to stderr by default, so piped stdout stays clean.
| API | Purpose |
|---|---|
tqdm(iterable, desc=, unit=, total=) |
Wrap an iterable |
tqdm(total=n) + bar.update(k) |
Manual progress for non-iterable work |
bar.set_description(text) |
Update the label mid-run |
bar.set_postfix(found=3) |
Show live counters beside the bar |
bar.write(msg) |
Print a line without corrupting the bar |
disable=True |
Turn the bar off (useful when not a TTY) |
file=sys.stdout |
Redirect the bar; defaults to stderr |
leave=False |
Remove the bar on completion |
tqdm.contrib.concurrent.thread_map |
Progress over a thread pool |
tqdm.contrib.concurrent.process_map |
Progress over a process pool |
Use bar.write() rather than print() inside a loop, or output will interleave with the bar.
Wrap a scan loop:
import time
from tqdm import tqdm
hosts = [f"192.168.56.{i}" for i in range(1, 21)]
for host in tqdm(hosts, desc="Sweeping", unit="host"):
time.sleep(0.05) # stand-in for a real probeSweeping: 100%|██████████████████████| 20/20 [00:01<00:00, 19.8host/s]
Progress over a password wordlist with a running count:
from tqdm import tqdm
wordlist = [f"pass{i}" for i in range(1000)]
target = "pass742"
found = None
for pw in tqdm(wordlist, desc="Cracking", unit="pw"):
if pw == target: # stand-in for a real auth check
found = pw
break
print("recovered:", found)Cracking: 74%|████████████▌ | 743/1000 [00:00<00:00, 900000pw/s]
recovered: pass742
Manual updates for a chunked download:
from tqdm import tqdm
total_bytes = 5 * 1024 * 1024
chunk = 512 * 1024
with tqdm(total=total_bytes, unit="B", unit_scale=True, desc="Downloading") as bar:
downloaded = 0
while downloaded < total_bytes:
downloaded += chunk # stand-in for reading a socket
bar.update(chunk)Downloading: 100%|█████████████████| 5.00M/5.00M [00:00<00:00, 250MB/s]
- Scan progress — show ETA during host/port sweeps so operators know a long scan is alive, not hung.
- Brute-force feedback — track progress and rate through password or fuzzing wordlists.
- Bulk transfers — visualize progress when exfiltrating evidence or downloading large wordlists/tools.
- Batch processing — monitor loops over thousands of URLs, hashes, or log lines during analysis.
- Rate awareness — the throughput readout helps spot rate-limiting or performance bottlenecks.
- Using
print()inside atqdmloop — it corrupts the bar. Usebar.write(). - Not passing
total=when the iterable has no length (a generator), so no percentage or ETA is shown. - Wrapping an already-materialised list built at high cost just to get a length.
- Assuming the bar goes to stdout — it goes to stderr, which is correct but surprising.
- Leaving bars enabled in non-interactive runs — cron logs fill with progress redraws; pass
disable=not sys.stderr.isatty(). - Nesting bars without
position=, producing overlapping output. - Adding tqdm to a library rather than the application layer.
[!warning] Progress output belongs on stderr Keep findings on stdout so the tool composes in a pipeline.
- A progress bar reveals scan pacing. Screenshots of a bar in a report can disclose target counts and timing you did not intend to share.
set_postfixcan leak data. Showing the current target hostname beside the bar puts it in any captured terminal output or CI log; keep identifiers out of the label in shared environments.- Disable bars in automated runs. Cron and CI logs filled with redraw sequences are hard to read and can mask real errors.
- A progress bar is not rate limiting. It shows how fast you are going; it does not slow you down. Add an explicit delay if the target needs protection.
- tqdm has no security function — it is presentation only, and adds a dependency to a tool that may need to run on a locked-down host.
- Pass
total=when wrapping generators or manual loops so the bar can show a percentage and ETA. - Use
unit=/unit_scale=Truefor human-readable byte counts. - Prefer
tqdm.write()overprint()inside a loop to avoid breaking the bar. - Wrap the outermost loop only; nested bars (
leave=False) get noisy fast. - Combine with [[colorama]] or a logger for colored status alongside the bar.
- [[Rich]] — richer progress bars plus tables and layout
- [[colorama]] — color the status text around your progress bars
- [[Readme|Python for Security Professionals]] — course home