VirusTotal's Public API lets you upload and scan files, submit and scan URLs, access finished scan reports and make automatic comments on URLs and samples without the need of using the HTML website interface. In other words, it allows you to build simple scripts to access the information generated by VirusTotal.
The chosen format for the API is HTTP POST requests with JSON object responses and it is limited to at most 4 requests of any nature in any given 1 minute time frame. If you run a honeyclient, honeypot or any other automation that is going to provide resources to VirusTotal and not only retrieve reports you are entitled to a higher request rate quota, ask for it at and you will receive special privileges when performing the calls to the API. Note that you will only have a higher request rate quota when asking for files or URLs that you previously sent to VirusTotal.
In this second version we have improved the response format so as to ease the task of retrieving results, we have also introduced batch requests, you may now ask for several items with a sole API call (as long as you cohere with the request rate limit).
The public API is a free service, available for any website or application that is free to consumers. The API must not be used in commercial products or services, it can not be used as a substitute for antivirus products and it can not be integrated in any project that may harm the antivirus industry directly or indirectly. Noncompliance of these terms will result in immediate permanent ban of the infractor individual or organization. Please see the terms of service for more information.
Audience
Getting started
Response basics
Sending and scanning files
Rescanning already submitted files
Retrieving file scan reports
Sending and scanning URLs
Retrieving URL scan reports
Retrieving IP address reports (includes Passive DNS)
Retrieving domain reports (includes Passive DNS)
Making comments on files and URLs
Scripts to interact with the public API
This document is intended for programmers who want to write client applications that can interact with VirusTotal using JSON and HTTP.
This document assumes that you understand general networking and programming ideas. Even though code examples are built using python, any programming language of your choice can be used to interact with this API.
The process could not be easier. Sign up to VirusTotal Community. Once you have a valid VirusTotal Community account, you will find your personal API key in your Community profile. This key is all you need to use VirusTotal's API.
The API response format is a JSON object containing at least the following two properties:
Whenever you exceed the public API request rate limit a 204 HTTP status code is returned. If you try to perform calls to functions for which you do not have the required privileges an HTTP Error 403 Forbidden is raised.
The VirusTotal API allows you to send files. Before performing your submissions we encourage you to retrieve the latest report on the files, if it is recent enough you might want to save time and bandwidth by making use of it. File size limit is 32MB. If you have a need to scan larger files, please contact us, and tell us your use case.
In order to send a file you must perform an HTTP POST request to the following URL:
This API call expects multipart/form-data parameters, the string part of the the call should have the following parameter:
The file part of the call should contain the name of the submitted file and the file itself. We strongly encourage you to send the file with the name with which it was found in the wild since this is very rich metadata for the VirusTotal database.
Summing up, the API acts like a form with a file input field named file.
Example Code:
import requests
params = {'apikey': '-YOUR API KEY HERE-'}
files = {'file': ('myfile.exe', open('myfile.exe', 'rb'))}
response = requests.post('https://www.virustotal.com/vtapi/v2/file/scan', files=files, params=params)
json_response = response.json()
curl -v -F 'file=@/path/to/file' -F \
apikey=${VT_API_KEY} https://www.virustotal.com/vtapi/v2/file/scan
$file_name_with_full_path = realpath('/path/to/file');
$api_key = getenv('VT_API_KEY') ? getenv('VT_API_KEY') :'YOUR_API_KEY';
$cfile = curl_file_create($file_name_with_full_path);
$post = array('apikey' => $api_key,'file'=> $cfile);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.virustotal.com/vtapi/v2/file/scan');
curl_setopt($ch, CURLOPT_POST, True);
curl_setopt($ch, CURLOPT_VERBOSE, 1); // remove this if your not debugging
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // please compress data
curl_setopt($ch, CURLOPT_USERAGENT, "gzip, My php curl client");
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,True);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result=curl_exec ($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print("status = $status_code\n");
if ($status_code == 200) { // OK
$js = json_decode($result, true);
print_r($js);
} else { // Error occured
print($result);
}
curl_close ($ch);
Example response:
{
'permalink': 'https://www.virustotal.com/file/d140c...244ef892e5/analysis/1359112395/',
'resource': u'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556',
'response_code': 1,
'scan_id': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556-1359112395',
'verbose_msg': 'Scan request successfully queued, come back later for the report',
'sha256': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556'
}
The scan_id field of the JSON object lets us query the report later making use of the file report retrieving API. Keep in mind that files sent using the API have the lowest scanning priority, depending on VirusTotal's load, it may take several hours before the file is scanned, so query the report at regular intervals until the result shows up and do not keep sending the file repeatedly.
The call allows you to rescan files in VirusTotal's file store without having to resubmit them, thus saving bandwidth.
The VirusTotal public API allows you to rescan files that you or other users already sent in the past and, hence, are already present in our file store. Before requesting a rescan we encourage you to retrieve the latest report on the files, if it is recent enough you might want to save time and bandwidth by making use of it.
In order to request a rescan you must perform an HTTP POST request to the following URL:
This API call expects the following HTTP POST parameters:
Example Code:
import requests
params = {'apikey': '-YOUR API KEY HERE-', 'resource': '7657fcb7d772448a6d8504e4b20168b8'}
headers = {
"Accept-Encoding": "gzip, deflate",
"User-Agent" : "gzip, My Python requests library example client or username"
}
response = requests.post('https://www.virustotal.com/vtapi/v2/file/rescan',
params=params)
json_response = response.json()
curl -v --request POST \
--url 'https://www.virustotal.com/vtapi/v2/file/rescan' \
-d apikey=${VT_API_KEY} \
-d 'resource=7657fcb7d772448a6d8504e4b20168b8'
$post = array('apikey' => 'YOUR_API_KEY','resource'=>'99017f6eebbac24f351415dd410d522d');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.virustotal.com/vtapi/v2/file/rescan');
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // please compress data
curl_setopt($ch, CURLOPT_USERAGENT, "gzip, My php curl client");
curl_setopt($ch, CURLOPT_VERBOSE, 1); // remove this if your not debugging
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result = curl_exec ($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print("status = $status_code\n");
if ($status_code == 200) { // OK
$js = json_decode($result, true);
print_r($js);
} else { // Error occured
print($result);
}
curl_close ($ch);
Example response:
{
'response_code': 1,
'scan_id': '54bc950d46a0d1aa72048a17c8275743209e6c17bdacfc4cb9601c9ce3ec9a71-1390472785'
'permalink': 'https://www.virustotal.com/file/__sha256hash__/analysis/1390472785/',
'sha256': '54bc950d46a0d1aa72048a17c8275743209e6c17bdacfc4cb9601c9ce3ec9a71',
'resource': '7657fcb7d772448a6d8504e4b20168b8',
}
The response_code field of the individual responses will be 1 if the file corresponding to the given hash was successfully queued for rescanning. If the file was not present in our file store this code will be 0. In the event of some unexpected error the code will be fixed to -1.
The scan_id field of the individual responses lets us query the report later making use of the file report retrieving API. Keep in mind that files sent using the API have the lowest scanning priority, depending on VirusTotal's load, it may take several hours before the file is scanned, so query the report at regular intervals until the result shows up and do not keep sending the file rescan requests once and over again.
In order to retrieve a scan report on a given file you must perform an HTTP POST request to the following URL:
With the following two HTTP POST parameters:
Example Code:
import requests
params = {'apikey': '-YOUR API KEY HERE-', 'resource': '7657fcb7d772448a6d8504e4b20168b8'}
headers = {
"Accept-Encoding": "gzip, deflate",
"User-Agent" : "gzip, My Python requests library example client or username"
}
response = requests.get('https://www.virustotal.com/vtapi/v2/file/report',
params=params, headers=headers)
json_response = response.json()
curl -v --request POST \
--url 'https://www.virustotal.com/vtapi/v2/file/report' \
-d apikey=${VT_API_KEY} \
-d 'resource=7657fcb7d772448a6d8504e4b20168b8'
$post = array('apikey' => 'YOUR_API_KEY','resource'=>'99017f6eebbac24f351415dd410d522d');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.virustotal.com/vtapi/v2/file/report');
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // please compress data
curl_setopt($ch, CURLOPT_USERAGENT, "gzip, My php curl client");
curl_setopt($ch, CURLOPT_VERBOSE, 1); // remove this if your not debugging
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result = curl_exec ($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print("status = $status_code\n");
if ($status_code == 200) { // OK
$js = json_decode($result, true);
print_r($js);
} else { // Error occured
print($result);
}
curl_close ($ch);
Example response:
{
'response_code': 1,
'verbose_msg': 'Scan finished, scan information embedded in this object',
'resource': '99017f6eebbac24f351415dd410d522d',
'scan_id': '52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c-1273894724',
'md5': '99017f6eebbac24f351415dd410d522d',
'sha1': '4d1740485713a2ab3a4f5822a01f645fe8387f92',
'sha256': '52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c',
'scan_date': '2010-05-15 03:38:44',
'positives': 40,
'total': 40,
'scans': {
'nProtect': {'detected': true, 'version': '2010-05-14.01', 'result': 'Trojan.Generic.3611249', 'update': '20100514'},
'CAT-QuickHeal': {'detected': true, 'version': '10.00', 'result': 'Trojan.VB.acgy', 'update': '20100514'},
'McAfee': {'detected': true, 'version': '5.400.0.1158', 'result': 'Generic.dx!rkx', 'update': '20100515'},
'TheHacker': {'detected': true, 'version': '6.5.2.0.280', 'result': 'Trojan/VB.gen', 'update': '20100514'},
.
.
.
'VirusBuster': {'detected': true, 'version': '5.0.27.0', 'result': 'Trojan.VB.JFDE', 'update': '20100514'},
'NOD32': {'detected': true, 'version': '5115', 'result': 'a variant of Win32/Qhost.NTY', 'update': '20100514'},
'F-Prot': {'detected': false, 'version': '4.5.1.85', 'result': null, 'update': '20100514'},
'Symantec': {'detected': true, 'version': '20101.1.0.89', 'result': 'Trojan.KillAV', 'update': '20100515'},
'Norman': {'detected': true, 'version': '6.04.12', 'result': 'W32/Smalltroj.YFHZ', 'update': '20100514'},
'TrendMicro-HouseCall': {'detected': true, 'version': '9.120.0.1004', 'result': 'TROJ_VB.JVJ', 'update': '20100515'},
'Avast': {'detected': true, 'version': '4.8.1351.0', 'result': 'Win32:Malware-gen', 'update': '20100514'},
'eSafe': {'detected': true, 'version': '7.0.17.0', 'result': 'Win32.TRVB.Acgy', 'update': '20100513'}
},
'permalink': 'https://www.virustotal.com/file/52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c/analysis/1273894724/'
}
If you are interested in the additional information included in web reports (file analysis tools like Portable Executable properties, ExifTool information, file signing information, etc.) then you should consider the private API.
URLs can also be submitted for scanning. Once again, before performing your submission we encourage you to retrieve the latest report on the URL, if it is recent enough you might want to save time and bandwidth by making use of it.
To submit a URL you must perform an HTTP POST request to the following URL:
This API call expects the following HTTP POST parameters:
Some python code will hopefully illustrate better how this is done:
Example Code:
import requests
params = {'apikey': '-YOUR API KEY HERE-', 'url':'http://www.virustotal.com'}
response = requests.post('https://www.virustotal.com/vtapi/v2/url/scan', data=params)
json_response = response.json()
curl -v --request POST \
--url 'https://www.virustotal.com/vtapi/v2/url/scan' \
-d apikey=${VT_API_KEY} \
-d 'url=https://www.google.com/'
$virustotal_api_key = 'YOUR_API_KEY';
$scan_url = 'https://www.google.com/';
$post = array('apikey' => $virustotal_api_key,'url'=> $scan_url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.virustotal.com/vtapi/v2/url/scan');
curl_setopt($ch, CURLOPT_POST, True);
curl_setopt($ch, CURLOPT_VERBOSE, 1); // remove this if your not debugging
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,True);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result=curl_exec ($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print("status = $status_code\n");
if ($status_code == 200) { // OK
$js = json_decode($result, true);
print_r($js);
} else { // Error occured
print($result);
}
curl_close ($ch);
Example response:
{
'response_code': 1,
'verbose_msg': 'Scan request successfully queued, come back later for the report',
'scan_id': '1db0ad7dbcec0676710ea0eaacd35d5e471d3e11944d53bcbd31f0cbd11bce31-1320752364',
'scan_date': '2015-11-08 11:39:24',
'url': 'http://www.virustotal.com/',
'permalink': 'http://www.virustotal.com/url/1db0ad7dbcec0676710ea0eaacd35d5e471d3e11944d53bcbd31f0cbd11bce31/analysis/1320752364/'
}
The scan_id parameter of the JSON object can then be used to query for the scan report making use of the URL scan report retrieving API described in the next section. Keep in mind that URLs sent using the API have the lowest scanning priority, depending on VirusTotal's load, it may take several hours before the URL is scanned, so query the report at regular intervals until the result shows up and do not keep submitting the URL once and over again.
In order to retrieve a scan report on a given URL you must perform an HTTP POST request to the following URL:
With the following HTTP POST parameters:
Some python code will hopefully illustrate better how this is done:
Example Code:
import requests
headers = {
"Accept-Encoding": "gzip, deflate",
"User-Agent" : "gzip, My Python requests library example client or username"
}
params = {'apikey': '-YOUR API KEY HERE-', 'resource':'http://www.virustotal.com'}
response = requests.post('https://www.virustotal.com/vtapi/v2/url/report',
params=params, headers=headers)
json_response = response.json()
curl -v --request POST \
--url 'https://www.virustotal.com/vtapi/v2/url/report' \
-d apikey=${VT_API_KEY} \
-d 'resource=http://www.virustotal.com/'
$api_key = getenv('VT_API_KEY') ? getenv('VT_API_KEY') :'YOUR_API_KEY';
$scan_url = 'https://www.google.com/';
$post = array('apikey' => $api_key,'resource'=> $scan_url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.virustotal.com/vtapi/v2/url/report');
curl_setopt($ch, CURLOPT_POST, True);
curl_setopt($ch, CURLOPT_VERBOSE, 1); // remove this if your not debugging
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // please compress data
curl_setopt($ch, CURLOPT_USERAGENT, "gzip, My php curl client");
curl_setopt($ch, CURLOPT_RETURNTRANSFER ,True);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result=curl_exec ($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print("status = $status_code\n");
if ($status_code == 200) { // OK
$js = json_decode($result, true);
print_r($js);
} else { // Error occured
print($result);
}
curl_close ($ch);
Example response:
{
'response_code': 1,
'verbose_msg': 'Scan finished, scan information embedded in this object',
'scan_id': '1db0ad7dbcec0676710ea0eaacd35d5e471d3e11944d53bcbd31f0cbd11bce31-1390467782',
'permalink': 'https://www.virustotal.com/url/__urlsha256__/analysis/1390467782/',
'url': 'http://www.virustotal.com/',
'scan_date': '2014-01-23 09:03:02',
'filescan_id': None,
'positives': 0,
'total': 51,
'scans': {
'CLEAN MX': {'detected': False, 'result': 'clean site'},
'MalwarePatrol': {'detected': False, 'result': 'clean site'}
[... continues ...]
}
}
Note that the second call makes use of the batch request feature. The results for each resource are JSON objects with several properties describing the scan output and metadata. If you are interested in the additional information included in web reports (web response headers, website content category according to different tools, threat description, etc.) you should consider the private API.
In order to retrieve a report on a given IP address (including the information recorded by VirusTotal's Passive DNS infrastructure) you must perform an HTTP GET request to the following URL:
With the following HTTP GET parameters:
Some python code will hopefully illustrate better how this is done:
>>> import json
>>> import urllib
>>> url = 'https://www.virustotal.com/vtapi/v2/ip-address/report'
>>> parameters = {'ip': '90.156.201.27', 'apikey': '-- YOUR API KEY --'}
>>> response = urllib.urlopen('%s?%s' % (url, urllib.urlencode(parameters))).read()
>>> response_dict = json.loads(response)
>>> print response_dict
{u'response_code': 1,
u'verbose_msg': u'IP address found in dataset',
u'resolutions': [
{u'last_resolved': u'2013-04-08 00:00:00', u'hostname': u'027.ru'},
{u'last_resolved': u'2013-04-08 00:00:00', u'hostname': u'auto.rema-tiptop.ru'},
{u'last_resolved': u'2013-04-08 00:00:00', u'hostname': u'catalog24de.ru'},
{u'last_resolved': u'2013-04-08 00:00:00', u'hostname': u'club.velhod.ru'},
{u'last_resolved': u'2013-04-08 00:00:00', u'hostname': u'danilova.pro'},
... continues ...
],
u'detected_urls': [
{"url": "http://027.ru/", "positives": 2, "total": 37, "scan_date": "2013-04-07 07:18:09"},
... continues ...
]}
Please note that only 1000 different domain name mappings will be returned at most.
As you may have noticed, not only does the report include the passive DNS data on the IP address, but also the latest URLs detected by at least one URL scanner and hosted at such IP address. This additional information may increase with time, including new notions such as the latest malware samples that were seen communicating with the given IP address, malware specimens that were downloaded from such IP address, etc.
If VirusTotal has absolutely no information regarding the IP address under consideration, the JSON's response code will be 0, -1 if the submitted IP address is invalid.
In order to retrieve a report on a given domain (including the information recorded by VirusTotal's Passive DNS infrastructure) you must perform an HTTP GET request to the following URL:
With the following HTTP GET parameters:
Some python code will hopefully illustrate better how this is done:
>>> import json
>>> import urllib
>>> url = 'https://www.virustotal.com/vtapi/v2/domain/report'
>>> parameters = {'domain': '027.ru', 'apikey': '-- YOUR API KEY --'}
>>> response = urllib.urlopen('%s?%s' % (url, urllib.urlencode(parameters))).read()
>>> response_dict = json.loads(response)
>>> print response_dict
{"response_code": 1,
"verbose_msg": "Domain found in dataset",
"resolutions": [
{"last_resolved": "2013-04-05 00:00:00", "ip_address": "90.156.201.11"},
{"last_resolved": "2013-04-07 00:00:00", "ip_address": "90.156.201.14"},
{"last_resolved": "2013-04-08 00:00:00", "ip_address": "90.156.201.27"},
{"last_resolved": "2013-04-07 00:00:00", "ip_address": "90.156.201.71"},
... continues ...
],
"detected_urls": [
{"url": "http://027.ru/", "positives": 2, "total": 37, "scan_date": "2013-04-07 07:18:09"},
... continues ...
]}
Please note that only 1000 different IP address resolutions will be returned at most.
As you may have noticed, not only does the report include the passive DNS data on the domain, but also the latest URLs detected by at least one URL scanner and hosted at such domain. This additional information may increase with time, including new notions such as the latest malware samples that were seen communicating with the given domain, malware specimens that were downloaded from such domain, etc.
If VirusTotal has absolutely no information regarding the domain under consideration, the JSON's response code will be 0, -1 if the submitted domain is invalid.
The initial idea of VirusTotal Community was that users should be able to make comments on files and URLs, the comments may be malware analyses, false positive flags, disinfection instructions, etc.
Imagine you have some automatic setup that can produce interesting results related to a given sample or URL that you submit to VirusTotal for antivirus characterization, you might want to give visibility to your setup by automatically reviewing samples and URLs with the output of your automation.
In order to do so you can make use of VirusTotal's API by performing an HTTP POST request to the following URL:
You must provide the following HTTP POST parameters to the call:
Some python code will hopefully illustrate better how this is done:
>>> import simplejson
>>> import urllib
>>> import urllib2
>>> url = "https://www.virustotal.com/vtapi/v2/comments/put"
>>> parameters = {"resource": "99017f6eebbac24f351415dd410d522d",
... "comment": "How to disinfect you from this file... #disinfect #zbot",
... "apikey": "-- YOUR API KEY --"}
>>> data = urllib.urlencode(parameters)
>>> req = urllib2.Request(url, data)
>>> response = urllib2.urlopen(req)
>>> json = response.read()
>>> print json
{"response_code": 1,
"verbose_msg": "Your comment was successfully posted"}
If the comment was successfully posted the response code will be 1, 0 otherwise.
This section links several scripts written by VirusTotal Community members to interact with the public API using different programming languages. VirusTotal itself is not responsible for these scripts, they are contributions of the Community and their use is at your own risk. If you would like to see your own implementation listed in this section just get in touch with us.