Enrich Indicators
/api/v1/enrichment/lookupEnrich up to 10 indicators of compromise per request against 14 threat intelligence providers. Each indicator costs 3 credits. Returns per-provider verdicts, aggregated scores, and a summary classification for each indicator.
enrichment:read
3 per indicator
All plans
60 seconds
Supported Indicator Types
| Type | Example | Notes |
|---|---|---|
| ip | 185.220.101.42 | IPv4 addresses |
| domain | evil-domain.com | Domain names without protocol |
| url | https://malware.example.com/payload | Full URLs (SSRF-protected) |
| hash | 44d88612fea8a8f36de82e1278abb02f | MD5, SHA-1, or SHA-256 |
| attacker@phishing.com | Email addresses |
Request Body
{
"indicators": [
{ "type": "ip", "value": "185.220.101.42" },
{ "type": "domain", "value": "evil-domain.com" },
{ "type": "hash", "value": "44d88612fea8a8f36de82e1278abb02f" }
]
}| Field | Type | Required | Description |
|---|---|---|---|
| indicators | object[] | Yes | Array of 1–10 indicator objects to enrich |
| indicators[].type | string | Yes | One of "ip", "domain", "url", "hash", "email" |
| indicators[].value | string | Yes | The indicator value. Max 2048 characters. |
Response
{
"data": {
"results": [
{
"indicator": { "type": "ip", "value": "185.220.101.42" },
"verdict": "malicious",
"score": 85,
"providers": {
"virustotal": {
"verdict": "malicious",
"score": 90,
"details": {
"positives": 12,
"total": 94,
"as_owner": "Tor Exit Node"
}
},
"abuseipdb": {
"verdict": "suspicious",
"score": 75,
"details": {
"total_reports": 342,
"confidence_score": 75,
"categories": ["SSH Brute-Force", "Port Scan"]
}
},
"shodan": {
"verdict": "suspicious",
"score": 60,
"details": {
"ports": [22, 80, 443],
"org": "Tor Network",
"os": "Linux"
}
}
},
"enriched_at": "2026-03-24T12:34:56.789Z"
},
{
"indicator": { "type": "domain", "value": "evil-domain.com" },
"verdict": "suspicious",
"score": 45,
"providers": {
"virustotal": {
"verdict": "suspicious",
"score": 45,
"details": {
"positives": 3,
"total": 94
}
},
"urlscan": {
"verdict": "clean",
"score": 10,
"details": {
"malicious": false,
"categories": []
}
}
},
"enriched_at": "2026-03-24T12:34:57.123Z"
},
{
"indicator": { "type": "hash", "value": "44d88612fea8a8f36de82e1278abb02f" },
"verdict": "unknown",
"score": 0,
"providers": {},
"enriched_at": "2026-03-24T12:34:57.456Z"
}
],
"summary": {
"total": 3,
"malicious": 1,
"suspicious": 1,
"clean": 0,
"unknown": 1
}
},
"meta": {
"request_id": "req_abc123def456",
"credits_used": 9,
"credits_remaining": 91,
"processing_time_ms": 3450
}
}| Field | Type | Description |
|---|---|---|
| data.results | array | Array of enrichment results, one per indicator submitted |
| data.results[].indicator | object | The original indicator (type and value) that was enriched |
| data.results[].verdict | string | "malicious", "suspicious", "clean", or "unknown" |
| data.results[].score | number | Aggregated threat score from 0 (clean) to 100 (malicious) |
| data.results[].providers | object | Per-provider results keyed by provider name, each with verdict, score, and details |
| data.results[].enriched_at | string | ISO 8601 timestamp of when the enrichment was performed |
| data.summary | object | Counts of indicators by verdict: total, malicious, suspicious, clean, unknown |
| meta.request_id | string | Unique request identifier for support and debugging |
| meta.credits_used | number | Credits consumed by this request |
| meta.credits_remaining | number | Remaining credit balance after this request |
| meta.processing_time_ms | number | Server-side processing time in milliseconds |
Scoring & Verdicts
Each indicator is enriched against all applicable providers in parallel. The aggregated score is a weighted average of scores from providers that found the indicator. The verdict is derived from the score:
| Score Range | Verdict |
|---|---|
| 70 – 100 | malicious |
| 30 – 69 | suspicious |
| 0 – 29 | clean |
| No data | unknown |
Individual provider failures are handled gracefully. If a provider times out or errors, the remaining providers still contribute to the verdict. The failed provider is omitted from the providers object.
Credit Pricing
IOC Enrichment is billed per indicator submitted in the request. Each indicator is enriched against up to 14 threat intelligence providers in parallel.
| Indicators | Cost | Example |
|---|---|---|
| 1 indicator | 3 credits | Single IP reputation check |
| 3 indicators | 9 credits | IP + domain + hash from an incident |
| 5 indicators | 15 credits | IOCs extracted from a phishing email |
| 10 indicators | 30 credits | Full batch enrichment (max per request) |
Credits are deducted before enrichment begins. If your balance is insufficient for the number of indicators submitted, the request returns 402 Insufficient Credits and no indicators are processed.
Phishing sub-tool pricing differs. When IOC enrichment is called as part of the Phishing Analysis workflow via /api/v1/phishing/enrich, the cost is 2 credits flat (regardless of indicator count) since it serves as a lightweight follow-up to email analysis.
Threat Intelligence Providers
Each indicator is queried against all providers that support its type. Not all providers support all indicator types — see the “Supported Types” column for which types each provider handles.
| Provider | Supported Types | Data Collected |
|---|---|---|
| VirusTotal | ip, domain, url, hash | Multi-engine antivirus scan results, detection ratios, and community reputation |
| AbuseIPDB | ip | Crowd-sourced IP abuse reports, confidence scoring, ISP and usage type classification |
| Shodan | ip, domain | Open ports, service banners, known CVEs, OS fingerprinting, and organization data |
| AlienVault OTX | ip, domain, url, hash | Community threat intelligence pulses, tags, and indicator correlation |
| urlscan.io | ip, domain, url | URL analysis with scan history, malicious verdicts, community tags, and contacted infrastructure |
| URLhaus | ip, domain, url, hash | Malware URL database — active/offline status, payload tracking, and threat type classification |
| ThreatFox | ip, domain, url, hash | IOC sharing platform — malware family association, threat types, and reporter context |
| MalwareBazaar | hash | Malware sample repository — file metadata, signature matches, and delivery method tracking |
| Hybrid Analysis | ip, domain, url, hash | Sandbox analysis results, behavioral indicators, and threat scoring |
| GreyNoise | ip | Internet noise classification — distinguishes mass scanners from targeted attacks, RIOT benign service detection |
| Censys | ip | Internet-wide scan data for host discovery, certificate analysis, and service enumeration |
| Pulsedive | ip, domain, url | Threat intelligence aggregation with risk scoring, linked indicators, and feed correlation |
| OpenPhish | url, domain | Automated phishing URL detection — checks against known active phishing campaigns |
| IPVoid DNSBL | ip | DNS-based blackhole list checks across multiple blocklist providers |
Caching
Enrichment results are cached for 24 hours per indicator and source. Subsequent requests for the same indicator within the cache window return cached provider results without re-querying the upstream provider.
Cached results are still aggregated fresh on each request, so the verdict and score reflect the latest scoring logic. Credits are charged at 3 credits per indicator regardless of cache status.
Code Examples
curl -X POST https://api.dfir-lab.ch/v1/enrichment/lookup \
-H "Authorization: Bearer sk-dfir-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"indicators": [
{ "type": "ip", "value": "185.220.101.42" },
{ "type": "domain", "value": "evil-domain.com" },
{ "type": "hash", "value": "44d88612fea8a8f36de82e1278abb02f" }
]
}'import requests
response = requests.post(
"https://api.dfir-lab.ch/v1/enrichment/lookup",
headers={
"Authorization": "Bearer sk-dfir-your-key-here",
"Content-Type": "application/json",
},
json={
"indicators": [
{"type": "ip", "value": "185.220.101.42"},
{"type": "domain", "value": "evil-domain.com"},
{"type": "hash", "value": "44d88612fea8a8f36de82e1278abb02f"},
]
},
)
data = response.json()
for result in data["data"]["results"]:
ioc = result["indicator"]
verdict = result["verdict"]
score = result["score"]
print(f"{ioc['type']}:{ioc['value']} -> {verdict} (score: {score})")
summary = data["data"]["summary"]
print(f"\nSummary: {summary['malicious']} malicious, "
f"{summary['suspicious']} suspicious, {summary['clean']} clean")const response = await fetch("https://api.dfir-lab.ch/v1/enrichment/lookup", {
method: "POST",
headers: {
Authorization: "Bearer sk-dfir-your-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify({
indicators: [
{ type: "ip", value: "185.220.101.42" },
{ type: "domain", value: "evil-domain.com" },
{ type: "hash", value: "44d88612fea8a8f36de82e1278abb02f" },
],
}),
});
const { data, meta } = await response.json();
for (const result of data.results) {
console.log(
`${result.indicator.type}:${result.indicator.value} -> ` +
`${result.verdict} (score: ${result.score})`
);
}
console.log(`Credits used: ${meta.credits_used}, remaining: ${meta.credits_remaining}`);Important Notes
- Maximum 10 indicators per request. For larger batches, split across multiple requests.
- URL-type indicators have SSRF protection. URLs pointing to private or internal IP ranges (
10.x.x.x,192.168.x.x,172.16-31.x.x,127.0.0.1) are rejected with a400 Bad Request. - Enrichment queries all applicable providers in parallel. Requests typically complete in 2–10 seconds depending on provider response times. The 60-second timeout covers worst-case scenarios.
- Indicator values have a maximum length of 2048 characters.
- The
enrichment:readpermission scope is required. API keys withphishing:readscope also have enrichment access automatically.