A colleague clicked a suspicious link. We needed to check if it was malicious — fast.
VirusTotal scans URLs and files against 70+ antivirus engines. And they have a free API.
Getting Started
Sign up at virustotal.com for a free API key. The free tier gives you:
- 4 lookups/minute
- 500 lookups/day
- 15.5K lookups/month
1. Scan a URL
import requests
import time
API_KEY = 'your_api_key_here' # Free from virustotal.com
BASE = 'https://www.virustotal.com/api/v3'
HEADERS = {'x-apikey': API_KEY}
def scan_url(url):
# Submit URL for scanning
resp = requests.post(
f'{BASE}/urls',
headers=HEADERS,
data={'url': url}
)
analysis_id = resp.json()['data']['id']
# Wait for results
time.sleep(15)
# Get results
result = requests.get(
f'{BASE}/analyses/{analysis_id}',
headers=HEADERS
).json()
stats = result['data']['attributes']['stats']
return {
'url': url,
'malicious': stats.get('malicious', 0),
'suspicious': stats.get('suspicious', 0),
'harmless': stats.get('harmless', 0),
'undetected': stats.get('undetected', 0)
}
result = scan_url('https://example.com')
print(f"Malicious: {result['malicious']}/70+ engines")
2. Check a File Hash
Already have a file hash? Look it up without uploading:
import hashlib
def check_file_hash(file_path):
# Calculate SHA256
sha256 = hashlib.sha256()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
sha256.update(chunk)
file_hash = sha256.hexdigest()
# Look up on VirusTotal
resp = requests.get(
f'{BASE}/files/{file_hash}',
headers=HEADERS
)
if resp.status_code == 404:
return {'hash': file_hash, 'status': 'Not found in VT database'}
data = resp.json()['data']['attributes']
stats = data.get('last_analysis_stats', {})
return {
'hash': file_hash,
'malicious': stats.get('malicious', 0),
'total_engines': sum(stats.values()),
'name': data.get('meaningful_name', 'Unknown'),
'type': data.get('type_description', 'Unknown')
}
3. Batch URL Checker
def batch_check_urls(urls):
results = []
for url in urls:
# Use URL lookup (faster than scan)
import base64
url_id = base64.urlsafe_b64encode(url.encode()).decode().strip('=')
resp = requests.get(
f'{BASE}/urls/{url_id}',
headers=HEADERS
)
time.sleep(15) # Free tier: 4 req/min
if resp.status_code == 200:
stats = resp.json()['data']['attributes']['last_analysis_stats']
results.append({
'url': url,
'malicious': stats.get('malicious', 0),
'clean': stats.get('harmless', 0)
})
else:
results.append({'url': url, 'status': 'Not found'})
for r in results:
flag = '!!!' if r.get('malicious', 0) > 0 else '[ok]'
print(f" {flag} {r['url'][:40]:<42} mal:{r.get('malicious','?')}")
return results
4. Domain Intelligence
def check_domain(domain):
resp = requests.get(
f'{BASE}/domains/{domain}',
headers=HEADERS
).json()
attrs = resp['data']['attributes']
stats = attrs.get('last_analysis_stats', {})
return {
'domain': domain,
'malicious': stats.get('malicious', 0),
'reputation': attrs.get('reputation', 0),
'categories': attrs.get('categories', {}),
'creation_date': attrs.get('creation_date', 'Unknown'),
'registrar': attrs.get('registrar', 'Unknown')
}
info = check_domain('google.com')
print(f"Reputation: {info['reputation']}")
print(f"Malicious detections: {info['malicious']}")
Rate Limits
| Tier | Requests/min | Requests/day |
|---|---|---|
| Free | 4 | 500 |
| Premium | 30 | 30,000 |
The free tier is enough for personal security checks and small-scale monitoring.
Use Cases
- Security teams: Automate URL scanning in email filters
- Developers: Check downloaded packages before installing
- SOC analysts: Bulk-check IOCs from threat intelligence feeds
- DevOps: Scan artifacts in CI/CD pipelines
- Researchers: Study malware distribution patterns
Combine with Other APIs
- WHOIS/RDAP — check domain age
- GitHub API — verify repo owners
PyPI Supply Chain Scanner — check package risks
NASA Has 5 Free APIs — track asteroids, Mars photos, space weather
Building security tools with free APIs. More: GitHub | Writing: Spinov001@gmail.com
More free tools: 77 Web Scraping Tools & APIs
What security tool do you wish had a better API? VirusTotal is great but the free tier is limited. What alternatives do you use? 👇
Top comments (0)