A Python SDK for the Validin API, designed to enable large-scale monitoring and enrichment workflows.
- Python 3.9+
- A Validin API key
pip install validin-sdkfrom validin import ClientThe client requires an API key. The simplest approach is to set environment variables:
export VALIDIN_API_KEY="your-api-key"Enterprise users must also set their endpoint:
export VALIDIN_BASE_URL="https://your-enterprise-endpoint.example.com"You can also pass these directly to the client:
from validin import Client
client = Client(api_key="your-api-key", base_url="https://your-enterprise-endpoint.example.com")If base_url is not provided, the client defaults to https://app.validin.com.
Monitor a DNS TXT record query and add new results to a project:
from validin import Client
client = Client()
results = client.extra_history("anthropic-domain-verification*", lookback=1)
client.add_to_project("YOUR_PROJECT_ID", results.values)For more advanced workflows, see the examples/ directory.
An Indicator represents a queryable entity: a domain, IP address, hash, or raw string. The SDK automatically infers the type from a string, so you can pass raw strings directly to any client method:
client.dns_history("example.com") # inferred as domain
client.dns_history("1.1.1.1") # inferred as IP
client.extra_history("some-txt-value*") # inferred as string (wildcard)A ResultSet is the container returned by every query method. It provides:
- Iteration — loop over individual records (
for record in results) .keys— deduplicated list ofIndicatorobjects from each record (the domains/IPs represented).keys_str— string values of.keys.values— deduplicated list of record values.to_rows()— convert all records to a list of dictionaries.aggregate()— flatten nested bulk results into a single deduplicated ResultSet
Each query method returns a ResultSet containing typed record objects. Available record types:
DNSRecordCertificateRecordHostResponseRecordScanRecordEnrichedIndicator(from reputation lookups)AnnotationRecord(nested insideEnrichedIndicator)LookalikeRecordRegistrationRecordYaraMatchRecordContent
An EnrichedIndicator is a subclass of Indicator that carries reputation and annotation data directly on the indicator itself. You encounter enriched indicators in two places:
1. Enrichment lookups — client.enrich() returns a ResultSet where each record is an EnrichedIndicator with score, verdict, and annotations:
results = client.enrich("example.com")
enriched = results[0]
print(enriched.value) # "example.com"
print(enriched.score) # 0
print(enriched.verdict) # "benign"
print(enriched.annotations) # tuple of AnnotationRecord objects
print(enriched.to_rows()) # annotations flattened to list of dictsFor bulk enrichment, use .keys to iterate the EnrichedIndicator objects:
results = client.enrich(["example.com", "1.1.1.1", "google.com"])
for enriched in results.keys:
print(f"{enriched.value}: score={enriched.score}, verdict={enriched.verdict}")
print(enriched.to_rows())2. History queries with annotate=True — When you pass annotate=True to dns_history(), extra_history(), host_responses(), or registration_history(), the key and value fields on returned records are upgraded from plain Indicator to EnrichedIndicator when the API provides intel data:
results = client.dns_history("example.com", annotate=True)
for record in results:
if isinstance(record.value, EnrichedIndicator):
print(f"{record.value.value} has {len(record.value.annotations)} annotations")
print(record.value.informational)Since EnrichedIndicator is a subclass of Indicator, enriched indicators work everywhere a plain indicator does — you can pass them directly to add_to_project(), use them as input to other queries, or access .value and .type as usual.
Any method that supports bulk queries can also accept a list of indicators instead of a single value:
results = client.enrich(["example.com", "1.1.1.1", "google.com"])When you pass a list, the SDK:
- Rate-limits requests to 5 queries/second
- Retries transient failures (408, 429, 500, 502, 503, 504) with exponential backoff
- Silently skips 404 responses
The returned ResultSet contains one child ResultSet per indicator. To flatten all results into a single deduplicated list, call .aggregate():
results = client.dns_history(["example.com", "google.com", "cloudflare.com"])
# Iterate per-indicator results
for child_result_set in results:
print(child_result_set.query_key, len(child_result_set))
# Or flatten into one deduplicated set
all_records = results.aggregate()
print(all_records.to_rows())DNS record history for a domain, IP, or string.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/dns/history/:domain |
| IP | /api/axon/ip/dns/history/:ip |
| String | /api/axon/string/dns/history |
Supports bulk queries. Options: limit, first_seen, last_seen, lookback, annotate, wildcard, categories_include, exclude_nx.
Extended DNS record history (TXT, SPF, etc.) for a domain, IP, or string.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/dns/extra/:domain |
| IP | /api/axon/ip/dns/extra/:ip |
| String | /api/axon/string/dns/extra2 |
Supports bulk queries. Options: limit, first_seen, last_seen, lookback, annotate, wildcard, categories_include, exclude_nx.
HTTP crawl/response history for a domain, IP, hash, or string.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/crawl/history/:domain |
| IP | /api/axon/ip/crawl/history/:ip |
| Hash | /api/axon/hash/crawl/history/:hash |
| String | /api/axon/string/crawl/history |
Supports bulk queries. Options: limit, lookback, annotate.
Enrich a domain or IP with reputation score, verdict, and annotations. Returns a ResultSet of EnrichedIndicator objects (one per queried indicator), each carrying score, verdict, annotations, and informational fields.
Also available as client.reputation() (alias).
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/reputation/quick/:domain |
| IP | /api/axon/ip/reputation/quick/:ip |
Supports bulk queries. No additional options.
Find newly registered lookalike domains matching a domain or regex pattern.
| Target Type | API Route |
|---|---|
| Domain | /api/lookalike/domain/:domain |
| Regex | /api/lookalike/regex |
Does not support bulk queries. Options: exclude, limit (1–250), lookback (1–90 days), depth (labels, subdomains, fqdns), similarity (0–4, domain targets only).
Regex targets can be passed as a compiled re.Pattern or a slash-delimited string:
client.lookalikes("/^(t-mobile|att|verizon)\.([a-z]{5,6})\.(icu|cc)$/")Historic certificate transparency matches for a domain.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/certificates/:domain |
Does not support bulk queries. Options: wildcard, limit (1–20000), first_seen, last_seen, lookback, time_format (unix or iso).
Historic PTR/hostname records for a domain or IP.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/dns/hostname/:domain |
| IP | /api/axon/ip/dns/hostname/:ip |
Supports bulk queries. Options: limit, first_seen, last_seen, lookback, annotate, time_format (unix or iso), and wildcard for domain queries only.
Start an enterprise live HTTP/S scan and return a ScanJob.
| API Route |
|---|
/api/axon/live/scan/start |
Does not support bulk queries. Options: countries, refer, user_agent.
Fetch live scan status and return a ScanJob.
| API Route |
|---|
/api/axon/live/scan/results/:scan_id |
Does not support bulk queries.
Convenience wrapper around start_scan(). By default it waits for completion and returns a ResultSet of ScanRecord objects. Pass wait=False to get a ScanJob immediately.
| API Route |
|---|
/api/axon/live/scan/start -> poll results -> crawl |
Options: countries, refer, user_agent, wait, poll_interval, timeout.
WHOIS/RDAP registration history for a domain or string.
| Indicator Type | API Route |
|---|---|
| Domain | /api/axon/domain/registration/history/:domain |
| String | /api/axon/string/registration/history2 |
Supports bulk queries. Options: limit, first_seen, last_seen, lookback, annotate, wildcard, include_fields.
Fetch YARA rule match results from a project. Auto-paginates all results by default.
| API Route |
|---|
/api/project/:project_id/yara/rules/:rule_id/matches |
Does not support bulk queries. Options: page, page_size (provide both for manual pagination), include_html, first_seen, last_seen.
Fetch stored HTML content by body hash.
| API Route |
|---|
/api/axon/hash/content/html/sha1/:hash |
Supports bulk queries. Accepts a hash string, Indicator, or HostResponse object with a body_hash.
Add indicators to a Validin project.
| API Route |
|---|
/api/project/:project_id/indicators/add |
Accepts a single indicator or a list. Options: note, tags.
Example scripts are in the examples/ directory:
**lookalike_monitor.py**— Regex lookalike search for telecom smishing domains, filter by registrar, add to project.**dns_txt_record_monitor.py**— Monitor a DNS TXT record wildcard query and add results to a project.**bulk_indicator_enrichment.py**— Bulk enrichment lookup across multiple indicators.**ingest_yara_matches.py**— Fetch all YARA rule matches for a project and print aggregated results.**sample_host_responses.py**— Bulk host response history with HTML content download.
The SDK raises ApiError for HTTP failures and ValidinError as a base exception class:
from validin import Client
from validin_sdk.errors import ApiError, ValidinError
client = Client()
try:
results = client.dns_history("example.com")
except ApiError as e:
print(f"HTTP {e.status_code}: {e.message}")
except ValidinError as e:
print(f"SDK error: {e}")The SDK currently covers 24 of 76 API endpoints.
| Status | Method | Endpoint | Summary | SDK Method |
|---|---|---|---|---|
| Bulk | ||||
| ❌ | POST | /api/axon/bulk/osint/context |
Bulk OSINT Context | |
| Domain | ||||
| ✅ | GET | /api/axon/domain/certificates/{domain} |
Domain Certificates | certificates() |
| ✅ | GET | /api/axon/domain/crawl/history/{domain} |
Domain Crawl History | host_responses() |
| ✅ | GET | /api/axon/domain/dns/extra/{domain} |
Domain DNS Extra | extra_history() |
| ✅ | GET | /api/axon/domain/dns/history/{domain} |
Domain DNS History | dns_history() |
| ❌ | GET | /api/axon/domain/dns/history/{domain}/A |
Domain DNS History - A | |
| ❌ | GET | /api/axon/domain/dns/history/{domain}/AAAA |
Domain DNS History - AAAA | |
| ❌ | GET | /api/axon/domain/dns/history/{domain}/NS |
Domain DNS History - NS | |
| ❌ | GET | /api/axon/domain/dns/history/{domain}/NS_FOR |
Domain DNS History - NS_FOR | |
| ✅ | GET | /api/axon/domain/dns/hostname/{domain} |
Domain DNS Hostname | ptr_history() |
| ❌ | GET | /api/axon/domain/osint/history/{domain} |
Domain OSINT History | |
| ❌ | GET | /api/axon/domain/pivots/{domain} |
Domain Pivots | |
| ❌ | GET | /api/axon/domain/pivots/{domain}/{category} |
Domain Pivots - Category | |
| ✅ | GET | /api/axon/domain/registration/history/{domain} |
Domain Registration History | registration_history() |
| ❌ | GET | /api/axon/domain/registration/live/{domain} |
Domain Registration Live | |
| ✅ | GET | /api/axon/domain/reputation/quick/{domain} |
Domain Reputation Quick | enrich() |
| ❌ | GET | /api/axon/domain/subdomains/{domain} |
Domain Subdomains | |
| ✅ | GET | /api/lookalike/domain/{domain} |
Lookalike Domain | lookalikes() |
| ✅ | GET | /api/lookalike/regex |
Lookalike Regex | lookalikes() |
| ❌ | GET | /api/v2/domain/combined/connections/{domain} |
V2 Domain Combined Connections | |
| Hash | ||||
| ❌ | GET | /api/axon/hash/content/certificate/sha1/{hash} |
Hash Content Certificate (SHA1) | |
| ❌ | GET | /api/axon/hash/content/favicon/md5/{hash} |
Hash Content Favicon (MD5) | |
| ✅ | GET | /api/axon/hash/content/html/sha1/{hash} |
Hash Content HTML (SHA1) | fetch_content() |
| ✅ | GET | /api/axon/hash/crawl/history/{hash} |
Hash Crawl History | host_responses() |
| ❌ | GET | /api/axon/hash/pivots/{hash} |
Hash Pivots | |
| ❌ | GET | /api/axon/hash/pivots/{hash}/{category} |
Hash Pivots - Category | |
| IP | ||||
| ✅ | GET | /api/axon/ip/crawl/history/{ip} |
IP Crawl History | host_responses() |
| ❌ | GET | /api/axon/ip/crawl/history/{ip}/{cidr} |
IP Crawl History - (CIDR) | |
| ✅ | GET | /api/axon/ip/dns/extra/{ip} |
IP DNS Extra | extra_history() |
| ❌ | GET | /api/axon/ip/dns/extra/{ip}/{cidr} |
IP DNS Extra - (CIDR) | |
| ✅ | GET | /api/axon/ip/dns/history/{ip} |
IP DNS History | dns_history() |
| ❌ | GET | /api/axon/ip/dns/history/{ip}/{cidr} |
IP DNS History - (CIDR) | |
| ✅ | GET | /api/axon/ip/dns/hostname/{ip} |
IP DNS Hostname | ptr_history() |
| ❌ | GET | /api/axon/ip/dns/hostname/{ip}/{cidr} |
IP DNS Hostname - (CIDR) | |
| ❌ | GET | /api/axon/ip/osint/history/{ip} |
IP OSINT History | |
| ❌ | GET | /api/axon/ip/osint/history/{ip}/{cidr} |
IP OSINT History - (CIDR) | |
| ❌ | GET | /api/axon/ip/pivots/{ip} |
IP Pivots | |
| ❌ | GET | /api/axon/ip/pivots/{ip}/{cidr} |
IP Pivots - (CIDR) | |
| ❌ | GET | /api/axon/ip/pivots2/{ip}/{category} |
IP Pivots - Category | |
| ✅ | GET | /api/axon/ip/reputation/quick/{ip} |
IP Reputation Quick | enrich() |
| Projects | ||||
| ❌ | GET | /api/project/list |
Project List | |
| ❌ | GET | /api/project/{project_id} |
Project Details | |
| ❌ | GET | /api/project/{project_id}/alerts/dates |
Project - List Dates that have Alerts | |
| ❌ | GET | /api/project/{project_id}/alerts/latest |
Project - Latest Alerts | |
| ❌ | GET | /api/project/{project_id}/alerts/{date} |
Project - Alerts for Date | |
| ❌ | GET | /api/project/{project_id}/indicators |
Project - List Indicators | |
| ✅ | POST | /api/project/{project_id}/indicators/add |
Project - Add Indicators | add_to_project() |
| ❌ | POST | /api/project/{project_id}/indicators/add_note |
Project - Add Note to Indicators | |
| ❌ | POST | /api/project/{project_id}/indicators/delete |
Project - Delete Indicators | |
| ❌ | POST | /api/project/{project_id}/indicators/tags/add |
Project - Add Tags to Indicators | |
| ❌ | POST | /api/project/{project_id}/indicators/tags/delete |
Project - Delete Tags from Indicators | |
| ❌ | GET | /api/project/{project_id}/scans |
Project - List Scans | |
| ❌ | GET | /api/project/{project_id}/yara/rules |
Project - List Yara Rules | |
| ✅ | GET | /api/project/{project_id}/yara/rules/{rule_id}/matches |
Project - List Matches for YARA rule | yara_matches() |
| Scans | ||||
| ✅ | GET | /api/axon/live/scan/results/{scan_id} |
Live Scan Results | get_scan() |
| ✅ | GET | /api/axon/live/scan/results/{scan_id}/crawl |
Live Scan Results - Crawl | scan(), get_scan_crawl_results() |
| ✅ | POST | /api/axon/live/scan/start |
Live Scan Start | start_scan(), scan() |
| String | ||||
| ✅ | GET | /api/axon/string/dns/extra2 |
String DNS Extra History | extra_history() |
| ✅ | GET | /api/axon/string/dns/history |
String DNS History | dns_history() |
| ❌ | GET | /api/axon/string/pivots2 |
String Pivots | |
| ❌ | GET | /api/axon/string/pivots2/{category} |
String Pivots - Category | |
| ✅ | GET | /api/axon/string/registration/history2 |
String Registration History | registration_history() |
| ❌ | GET | /api/axon/string/registration/history2/{field} |
String Registration History - Category | |
| Threats | ||||
| ❌ | GET | /api/threat/group/{threat_key}/indicators |
Threat Group - List Indicators | |
| ❌ | GET | /api/threat/group/{threat_key}/reports |
Threat Group - List Reports | |
| ❌ | GET | /api/threat/group/{threat_key}/summary |
Threat Group - Summary | |
| ❌ | GET | /api/threat/indicators/recent |
Recent Threat Indicators | |
| ❌ | GET | /api/threat/names |
Threat Names | |
| ❌ | GET | /api/threat/reports/recent |
Recent Threat Reports | |
| Utilities | ||||
| ❌ | GET | /api/axon/advanced/query |
Advanced Query | |
| ❌ | POST | /api/axon/submissions/domains |
Submissions Domains | |
| ❌ | GET | /api/paths |
Paths | |
| ❌ | GET | /api/ping |
Ping | |
| ❌ | GET | /api/profile/token |
Profile Token | |
| ❌ | GET | /api/profile/usage |
Profile Usage | |
| ❌ | GET | /api/profile/usage/daily |
Profile Usage Daily |
MIT