vt-py − vt-py Documentation
vt−py is the official Python client library for the VirusTotal API v3.
This library requires Python 3.7.0+, Python 2.x is not supported. This is because vt−py makes use of the new async/await syntax for implementing asynchronous coroutines.
Not supporting Python 2.x was a difficult decision to make, as we are aware that Python 2.x is still popular among VirusTotal users, but in the long run we think it's the right one.
Python 2.7 has its days numbered, and the new concurrency features included in Python 3.5+ are perfect for creating highly performant code without having to deal with multi−threading or multi−processing.
The easiest and recommended way of installing vt−py is using pip:
$ pip install vt−py
Alternatively, you can get the source code directly from the GitHub and run setup.py. For getting the code you can either clone the public repository:
$ git clone
https://github.com/VirusTotal/vt−py.git
$ cd vt−py
Or, download the tarball for the latest release and uncompress it:
$ tar
−zxvf vt−py−X.Y.Z.tar.gz
$ cd vt−py−X.Y.Z
Once you have the code you can install it with:
$ pip install .
The API for this library is relatively small and shares the same concepts and principles seen in the underlying REST API. For this reason we highly recommend you to familiarize yourself with these - concepts before continuing.
While using this library you may have the impression that it's very similar to other general−purpose HTTP libraries like requests, as you will see very generic APIs like vt.Client.get(), vt.Client.post() and so on. In fact, you will find yourself relying on the REST API documentation in order to find the right endpoint where to send a request to, or learn about the attributes exported by some object. This has been a deliberate decision. We wanted vt−py to be as lightweight and generic as possible, so that changes in the REST API don't always require a new version of this client library, but at the same time offering the right abstractions so that you don't need to deal with details like setting HTTP headers, serializing and deserializing JSON, etc.
So, this is not a high−level library that completely abstract you out of the underlying REST API, quite the contrary, this library is more like a HTTP library that has been enriched with features specifically tailored to work with the VirusTotal API.
Start by importing the vt module:
>>> import vt
Create a client, replace <apikey> with your actual VirusTotal API key:
>>> client = vt.Client("<apikey>")
Ask for the file you are interested in, you can replace the hash in the example with some other SHA−256, SHA−1 or MD5:
>>> file = client.get_object("/files/44d88612fea8a8f36de82e1278abb02f")
Now file is an instance of vt.Object that contains information about the requested file. This object have the attributes returned in the API response which are listed in the VirusTotal API v3 documentation. Some examples:
>>>
file.size
68
>>>
file.sha256
'275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f'
>>>
file.type_tag
'text'
>>>
file.last_analysis_stats
{'failure': 0, 'harmless': 0, 'malicious': 62, 'suspicious':
0, 'timeout': 0, 'type−unsupported': 9, 'undetected':
2}
Alternatively, you can use the vt.Object.get() method for retrieving object's attributes:
>>>
file.get("size")
68
>>>
file.get("type_tag")
'text'
This method is useful for those attributes that are optional and do not appear in all files. For example, a Portable Executable (PE) file won't have the pdf_info attribute, and PDF document won't have the pe_info attribute. This means that file.pe_info will fail for PDF files but with file.get("pe_info") you are on the safe side, as it will return None if the pe_info attribute is not present.
Create a client as explained in the previous section and ask for the desired URL as follows:
>>>
url_id = vt.url_id("http://www.virustotal.com")
>>> url = client.get_object("/urls/{}",
url_id)
In this case the code is not as straightforward as it was for getting a file. While retrieving a file any of its hashes can be used as the file identifier, but with URLs is a bit more complicated. You must use vt.url_id() for generating the appropriate identifier. You can find more information about why this is necessary in: - https://docs.virustotal.com/reference/url.
Also notice how we are using a placeholder {} in the path. The placeholder will be replaced with the value of url_id. This works exactly like Python's new−style string formatting using the .format() function. This other code is equivalent:
>>> url = client.get_object("/urls/{}".format(url_id))
The returned object contains the URL attributes. Some examples:
>>>
url.times_submitted
213730
>>>
url.last_analysis_stats
{'harmless': 61, 'malicious': 0, 'suspicious': 1, 'timeout':
0, 'undetected': 8}
Before scanning a file is highly recommended that you look up for it as described in Get information about a file. If the file already exists and the latest analysis is fresh enough, you can use that instead of scanning the file again. If not, you can scan it with:
>>>
with open("/path/to/file", "rb") as f:
>>> analysis = client.scan_file(f)
When vt.Client.scan_file() returns the analysis is not completed yet, the object returned only has the analysis ID but not attributes. In order to track the status of the analysis you must ask for the analysis object until it's status is completed:
>>>
while True:
>>> analysis =
client.get_object("/analyses/{}", analysis.id)
>>> print(analysis.status)
>>> if analysis.status == "completed":
>>> break
>>> time.sleep(30)
Alternatively you can use the wait_for_completion argument:
>>>
with open("/path/to/file", "rb") as f:
>>> analysis = client.scan_file(f,
wait_for_completion=True)
When wait_for_completion is True vt.Client.scan_file() doesn't return until the analysis has been completed.
Scanning a URL is very similar to scanning a file, you just need to use vt.Client.scan_url() instead of vt.Client.scan_file():
>>> analysis = client.scan_url('https://somedomain.com/foo/bar')
NOTE:
This feature is available only for premium users.
Downloading a file it's very simple, you only need to provide the hash and a file−like object where the file's content will be written to. The target file must be opened in "wb" mode:
>>>
with open("/path/to/target_file", "wb")
as f:
>>>
client.download_file("44d88612fea8a8f36de82e1278abb02f",
f)
NOTE:
This feature is available only for premium users.
Create an empty object of type retrohunt_job and set its rules attribute:
>>> job
= vt.Object("retrohunt_job")
>>> job.rules = "rule test { condition:false
}"
Post the object to the /intelligence/retrohunt_jobs collection:
>>> job = client.post_object("/intelligence/retrohunt_jobs", obj=job)
Notice that job has been replaced with the value returned by vt.Client.post_object(), so now job has an ID and additional attributes.
>>>
job.id
'username−123456789'
>>>
job.status
'starting'
With the object identifier you can ask for the job again a see it making progress. Wait for a few seconds and do:
>>> job = client.get_object("/intelligence/retrohunt_jobs/{}", job.id)
The job status should have changed to running:
>>>
job.status
'running'
And the progress attribute should show the completion percentage:
>>>
job.progress
1.4145595
Let's abort the job:
>>>
response =
client.post("/intelligence/retrohunt_jobs/{}/abort",
job.id)
>>> response.status
200
Here we are using vt.Client.post() instead of vt.Client.post_object(), this is because the /intelligence/retrohunt_jobs/{id}/abort endpoint doesn't expect an object, just a POST request with an empty body. The result from vt.Client.post() is a vt.ClientResponse instance.
NOTE:
This feature is available only for premium users.
Create an empty object of type hunting_ruleset and set its name and rules attributes:
>>> rs
= vt.Object("hunting_ruleset")
>>> rs.name = "My test ruleset"
>>> rs.rules = "rule test { condition:false
}"
Post the object to the /intelligence/hunting_rulesets collection:
>>> rs = client.post_object("/intelligence/hunting_rulesets", obj=rs)
Because we didn't set the enabled attribute while creating the ruleset, it was created with enabled=False by default:
>>>
rs.enabled
False
Let's enable the ruleset:
>>>
rs.enabled = True
>>> rs =
client.patch_object("/intelligence/hunting_rulesets/{}",
rs.id, obj=rs)
>>> rs.enabled
True
Once you're done using the client, call client.close() at the end of your script, to make sure the client is properly closed. Otherwise you might see tracebacks indicating the client was never closed.
>>> client.close()
If you use a context manager<https://docs.python.org/3/reference/datamodel.html#context−managers>, the client is automatically closed after exiting, so there's no need to call close:
>>>
with vt.Client('<apikey>') as client:
... file =
client.get_object("/files/44d88612fea8a8f36de82e1278abb02f")
... print(file.type_tag)
text
VT module.
exception vt.APIError(code: str, message: str)
Class that encapsules errors returned by the VirusTotal API.
class vt.Client(apikey: str,
agent: str = 'unknown', host: str | None =
None, trust_env: bool = False, timeout: int = 300, proxy:
str | None =
None, headers: Dict | None = None, verify_ssl: bool = True,
connector:
BaseConnector = None)
Client for interacting with
VirusTotal.
Parameters
|
• |
apikey (str) −− Your VirusTotal API key. | ||
|
• |
agent (str) −− A string that identifies your application. | ||
|
• |
host (str) −− By default https://www.virustotal.com, it can be changed for testing purposes. | ||
|
• |
trust_env (bool) −− Get proxies information from HTTP_PROXY/HTTPS_PROXY environment variables if the parameter is True (False by default). | ||
|
• |
timeout (int) −− A int that determines the number of seconds to wait for a request to timeout (300 by default). | ||
|
• |
proxy (str) −− A string indicating the proxy to use for requests made by the client (None by default). | ||
|
• |
headers (dict) −− Dict of headers defined by the user. | ||
|
• |
verify_ssl (bool) −− Whether to verify the certificate in SSL connections. | ||
|
• |
connector (aiohttp.BaseConnector) −− (Optional) A custom aiohttp connector. |
close() -> None
Closes the client.
When the client is not needed anymore it must be closed for releasing resources like TCP connections.
Not closing the client after it's used might show error tracebacks indicating it was not properly closed.
async close_async() -> None
Like close() but returns a coroutine.
delete(path: str,
*path_args: Any, data: str | bytes | None =
None, json_data: Dict | None = None) ->
ClientResponse
Sends a DELETE request to a
given API endpoint.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
data (A string or bytes) −− Data sent in the request body. | ||
|
• |
json_data (dict) −− dict containing data to send in the request body as JSON. |
Returns
An instance of ClientResponse.
async delete_async(path:
str, *path_args: Any, data: str | bytes
| None = None, json_data: Dict | None = None) ->
ClientResponse
Like delete() but returns a coroutine.
download_file(file_hash: str, file: BinaryIO) -> None
Downloads a file given its _ (SHA−256, SHA−1 or MD5).
The file
identified by the hash will be written to the provided file
object. The file object must be opened in write binary mode
('wb').
Parameters
|
• |
file_hash (str) −− File hash. | ||
|
• |
file (file−like object) −− A file object where the downloaded file will be written to. |
async
download_file_async(file_hash: str, file: BinaryIO) ->
None
Like download_file() but returns a coroutine.
download_zip_files(hashes:
List[str], zipfile: BinaryIO,
password: str | None = None, sleep_time: int = 20) ->
None
Creates a bundle zip bundle containing one or multiple files.
The file
identified by the hash will be written to the provided file
object. The file object must be opened in write binary mode
('wb').
Parameters
|
• |
hashes −− list of file hashes (SHA−256, SHA−1 or MD5). | ||
|
• |
zipfile −− A file object where the downloaded zip file will be written to. | ||
|
• |
password −− optional, a password to protect the zip file. | ||
|
• |
sleep_time −− optional, seconds to sleep between each request. |
feed(feed_type: FeedType, cursor: str | None = None) -> Feed
Returns an iterator for a VirusTotal feed.
This functions
returns an iterator that allows to retrieve a continuous
stream of files as they are scanned by VirusTotal. See the
documentation for the Feed class for more details.
Parameters
|
• |
feed_type −− One of the supported feed types enumerated in FeedType. | ||
|
• |
cursor (str) −− An optional cursor indicating where to start. This argument can be a string in the format 'YYYMMDDhhmm' indicating the date and time of the first package that will be retrieved. |
get(path: str, *path_args:
Any, params: Dict | None = None) ->
ClientResponse
Sends a GET request to a given API endpoint.
This is a
low−level function that returns a raw HTTP response,
no error checking nor response parsing is performed. See
get_json(), get_data() and get_object()
for higher−level functions.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
params (dict) −− Parameters sent in the request. |
Returns
An instance of ClientResponse.
async get_async(path: str,
*path_args: Any, params: Dict | None
= None) -> ClientResponse
Like get() but returns a coroutine.
get_data(path: str,
*path_args: Any, params: Dict | None = None)
-> Any
Sends a GET request to a given API endpoint and returns response's data.
Most VirusTotal API responses are JSON−encoded with the following format:
{"data": <response data>}
This function
parses the server's response and return only the data, if
the response is not in the expected format an exception is
raised. For endpoints where the data is a VirusTotal object
you can use get_object() instead.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
params (dict) −− Parameters sent in the request. |
Returns
Whatever the server returned in the response's data field, it may be a dict, list, string or some other Python type, depending on the endpoint called.
async get_data_async(path:
str, *path_args: Any, params: Dict |
None = None) -> Any
Like get_data() but returns a coroutine.
async
get_error_async(response: ClientResponse)
-> APIError |
None
Given a ClientResponse returns a APIError
This function
checks if the response from the VirusTotal backend was an
error and returns the appropriate APIError or None if
no error occurred.
Parameters
response −− A ClientResponse instance.
Returns
An instance of APIError or None.
get_json(path: str,
*path_args: Any, params: Dict | None = None)
-> Dict
Sends a GET request to a given API endpoint and parses the response.
Most VirusTotal
API responses are JSON−encoded. This function parses
the JSON, check for errors, and return the server response
as a dictionary.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
params (dict) −− Parameters sent in the request. |
Returns
A dictionary with the backend's response.
async get_json_async(path:
str, *path_args: Any, params: Dict |
None = None) -> Dict
Like get_json() but returns a coroutine.
get_object(path: str,
*path_args: Any, params: Dict | None =
None) -> Object
Sends a GET request to a given API endpoint and returns an object.
The endpoint
specified must return an object, not a collection. This
means that get_object can be used with endpoints like
/files/{file_id} and /urls/{url_id}, which return an
individual object but not with /comments, which returns a
collection of objects.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
params (dict) −− Parameters sent in the request. |
Returns
An instance of Object.
async get_object_async(path:
str, *path_args: Any, params: Dict
| None = None) -> Object
Like get_object() but returns a coroutine.
iterator(path: str,
*path_args: Any, params: Dict | None = None,
cursor: str | None = None, limit: int | None = None,
batch_size:
int = 0) -> Iterator
Returns an iterator for the collection specified by the given path.
The endpoint
specified by path must return a collection of objects. An
example of such an endpoint are /comments and
/intelligence/search.
Parameters
|
• |
path (str) −− Path to API endpoint returning a collection. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
params (dict) −− Additional parameters passed to the endpoint. | ||
|
• |
cursor (str) −− Cursor for resuming the iteration at the point it was left previously. A cursor can be obtained with Iterator.cursor(). This cursor is not the same one returned by the VirusTotal API. | ||
|
• |
limit (int) −− Maximum number of objects that will be returned by the iterator. If a limit is not provided the iterator continues until it reaches the last object in the collection. | ||
|
• |
batch_size (int) −− Maximum number of objects retrieved on each call to the endpoint. If not provided the server will decide how many objects to return. |
Returns
An instance of Iterator.
patch(path: str, *path_args:
Any, data: str | bytes | None =
None, json_data: Dict | None = None) ->
ClientResponse
Sends a PATCH request to a given API endpoint.
This is a
low−level function that returns a raw HTTP response,
no error checking nor response parsing is performed. See
patch_object() for a higher−level function.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
data (A string or bytes) −− Data sent in the request body. | ||
|
• |
json_data (dict) −− dict containing data to send in the request body as JSON. |
Returns
An instance of ClientResponse.
async patch_async(path: str,
*path_args: Any, data: str | bytes
| None = None, json_data: Dict | None = None) ->
ClientResponse
Like patch() but returns a coroutine.
patch_object(path: str, *path_args: Any, obj: Object) -> Object
Sends a PATCH request for modifying an object.
This function
modifies an object. The endpoint must be one that identifies
an object, like /intelligence/hunting_rulesets/{id}.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
obj (Object) −− Object that has been modified. |
Returns
An instance of Object representing the same object after the changes has been applied.
async
patch_object_async(path: str, *path_args: Any, obj:
Object) -> Object
Like patch_object() but returns a coroutine.
post(path: str, *path_args:
Any, data: str | bytes | None =
None, json_data: Dict | None = None) ->
ClientResponse
Sends a POST request to a given API endpoint.
This is a
low−level function that returns a raw HTTP response,
no error checking nor response parsing is performed. See
post_object() for a higher−level function.
Parameters
|
• |
path (str) −− Path to API endpoint, can contain format placeholders {}. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
data (A string or bytes) −− Data sent in the request body. | ||
|
• |
json_data (dict) −− dict containing data to send in the request body as JSON. |
Returns
An instance of ClientResponse.
async post_async(path: str,
*path_args: Any, data: str | bytes |
None = None, json_data: Dict | None = None) ->
ClientResponse
Like post() but returns a coroutine.
post_object(path: str, *path_args: Any, obj: Object) -> Object
Sends a POST request for creating an object.
This function
create a new object. The endpoint must be one that
identifies a collection, like
/intelligence/hunting_rulesets.
Parameters
|
• |
path (str) −− Path to API endpoint. | ||
|
• |
path_args −− A variable number of arguments that are put into any placeholders used in path. | ||
|
• |
obj (Object) −− Instance Object with the type expected by the API endpoint. |
Returns
An instance of Object representing the new object.
async
post_object_async(path: str, *path_args: Any, obj: Object)
-> Object
Like post_object() but returns a coroutine.
scan_file(file: BinaryIO,
wait_for_completion: bool = False) ->
Object
Scans a file.
Parameters
|
• |
file (File−like object.) −− File to be scanned. | ||
|
• |
wait_for_completion (bool) −− If True the function doesn't return until the analysis has been completed. |
Returns
An instance of Object of analysis type.
async scan_file_async(file:
BinaryIO, wait_for_completion: bool
= False) -> Object
Like scan_file() but returns a coroutine.
scan_file_private(file:
BinaryIO | str, wait_for_completion:
bool = False) -> Object
Scan file privately.
|
Args: |
file: File to scan (path string or file object) wait_for_completion: Wait for completion |
Returns:
Object: Analysis object with scan results
async
scan_file_private_async(file: BinaryIO | str,
wait_for_completion: bool = False) -> Object
Async version of scan_file_private
scan_url(url: str, wait_for_completion: bool = False) -> Object
Scans a URL.
Parameters
|
• |
url (str) −− The URL to be scanned. | ||
|
• |
wait_for_completion (bool) −− If True the function doesn't return until the analysis has been completed. |
Returns
An instance of Object of analysis type.
async scan_url_async(url:
str, wait_for_completion: bool =
False) -> Object
Like scan_url() but returns a coroutine.
scan_url_private(url: str,
wait_for_completion: bool = False) ->
Object
Scans a URL privately.
Parameters
|
• |
url (str) −− The URL to be scanned. | ||
|
• |
wait_for_completion (bool) −− If True the function doesn't return until the analysis has been completed. |
Returns
An instance of Object of analysis type.
async
scan_url_private_async(url: str, wait_for_completion: bool
= False) -> Object
Like scan_url_private() but returns a coroutine.
class vt.ClientResponse(aiohttp_resp: ClientResponse)
Class representing the HTTP responses returned by the client.
This class is just a thing wrapper around aiohttp.ClientResponse that allows using it in both asynchronous and synchronous mode. Instances of this class have all the attributes that you can find in aiohttp.ClientResponse, like version, status, method, url, and so on. Methods in aiohttp.ClientResponse that return a coroutine have two flavors in this class: synchronous and asynchronous. For example, aiohttp.ClientResponse.read() becomes vt.ClientResponse.read_async(), and vt.ClientResponse.read() is the synchronous version of vt.ClientResponse.read_async(). Find more information about attributes and methods in aiohttp.ClientResponse in:
- https://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.ClientResponse
NOTE:
This feature is available only for premium users.
VT module.
class vt.Feed(client: Client, feed_type:
FeedType, cursor: str | None =
None)
Feed represents a stream of objects received from VirusTotal in real−time.
For more information about VirusTotal Feeds see: - https://docs.virustotal.com/reference/file−feed
In the example below the loop iterates forever, retrieving file objects as they are processed by VirusTotal. For a more elaborate example see the file examples/file_feed.py in this repository.
>>>
with vt.Client(<apikey>) as client:
>>> for file_obj in client.feed(vt.FeedType.FILES):
>>> print(file_obj.id)
Instances of
this class are not created directly, you should use the
vt.Client.feed() method instead.
property cursor: str
Returns a cursor indicating the last item retrieved from the feed.
This cursor can be used for creating a new Feed object that continues where a previous one left.
class vt.FeedType(*values)
Feed types.
FILES = 'files'
FILE_BEHAVIOURS = 'file−behaviours'
URLS = 'urls'
VT module.
class vt.Object(obj_type: str, obj_id: str | None = None,
obj_attributes: Dict | None = None)
This class encapsulates any type of object in the VirusTotal API.
Instances of this class are usually obtained from calls to vt.Client.get_object(), however, you need to instantiante this class yourself for creating new objects that will be sent to the backend in a call to vt.Client.post_object().
Learn more
about objects in the VirusTotal API in: -
https://docs.virustotal.com/reference/objects
classmethod from_dict(obj_dict: Dict)
Creates an object from its dictionary representation.
The dictionary representation of a VirusTotal API object has the following structure:
{
"type": <object type>,
"id": <object id>,
"links": {
"self":
"https://www.virustotal.com/api/v3/<collection
name>/<obj id>"
},
"attributes": {
...
}
}
At least type and id are required to be present in the dictionary, if not, an exception is raised.
get(attr_name: str, default: Any | None = None) -> Any
Returns an attribute by name.
If the
attribute is not present in the object, it returns None or
the value specified in the "default" argument.
Parameters
|
• |
attr_name (str) −− Name of the attribute. | ||
|
• |
default −− An optional value that will be returned if the attribute is not present in the object. |
set_data(key: str, value: Any)
Sets a field of the object's data.
VT module.
class vt.Iterator(client: Client, path: str,
params=None, cursor: str |
None = None, limit: int | None = None, batch_size: int =
0)
Iterator allows iterating over object collections.
Some endpoints in the VirusTotal API represent a collection of objects, for example:
/files/{id}/comments
/intelligence/search
These collections can be iterated using an instance of this class.
Learn more about collections in the VirusTotal API in: - https://docs.virustotal.com/reference/collections−create
The following example iterates over the most recent 200 comments, retrieving them in batches of 20:
>>>
client = vt.Client(<apikey>)
>>> it = client.iterator('/comments',
batch_size=20, limit=200)
>>> for comment in it:
>>> print(comment.text)
>>> print(it.cursor)
When the iteration is done, it print the iterator's cursor. The cursor can be used for creating another iterator that continues at the point where the previous iterator left.
The Iterator class also exposes an async iterator:
>>> #
Define an async coroutine that iterates over the comments.
>>> async def print_comments():
>>> async for comment in
client.iterator('/comments', limit=200):
>>> print(comment.id)
>>> # Run the print_comments coroutine using
asyncio
>>> import asyncio
>>>
asyncio.get_event_loop().run_until_complete(print_comments)
property cursor: str | None
Cursor indicating the last returned object.
This cursor can be used for creating a new iterator that continues where the current one left.
property meta_async: Dict
Meta information.
The cursor is not included, as it's exposed as a property.
VT module.
vt.url_id(url: str) -> str
Generates the object ID for an URL.
The ID generated by this function can be used in calls that expect a URL ID like client.get_object('/urls/<id>')
VirusTotal
2023, VirusTotal