A simple Python CLI tool that reads file hashes from a text file and checks their reputation using the VirusTotal v3 API. Built to be free-tier friendly by respecting VirusTotal’s public API rate limits and using caching to avoid re-querying.
✅ Works great for SOC triage, IOC enrichment, threat hunting, and incident response workflows (personal / non-commercial use per VirusTotal terms).
-
Reads hashes from a file (one per line)
-
Supports SHA-256 / SHA-1 / MD5 (VirusTotal accepts all)
-
Free-tier pacing (default sleep 16.5 seconds per request)
-
Automatic backoff + retry on HTTP 429 (rate-limited)
-
Writes results to CSV
-
Saves progress to JSON cache so you can resume without wasting quota
-
Includes a VirusTotal GUI link per hash for quick pivoting
For each hash, the tool calls:
GET https://www.virustotal.com/api/v3/files/<hash>
It extracts last_analysis_stats and classifies:
-
MALICIOUS if malicious > 0
-
SUSPICIOUS if malicious == 0 and suspicious > 0
-
SAFE otherwise
-
UNKNOWN if the hash is not found (404)
-
Python 3.8+ (works on Linux/Windows/macOS)
-
requests
Install dependency:
pip install requests
You must set your VirusTotal API key in an environment variable named VT_API_KEY.
export VT_API_KEY="YOUR_VT_API_KEY_HERE"
setx VT_API_KEY "YOUR_VT_API_KEY_HERE"
After setx, restart the terminal so the variable is available.
Verify it’s set:
echo $VT_API_KEY
Basic run (uses defaults)
python3 vt_checker.py -i hashes.txt
This creates:
-
vt_results.csv (default output)
-
vt_cache.json (default cache)
Specify output CSV name
python3 vt_checker.py -i hashes.txt -o results.csv
Test with first 10 hashes only
python3 vt_checker.py -i hashes.txt --max 10
VirusTotal free public API is very strict. Default is already safe:
python3 vt_checker.py -i hashes.txt --sleep 16.5
If you get rate-limited (429), increase sleep:
python3 vt_checker.py -i hashes.txt --sleep 20
Disable caching
python3 vt_checker.py -i hashes.txt --cache ""
hashes.txt must contain one hash per line:
44d88612fea8a8f36de82e1278abb02f
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
-
Empty lines are ignored
-
Lines starting with # are ignored
-
Duplicates are automatically removed (order preserved)
CSV Columns
The tool writes:
-
hash
-
verdict (SAFE / SUSPICIOUS / MALICIOUS / UNKNOWN / ERROR)
-
malicious, suspicious, harmless, undetected, timeout
-
type_description
-
meaningful_name
-
last_analysis_date
-
vt_link (direct GUI link)
-
error
Example output row (conceptual)
hash,verdict,malicious,suspicious,harmless,undetected,timeout,type_description,meaningful_name,last_analysis_date,vt_link,error
...,MALICIOUS,12,1,0,34,0,Win32 EXE,setup.exe,1700000000,https://www.virustotal.com/gui/file/...,
The tool caches results in vt_cache.json. If you rerun the tool with the same input file, already-checked hashes are returned from cache:
... -> SAFE (cached)
Start fresh (clear cache)
rm vt_cache.json
Your script is configured to be “free-tier friendly” by default:
-
--sleep 16.5 seconds (≈ 3–4 requests/min)
-
Retries on 429 with exponential backoff
If you still hit 429 often:
-
Increase --sleep
-
Reduce parallel activity (don’t run multiple instances)