No sections match. Try “quota”, “URL” or “cursor”.
Your first query
Request an API key and confirm its permissions with VirusSign. Use the public API base URL below. Use your own issued key in the examples.
https://api.virussign.comSet VIRUSSIGN_API_KEY in your local environment, then query an indicator. The examples use example.com as a placeholder.
curl --get "https://api.virussign.com/v1/ioc/domains/example.com" \
--header "x-api-key: $VIRUSSIGN_API_KEY"import os
import requests # pip install requests
response = requests.get(
"https://api.virussign.com/v1/ioc/domains/example.com",
headers={"x-api-key": os.environ["VIRUSSIGN_API_KEY"]},
timeout=(5, 30),
)
if response.status_code == 404:
print("No available record; this does not mean safe.")
else:
response.raise_for_status()
result = response.json()
print(result.get("indicator", {}))Use the OpenAPI specification
The OpenAPI 3.1 specification (JSON) describes the API’s endpoints, parameters, authentication and response structures. Import it into an API client such as Postman to prepare requests, or use it with compatible tools to generate integration code. It contains no intelligence records or API keys.
Get started in Postman
- Download the JSON file using Download OpenAPI.
- In Postman, select Import and choose the file. Import it as a collection, or generate a collection from the imported specification.
- Check that the API base URL is
https://api.virussign.com. Set the collection’s API Key authorization header tox-api-keyand use a local secret variable for your issued key. - Choose an endpoint, replace its example or placeholder indicator, and send the request. The request must be within your key’s permissions and uses your API quota.
Downloading or importing the specification does not make API requests or grant access. You can also follow the cURL and Python examples directly without downloading it. Generated clients may need adjustments for optional response fields and pagination.
Authentication & access
You can also connect your API key to this website to investigate indicators using your account’s permissions and allowance. Website queries and direct API requests share the same quota.
Send your API key in the x-api-key HTTP header over HTTPS. Keep the key in server-side configuration or an environment variable. Do not embed it in public JavaScript, page URLs or source control.
| Access | Behavior |
|---|---|
| IOC and relationship queries | Use /v1/ioc/… with your issued API key. Available fields depend on the indicator, requested view and your account permissions. |
| Feed events | Use /v1/feed/events with a key that has feed permission. IOC access alone does not include feed access. |
| Trial scope | The approval message defines permissions, field availability, quota and expiry. Some fields may be filtered for evaluation accounts. |
The public website and the API are separate access surfaces. Website access does not grant permission to redistribute intelligence or integrate it into a commercial service.
IOC lookup
Query one indicator per request. A successful response contains meta and indicator. To process a list, send individual requests with bounded concurrency.
| IOC type | Standard route | Input |
|---|---|---|
| File | GET /v1/ioc/files/{ioc_key} | MD5, SHA-1 or SHA-256 hash |
| Domain | GET /v1/ioc/domains/{ioc_key} | Domain name, without a URL scheme or path |
| IP | GET /v1/ioc/ips/{ioc_key} | IP address |
| URL | GET /v1/ioc/urls?q={encoded_url} | Complete URL encoded as a query parameter |
| Parameter | Suggested usage | Meaning |
|---|---|---|
| view | Optional | Omit for the service default. compact requests reduced detail; full requests more context; full+relations also requests related objects. URL-encode the plus sign. |
| limit | Start with 10; recommended 1–50 | Requested page size per relationship, not a combined total across all relationship types. Use the returned cursor to retrieve more items. |
Choose a response view
Most examples omit view and follow the service default. Use view=compact when a smaller set of fields is sufficient, for example when reviewing many related indicators. The exact fields and size depend on the indicator type and your access. Request full or full+relations only when you need additional context.
curl --get "https://api.virussign.com/v1/ioc/domains/example.com" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "view=compact"To include relationships in an individual lookup, request them explicitly and start with a small limit:
curl --get "https://api.virussign.com/v1/ioc/files/YOUR_SHA256" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "view=full+relations" \
--data-urlencode "limit=10"Query a complete URL
Encode the complete URL with your HTTP client. Place q last in the query string, as shown below, and preserve the original URL’s case, path and query parameters.
curl --get "https://api.virussign.com/v1/ioc/urls" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "q=https://example.com/path?a=1&b=2"response = requests.get(
"https://api.virussign.com/v1/ioc/urls",
headers={"x-api-key": os.environ["VIRUSSIGN_API_KEY"]},
params={"q": "https://example.com/path?a=1&b=2"},
timeout=(5, 30),
)
response.raise_for_status()For subsequent URL relationship requests, use the opaque URL identifier in the returned canonical link when available. Do not assume a URL identifier is a file hash or reconstruct it from a different normalization algorithm.
Responses & data fields
Illustrative response. Available fields vary by IOC type, access and view.
{
"meta": {
"request_id": "example-request-id",
"timestamp": "2026-09-30T00:00:00Z",
"quota_remaining": 9999,
"took_ms": 120
},
"indicator": {
"type": "file",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"filenames": [
"illustrative-sample.exe"
],
"analysis": {
"malicious": true
},
"first_seen": "2026-09-01T00:00:00Z",
"last_seen": "2026-09-29T00:00:00Z"
}
}| Field | Meaning |
|---|---|
| meta.request_id | Request identifier for support and correlation. |
| meta.timestamp | Response timestamp in UTC. |
| meta.quota_remaining | Remaining balance or cycle allowance when provided. |
| meta.took_ms | Server-reported processing duration in milliseconds. |
| indicator.type | Indicator type: file, ip, domain or url. |
| indicator.value | Canonical indicator value; file records normally use SHA-256. |
| indicator.analysis.malicious | Malicious classification flag; false is not a guarantee of safety. |
| indicator.analysis.severity | Severity classification when applicable and available. |
| indicator.analysis.confidence | Confidence signal when available; not a calibrated probability. |
| indicator.analysis.family | Family attribution when available. |
| indicator.analysis.detections | Detection ratio when available. |
| indicator.first_seen / last_seen | First/last observation represented by this record; not necessarily the start or end of malicious activity. |
| indicator.relations | Optional groups of related objects when requested. |
| items / has_next / next_cursor | Standalone relationship result items and pagination metadata when available. |
Type-specific context
| Indicator | Examples of additional fields |
|---|---|
| File | md5, sha1, sha256, filenames, filesize, filetype, tags and observation timestamps. |
| IP | IP version, country, tags, analysis and observation timestamps. |
| Domain | Domain identity, tld, tags, analysis and observation timestamps. |
| URL | URL identity, host, scheme, port, path_qs, tags, analysis and observation timestamps. |
Relationships & pagination
/v1/ioc/{collection}/{ioc_key}/{relation}Replace each placeholder with the value for your request:
| Placeholder | Value |
|---|---|
| collection | Source IOC collection: files, ips, domains or urls. |
| ioc_key | Source indicator: a file hash, IP address, domain name or URL identifier. See IOC lookup for accepted identifiers. |
| relation | A supported relationship name from the table below, such as related_domains. |
Relationship queries accept cursor, limit and view. Choose a relationship supported by the source indicator type below. Start with limit=10; for initial integrations, keep pages at 50 or fewer and contact us before requesting larger pages.
| Relation | Related object | Source IOC types |
|---|---|---|
| related_files | Related files | ips, domains, urls |
| related_ips | IP addresses | files, domains, urls |
| related_domains | Domains | files, ips, urls |
| related_urls | URLs | files, ips, domains |
Available relationships vary by API key tier. Pro and Advanced access offer a broader range of relationship intelligence. Request API access to explore the options.
Not every relation applies to every IOC or permission level. A supported relation may have no items; an unsupported relation can return 404. Follow the returned next_cursor while has_next is true. Keep the IOC, relation, view and other filters unchanged while paging. Treat the cursor as opaque.
curl --get "https://api.virussign.com/v1/ioc/files/YOUR_SHA256/related_domains" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "limit=10"import os
import requests
endpoint = "https://api.virussign.com/v1/ioc/files/YOUR_SHA256/related_domains"
params = {"view": "compact", "limit": 10}
seen_cursors = set()
with requests.Session() as session:
session.headers["x-api-key"] = os.environ["VIRUSSIGN_API_KEY"]
for _ in range(10): # Explicit evaluation page budget.
response = session.get(endpoint, params=params, timeout=(5, 30))
response.raise_for_status()
page = response.json()
for item in page.get("items", []):
print(item) # Replace with idempotent processing.
if not page.get("has_next"):
break
cursor = page.get("next_cursor")
if not cursor or cursor in seen_cursors:
raise RuntimeError("Missing or repeated pagination cursor")
seen_cursors.add(cursor)
params["cursor"] = cursor
# Persist this cursor only after processing the page.
else:
print("Page budget reached; save the cursor before continuing.")Record your checkpoint only after successfully processing a page. Make ingestion idempotent so that retrying a page does not create duplicate records. Embedded relationship lists in a detail response can be truncated; use standalone paging for additional results.
Feed events
/v1/feed/eventsKeep your local intelligence up to date with additions, changes and revocations. Feed access requires an API key with feed permission.
Explore a feed sample before integrating. No API key required.
Choose a time window
For your first request, supply since or until. We recommend starting with a recent since and an explicit limit=50. Use since and until together for a completed window, or omit until for ongoing polling.
Use ISO 8601 timestamps with a timezone and whole seconds, such as 2026-10-04T00:00:00Z. The start is inclusive and the end is exclusive. These bounds apply to the feed event time, rather than the indicator's first or last observation.
curl --get "https://api.virussign.com/v1/feed/events" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "mode=events" \
--data-urlencode "types=file,host,url" \
--data-urlencode "since=2026-10-04T00:00:00Z" \
--data-urlencode "until=2026-10-04T01:00:00Z" \
--data-urlencode "limit=50"| Parameter | Usage |
|---|---|
| mode | events returns additions, updates and revocations. snapshot returns current, unexpired upsert entries within the requested time window. |
| types | Comma-separated file, host and/or url. host includes both domains and IP addresses. Defaults to all three. |
| since | Inclusive start time. Use a timezone-aware ISO 8601 timestamp with whole seconds, for example 2026-10-04T00:00:00Z. |
| until | Exclusive end time, later than since. Omit for ongoing polling. |
| cursor | Opaque continuation cursor. Use the returned value unchanged with the same account, mode, format and types. The cursor retains the time window; omit it to start a new window. |
| limit | Maximum events per page. Start with 50 and set it explicitly to control page size. Charges use the actual number returned. |
| format | native returns JSON with meta and items. stix returns a STIX 2.1 bundle with paging information in response headers. |
| view | compact returns core event information. full adds available context; native full responses also include STIX identifiers and patterns. |
Read a native JSON page
The default format returns meta and items. Events arrive in feed timestamp order. Apply upsert to add or update an indicator and revoke to withdraw it from your active feed dataset. Use each event's id for deduplication.
{
"meta": {
"request_id": "example-feed-request",
"timestamp": "2026-10-04T01:05:00Z",
"limit": 50,
"returned": 1,
"has_next": false,
"next_cursor": "OPAQUE_CHECKPOINT",
"quota_remaining": 999,
"took_ms": 80
},
"items": [
{
"id": "event--c65a54c0-1d37-54d0-b5c3-4700e3e0c7cc",
"timestamp": "2026-10-04T00:30:00Z",
"action": "upsert",
"reason": "CTX",
"indicator": {
"type": "domain",
"value": "example.com"
},
"analysis": {
"malicious": true,
"confidence": 70
},
"validity": {
"first_seen": "2026-10-03T12:00:00Z",
"last_seen": "2026-10-04T00:25:00Z"
}
}
]
}| Field | Meaning |
|---|---|
| meta.returned | Number of events returned on this page. |
| meta.has_next | Whether more matching events are available now. |
| meta.next_cursor | Continuation checkpoint. May still be present when has_next is false; may be null on an empty first page. |
| meta.quota_remaining | Remaining token balance or cycle allowance after this query is charged. |
| items[].id / timestamp | Event identifier for deduplication and feed event time in UTC. The time window filters this timestamp. |
| items[].action | upsert adds or updates the indicator in your local dataset. revoke withdraws it from the active feed; retain the event for your history. |
| items[].reason | Reason code describing the change, when available. |
| items[].indicator | Indicator type and value, with file hashes or an IP version where applicable. host results use type domain or ip. |
| items[].analysis / validity / tags | Classification, observation and expiration times, and tags when available. |
| items[].context / stix | Additional context and STIX references in native full responses, when available. |
Continue and resume
- Process the page, then save
meta.next_cursoras your checkpoint when provided. - While
meta.has_nextis true, request the next page using that cursor. Keep the same account,mode,formatandtypes. - When
has_nextis false, stop paging through a completed window. For an open-ended feed, pause before polling again with the latest cursor.
has_next is false. Its presence does not mean another page is available now. Empty queries are charged, so avoid continuous polling at the end of the feed.The cursor retains your time window. To start a different window, omit the cursor and supply new time bounds. If an empty first page has no cursor, keep your original since when polling again. For a future until, pause at the current end and resume later within that same window.
curl --get "https://api.virussign.com/v1/feed/events" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "mode=events" \
--data-urlencode "types=file,host,url" \
--data-urlencode "cursor=YOUR_NEXT_CURSOR" \
--data-urlencode "limit=50"This example processes a completed window with a ten-page budget. Replace the printed events with your own processing, and save each checkpoint only after its page has been successfully processed.
import os
import requests # pip install requests
endpoint = "https://api.virussign.com/v1/feed/events"
params = {
"mode": "events",
"types": "file,host,url",
"since": "2026-10-04T00:00:00Z",
"until": "2026-10-04T01:00:00Z",
"limit": 50,
}
# To resume this window, set params["cursor"] to your saved checkpoint.
with requests.Session() as session:
session.headers["x-api-key"] = os.environ["VIRUSSIGN_API_KEY"]
for _ in range(10): # Explicit page budget for evaluation.
response = session.get(endpoint, params=params, timeout=(5, 60))
response.raise_for_status()
page = response.json()
for event in page["items"]:
print(event) # Replace with durable, idempotent processing.
meta = page["meta"]
cursor = meta.get("next_cursor")
if cursor:
# Save only after all events on this page are processed.
print("Checkpoint:", cursor)
print("Remaining allowance:", meta.get("quota_remaining"))
if not meta["has_next"]:
break # Finished this completed time window.
if not cursor or cursor == params.get("cursor"):
raise RuntimeError("Missing or repeated feed cursor")
params["cursor"] = cursor
else:
print("Page budget reached; resume from the last checkpoint.")
Snapshot mode
Use mode=snapshot to retrieve current, unexpired upsert entries within the selected time window. Use mode=events for ongoing change tracking, including revocations. Start a new request without a cursor when switching modes.
STIX output
Set format=stix to receive a STIX 2.1 bundle. Read indicators from objects; revocations are represented by revoked=true. Pagination and remaining quota are supplied in response headers instead of a meta object. The media type is application/stix+json;version=2.1.
curl --get --include "https://api.virussign.com/v1/feed/events" \
--header "x-api-key: $VIRUSSIGN_API_KEY" \
--data-urlencode "mode=events" \
--data-urlencode "types=file,host,url" \
--data-urlencode "since=2026-10-04T00:00:00Z" \
--data-urlencode "until=2026-10-04T01:00:00Z" \
--data-urlencode "limit=50" \
--data-urlencode "format=stix"| Response header | Meaning |
|---|---|
| X-Limit | Requested page size. |
| X-Has-More | 1 means more events are available now; 0 means the current end has been reached. |
| X-Next-Cursor | Continuation checkpoint, when available. Its presence alone does not mean another page is available. |
| X-Quota-Remaining | Remaining token balance or cycle allowance after charging. |
| Link | Continuation URL with rel="next", when a cursor is available. Check X-Has-More before following it immediately. |
Use X-Has-More to control paging, and save X-Next-Cursor after processing the bundle. Selecting STIX does not change the billing rule. See Quotas & usage for examples.
Quotas & usage
Your access plan defines permissions, balance or cycle allowance, and any expiration. IOC lookups and feed queries use different charging rules.
IOC and relationship queries
IOC lookups and standalone relationship pages each use one query unit. Usage is based on accepted queries, including lookups with no matching record. A request that fails or times out after processing begins may also use quota; each retry is a separate request.
| Operation | Query units |
|---|---|
| IOC detail | 1 per accepted lookup, including any relationships included in that response. |
| Standalone relationship page | 1 per accepted page, regardless of the number of related items returned. |
| No matching IOC | 1 when the lookup is processed. |
Feed queries
Feed queries are charged after the query completes, based on events returned: one query unit per started block of 50 events, with a minimum of one unit per request, including empty results. At the standard rate, one query unit costs one token. Your access plan defines any account-specific rate.
| Events returned on one page | Tokens at the standard rate |
|---|---|
| 0 | 1 |
| 1–50 | 1 |
| 51–100 | 2 |
| 101–150 | 3 |
For example, a request with limit=50 that returns 12 events costs one token. The minimum applies separately to every page and polling request. Charges are deducted after the feed query completes; delivery interruptions or client timeouts do not necessarily prevent a charge.
Remaining allowance
Token plans apply the token cost per query unit defined in your plan. Cycle plans deduct the same query units from the current cycle allowance. Native responses report the remaining amount in meta.quota_remaining; STIX feed responses use X-Quota-Remaining.
Rate limits and total quota are separate controls. Handle 429 even when your balance is positive. Use bounded retries and allow a pause between feed polls.
Errors & retries
{
"detail": {
"error": {"code": 7, "message": "quota exceeded"}
}
}| HTTP status | Meaning | Action |
|---|---|---|
| 400 | Invalid parameters, cursor or limit | Correct the request before retrying. |
| 401 | Missing or invalid API key | Check the x-api-key header and issued credentials. |
| 403 | Quota exceeded, account disabled or permission denied | Inspect detail.error.code and message; request quota or permissions as needed. |
| 404 | IOC or relationship not found | Treat as no available result, not a safe verdict. Check type and relation. |
| 422 | Request validation error | Inspect the validation details for incorrect parameter types or ranges. |
| 429 | Rate limit exceeded | Respect Retry-After if present; otherwise use bounded exponential backoff with jitter. |
| 500 / 502 / 503 / 504 | Server or upstream failure | Retry conservatively; keep retry count bounded and remember a retry may consume quota. |
Use explicit connection and read timeouts. A client timeout does not prove that the server cancelled a query or refunded its cost. Avoid unlimited retry loops.
Integration checklist & support
- Verify the issued key’s endpoints, permissions, quota and expiration.
- Start with a small, representative set of your own indicators.
- Tolerate omitted optional fields and unknown additional fields.
- Limit concurrency, handle errors and persist pagination checkpoints.
- Measure coverage and usefulness separately from detection accuracy.
- Confirm commercial integration, caching and redistribution rights before production use.
When reporting an issue, include the endpoint, UTC timestamp, HTTP status and meta.request_id if available. Never send your API key. Remove sensitive indicators unless they are necessary for troubleshooting.
API v1 · Contact us for integration help or to review your access plan.