DEV Community

Alex Spinov
Alex Spinov

Posted on

VirusTotal Has a Free API — Scan Files and URLs for Malware Programmatically

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")
Enter fullscreen mode Exit fullscreen mode

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')
    }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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']}")
Enter fullscreen mode Exit fullscreen mode

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

  1. Security teams: Automate URL scanning in email filters
  2. Developers: Check downloaded packages before installing
  3. SOC analysts: Bulk-check IOCs from threat intelligence feeds
  4. DevOps: Scan artifacts in CI/CD pipelines
  5. Researchers: Study malware distribution patterns

Combine with Other APIs


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)