VirusTotal API – Getting started with security automation

VirusTotal API – Getting started with security automation

In this post, we explore the VirusTotal API. We also look at how Tines and security automation can power-up your usage of the VirusTotal API.

Public vs. Private APIs

VirusTotal provide two API versions: a Public API and a Private API. The main differences between the two are the volume of queries available and the depth of information provided. The public API allows four queries per minute, and does not allow malware sample downloads. For the majority of uses, the public VirusTotal API will be sufficient and is what we’ll focus on in this post.

Creating a VirusTotal account

Access to the VirusTotal public API is free, to get started, you’ll need to obtain an API key. The API key allows you to make queries against the API. Click “Join our community” on the home page. Enter the required details and click “sign-up”.

Virustotal api sign up

Getting a VirusTotal API key

After you’ve created your account, click your username in the top right-hand corner of the page. Then, from the drop-down menu, select “My API key”.

On the next page, VirusTotal will display your API key. It will be a long, alpha-numeric string. As with all API keys, you should treat this key as a password, store it securely in a password manager and don’t embed it in scripts.

VirusTotal API examples

Now that we have an API key, we can start making queries against the public API. The following examples use cURL, if you don’t have cURL installed, you may want to use our Postman collection. Additionally, in all the below examples, you should replace $your-api-key with the api key from your VirusTotal profile.

Further details, including sample requests in Python are available from the VirusTotal Public API docs page.

Get a file scan report

If we have a suspicious file, we can check its status with the VirusTotal API. In the below example, replace $your-file-hash with the hash of the file you want to check.

curl -v --request POST \
  --url '' \
  -d apikey=$your-api-key \
  -d 'resource=$your-file-hash'

If the file existed in its database, VirusTotal will return the results in a response similar to the below:

 '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': '', 'result': 'Trojan/VB.gen', 'update': '20100514'},
    'VirusBuster': {'detected': true, 'version': '', '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': '', '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': '', 'result': 'TROJ_VB.JVJ', 'update': '20100515'},
    'Avast': {'detected': true, 'version': '4.8.1351.0', 'result': 'Win32:Malware-gen', 'update': '20100514'},
    'eSafe': {'detected': true, 'version': '', 'result': 'Win32.TRVB.Acgy', 'update': '20100513'}
 'permalink': ''

Scan a file with VirusTotal API

The below request sends a file located at /your/file/path to VirusTotal for scanning.

curl -X POST \ \
  -F apikey=$your-api-key \
  -F file=@/your/file/path

If the submission was successful, you will receive a response similar to the below. Notice that VirusTotal will include a field called “scan_id”. VirusTotal will not scan the file immediately, and will instead add it to a queue. As such, the “scan_id” field allows us check the status of the scan.

Example response:

 'permalink': '',
 'resource': u'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556',
 'response_code': 1, 'scan_id': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556-1359112395',
 'verbose_msg': 'Scan request successfully queued, come back later for the report',
 'sha256': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556'

Rescan an already submitted file

If a scan report is out-of-date, or you want the most recent status, you can rescan the file by submitting its hash.

curl -X POST \ \
-F apikey=$your-api-key \
-F resource=7657fcb7d772448a6d8504e4b20168b8

Send and scan URL

VirusTotal mt1 mb0 provides the status of URLs in its database. To scan a URL, use a command similar to that shown below.

curl -X POST \ \
  -F apikey=$your-api-key \
  -F url=

Retrieve URL scan report

Before submitting a URL for scanning, it’s best practice to check if the URL already exists in the database. Do this using a command similar to that shown below.

curl -X POST \ \
  -F apikey=$your-api-key \
  -F resource=

Retrieve URL scan report (scan if does not exist)

In the above command, if the URL doesn’t already exist in VirusTotal’s database, the response will be blank. By specifying “scan=1” in our request, we can tell VirusTotal to automatically scan the URL if it doesn’t already exist.

curl -X POST \ \
  -F apikey=$your-api-key \
  -F url= \
  -F scan=1

Retrieve domain report

Most people are aware that VirusTotal contains the status of URLs and files across anti-virus engines. However, not everyone is aware that it also maintains an extensive database of passive DNS data. To get the status of a domain, including passive DNS information via the VirusTotal API, use a request similar to the below.

curl -X GET \

Retrieve IP address report

To retrieve the status of an IP address, including passive DNS, submit a request similar to the below to the VirusTotal API.

curl -X GET \

Comment on file or URL

An extremely valuable, if slightly underused resource in VirusTotal, is the comments applied to files and URLs. Below, we’re indicating that the URL in question is a phishing page. As such, we’re providing valuable context to other VirusTotal users.

curl -X POST \ \
  -F apikey=$your-api-key \
  -F resource= \
  -F 'comment=This is a phishing page'

VirusTotal API Postman Collection

When experimenting with APIs, an extremely useful tool is Postman. Postman, provides a GUI that allows interaction with APIs. Additionally, it’s easy to create, troubleshoot and share API requests. At Tines, we’ve created a Postman collection with common VirusTotal API queries. You can download the collection, or clone the repository, from here and then import it into Postman.

Configuring Postman with the VirusTotal API collection

After you have imported the Postman collection, create a new environment variable called “apikey”.  Next, store your VirusTotal API key in this variable (see below).

Postman with virustotal api key

The Postman collection contains several API calls that can be customised based on your requirements. For example, replace the URL you wish to scan, or the IP address for which you wish to perform a passive DNS lookup.

Using the VirusTotal API with Tines

In our automating phishing and abuse inbox management tutorial series, we used the VirusTotal API extensively to analyse suspicious URLs and files. So, you may want to start there to understand a real world security automation application of the VirusTotal API.

Adding your VirusTotal API key to a Tines credential

The Tines credential widget allows storage of secret information so it can be safely included in agent bodies, without fear of disclosure. To add your VirusTotal API key to Tines, when signed-in to a tenant, choose “Credentials” -> “New Credential”.

Tines supports a variety of credential types, for the VirusTotal API, choose “text”. Next, choose a name for the credential, then enter your API key under “Credential value”.

VirusTotal API key in Tines credential

After saving the credential, we can now include it in agent configurations with the credential widget: {% credential Virustotal %}.

Submitting queries to the VirusTotal API using Tines

The Tines HTTP Request Agent (HRA) is used to integrate with 3rd-party APIs. Configuring a HRA to talk to the VirusTotal API is easy. First, create a new HRA: from anywhere in your tenant choose “Agents -> New Agent” from the main menu, then choose “HTTP Request Agent” from the agent type drop-down.

After configuring the common config, edit the options block to reflect the call you want to make to VirusTotal. For example, if you want to comment on a file or URL you would use the following options block:

  "url": "",
  "content_type": "form",
  "method": "post",
  "payload": {
    "apikey": "{% credential Virustotal %}",
    "resource": "",
    "comment": "This is a phishing page"

If you wanted to retrieve an IP address report, you would use an options block similar to that shown below:

  "url": "",
  "content_type": "form",
  "method": "get",
  "payload": {
    "apikey": "{% credential Virustotal %}",
    "ip": ""

In this case, we’re using the GET method and calling the /ip-address/report endpoint. When the agent runs, it will emit the response from the VirusTotal API as a new event, see sample emitted event below:

Sample emitted Tines event Virus Total API

Avoiding VirusTotal API rate limits

As mentioned above, the VirusTotal API rate limits requests to four per minute. As a result, when you exceed this quota, VirusTotal will respond with an empty body and a HTTP status of 204.

  "get_ip_report_from_virustotal": ⊖{
    "body": "",
    "headers": {...},
    "status": 204

Using Tines, we can account for event spikes and thus avoid exceeding our quota using Trigger Agents (TA) and an Event Transformation Agent (ETA) in delay mode.

In the diagram below, we use a HTTP Request Agent to query the VirusTotal API, then check whether the response hit a rate limit. If it did, that is the status was 204, we wait 20 seconds and try again.

The configuration for these four agents is available in an importable story here.

To learn more about Tines, book a demo or get started with a fully-featured Community Edition account. It’s free to use, requires no up-front commitment and includes a generous automation capacity.

Eoin Hinchy
Eoin Hinchy
Founder, Tines