Please check VirusTotal Graph overview before start.

Basic usage

Start by importing the vt_graph_api module:

>>> import vt_graph_api

Creates a new graph (replace <apikey> with your actual VirusTotal API key):

>>> graph = vt_graph_api.VTGraph("<apikey>", name="My Graph", private=False)


Private graphs are only allowed for premium users.


Nodes are the minimum unit of graph representation. There’s some basic node types, and we have also the opportunity to represent our own custom node types, so we can specify any type such as “actor” or “email”. The basic node types are the following:

  • file
  • domain
  • ip_address
  • url

The difference between basic nodes and custom nodes is that the first ones could be expanded using VirusTotal API.

Adding Node


Adding a file node with no sha256 hash, url node with raw URL instead of an VirusTotal URL identifier or unknown ID with fetch_vt_enterprise option, consumes API quota.

We can either add a basic node:

>>> graph.add_node("www.virustotal.com", "domain", label="mynode")

Or our own custom type:

>>> graph.add_node("badguy", "victim")")

We can also specify that we want to fetch information on VirusTotal, about the node we are adding by setting fetch_information to True:

>>> graph.add_node("www.virustotal.com", "domain", fetch_information=True)

You can improve the search using VirusTotal Intelligence by setting fetch_vt_enterprise to True.


fetch_vt_enterprise flag is only available for premium users.

If we want to add some nodes at the same time we can use add_nodes, this method receive a node list which is a list of dictionaries with the following structure:

node_list = [
      "node_id" = "www.virustotal.com",
      "node_type" = "domain",
      "label" = "mynode",   # This attribute is optional.
      "attributes" = "",  # This attribute is optional.
      "x_position" = "", # This attribute is optional.
      "y_position" = ""  # This attribute is optional.

>>> graph.add_nodes(node_list, fetch_information=True)

See also

For advanced usage, please check API Reference.


Setting fetch_information to True consumes API quota.

Deleting Node


This method will raise NodeNotFoundError if the given node id does not belong to the graph.


This method also deletes every link which have relation with the deleted node.

Checking if node id belongs to graph

It is possible to check if graph has a node id using:

>>> graph.has_node("www.virustotal.com")


An expansion is the action to get related nodes to a node.

See also

Please see VirusTotal documentation in order to check relationship types:

Expanding node by a given expansion

There is file node with the hash 7ed0707be56fe3a7f5f2eb1747fdb929bbb9879e6c22b6825da67be5e93b6bd2 and we want to know the domains that the file has contacted with, so we can use VirusTotal API to get the connected domains by expanding the file node using contacted_domains as expansion type.

>>> graph.expand("7ed0707be56fe3a7f5f2eb1747fdb929bbb9879e6c22b6825da67be5e93b6bd2", "contacted_domains")

This method adds to the graph the contacted domains returned by VirusTotal API.


It is possible to specify the maximum number of nodes returned by the expansion, setting max_nodes_per_relationship parameter.


This call consumes API quota as much as max_nodes_per_relationship/max_nodes_per_query.


max_nodes_per_query = 40, this is the maximum number of nodes that we can request to VirusTotal API per query.

See also

Please check API Reference for more information.

Expanding node one level

We can expand a node in all of his available expansions:

>>> graph.expand_one_level("7ed0707be56fe3a7f5f2eb1747fdb929bbb9879e6c22b6825da67be5e93b6bd2", max_nodes_per_relationship=10)


This call consumes API quota as much as the number of node’s expansions * max_nodes_per_relationship/max_nodes_per_query.

Expanding the whole graph

Alternatively we can expand the whole graph all the levels that we want:

>>> graph.expand_n_level(level=2)


As the methods before, we can specify max_nodes_per_relationship and max_nodes to ensure that we will not take more nodes than necessary.


This call consumes API quota as much as the number of total expansion of each graph node * max_nodes_per_relationship/max_nodes_per_query.

See also

Please see API Reference for more information.


Once our graph is finished we can save it in VirusTotal:

>>> graph.save_graph()


At this point if the Graph is set public it will be searchable in VirusTotal UI.


We can recover a VirusTotal Graph if we have his ID:

>>> vt_graph_api.VTGraph.load_graph("<graphid>", "<apikey>")

We can retrieve graphs that we are viewer, editor or owner.


We cannot modify and save the loaded graph if we have no editor permissions.

See also

Please see also clone_graph method.


>>> vt_graph_api.VTGraph.clone_graph("<graphid>", "<apikey>")


This method does not clone the original user/group viewers/editors. It is necessary to call save_graph() in order to save the cloned graph in VirusTotal.

See also

Please check API Reference for advanced usage.


Check if someone is viewer

>>> vt_graph_api.is_viewer("<username>", "<graphid>", "<apikey>")

Check if someone is editor

>>> vt_graph_api.is_viewer("<username>", "<graphid>", "<apikey>")

Getting iframe

We can also get an iframe link in order to embed the graph in our website.

>>> vt_graph_api.get_iframe_code()
'<iframe src="https://www.virustotal.com/graph/embed/<<graph_id>>" width="800" height="600"></iframe>'

Getting API quota consumed

We can get how many API quota we have consumed since the script started

>>> graph.get_api_calls()