Documentation de l'API
Référence complète de l'API de renseignement de menace isMalicious. Vérifiez domaines, IP et URL pour détecter une activité malveillante.
curl -d "email=you@example.com" https://ismalicious.com/api/keys/instant50 requêtes gratuites/mois · clé API instantanée · sans formulaire
Welcome
Introduction to the isMalicious API
Welcome to the isMalicious API documentation. This API provides programmatic access to our comprehensive threat intelligence database, enabling you to check domains, IPs, emails, and URLs for malicious activity.
Base URL: https://api.ismalicious.com (all paths below are relative to this host — not ismalicious.com/api/...).
Documentation: Interactive reference · OpenAPI JSON · Playground
Key Features:
•Real-time threat intelligence checks (IPs, domains, URLs, file hashes)
•Ransomware intelligence, CVE catalog search, and threat-intel utilities (ASN, reverse IP, history)
•Email: lightweight check on this host; full email risk analysis is on the web app (see Email)
•Reputation scoring from configured sources
•Geolocation and WHOIS data
•Vulnerability scanning
•Downloadable blocklists
First call in 30 seconds — no account form. One request mints a free key (50 checks/month, 60/min) and emails you a link to claim the account later:
curl -d "email=you@example.com" https://ismalicious.com/api/keys/instantThe response carries the ready-made header and an example call:
{
"apiKey": "...",
"apiSecret": "...",
"authHeader": "X-API-KEY: <base64(apiKey:apiSecret)>",
"quota": { "requestsPerMonth": 50, "burst": "60/minute" },
"example": "curl -H \"X-API-KEY: ...\" \"https://ismalicious.com/api/check?query=example.com\""
}Limited to 3 keys per day per IP; an address that already has an account is pointed at login instead. The same path backs the MCP server's bootstrap_key tool.
Getting Started (with an account):
1.Create an account at ismalicious.com/auth/register
2.Generate API keys from your dashboard
3.Make your first API call
Need Help?
Authentication
How to authenticate API requests
Routes are split into public (no key) and protected (authentication required).
Hosts for automation
•Canonical API: https://api.ismalicious.com — use this for SDK/API-key clients (/check, /monitoring, /alerts, /taxii, /cve, webhook CRUD, etc.).
•Web API: https://ismalicious.com/api — dashboard session routes and some analyze helpers (/analyze, email/phone/crypto). Prefer api.ismalicious.com for server-to-server integrations; pointing automation at the web host can hit session-only mirrors.
Protected routes (typical integration): Send one of
•X-API-KEY: <base64(apiKey:apiSecret)>,
•Authorization: Basic with username = apiKey and password = apiSecret (appliances that cap a single field at 64 characters; curl -u apiKey:apiSecret),
•Authorization: Basic with any username and password = the Base64 credential (legacy TAXII / Sentinel / MISP),
•or a valid dashboard session cookie (browser / same-site requests).
If none is present, the API returns 401 with "message": "Provide an X-API-KEY header, a Bearer token, HTTP Basic Auth (apiKey:apiSecret), or a valid session cookie".
Public routes (no `X-API-KEY`): include GET /health, GET /openapi.json, GET /stats, GET /blocklist/stats, GET /blocklist/download/{filename}, GET /analytics, GET /cve/stats/epss, GET /cve/stats/timeseries, POST /contact, POST /feedback, POST /newsletter, POST /license, GET /email-sequences/unsubscribe (and related), plus POST /auth/resend-verification. Cron, webhooks, and internal routes use separate tokens — not customer API keys.
Getting your API keys
1.Log in and open Account Settings
2.Create an API key pair (apiKey + apiSecret)
Or skip the form: curl -d "email=you@example.com" https://ismalicious.com/api/keys/instant returns a free key pair and the ready X-API-KEY header in one request (3 per day per IP; the address gets a claim link to set a password and rotate the pair). See Welcome.
Header
X-API-KEY: <base64_encoded_credentials>Base64-encode apiKey:apiSecret (single colon, no spaces).
const credentials = btoa(`${apiKey}:${apiSecret}`);import base64
credentials = base64.b64encode(f"{api_key}:{api_secret}".encode()).decode()Special: licensed source list — POST /sources also requires header X-License-Key: <your_license_key> (active row in license_keys). You still pass X-API-KEY (or session) to satisfy the gateway.
Security: Never ship secrets to browsers; use env vars; rotate keys; optional IP allowlists on custom plans.
Public endpoints
No API key required — health, OpenAPI, aggregate stats, analytics slices, and CVE statistics.
/healthHealth check
Liveness probe for load balancers and monitoring. Returns a small JSON payload when the API process is up.
Example Request
curl -sS "https://api.ismalicious.com/health"Responses
{
"status": "operational",
"timestamp": "2026-03-29T12:00:00.000Z",
"services": {
"api": "operational",
"database": "operational",
"redis": "operational"
}
}/openapi.jsonOpenAPI 3 specification
Machine-readable API description (may lag slightly behind this page; routes are defined in apps/rust-api).
Example Request
curl -sS "https://api.ismalicious.com/openapi.json" | head -c 400Responses
{ ... }/statsPublic threat statistics
High-level counts and trends from cached metrics (threat overview, categories, recent activity).
Example Request
curl -sS "https://api.ismalicious.com/stats"Responses
{
"totalThreats": 0,
"byType": {
"domains": 0,
"ips": 0
},
"recentActivity": {
"last24h": 0,
"last7d": 0,
"growthRate": 0
}
}/analyticsAnalytics metric slices
Reads sub-documents from the metrics:threats Redis JSON. Optional metric query selects one slice (same names as the Next.js /api/analytics route).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
metric | string | No | e.g. threatOverview, categoryBreakdown, topSources, threatDistribution, threatTimeline, geoThreatMap, vulnerabilityPatterns |
Example Request
curl -sS "https://api.ismalicious.com/analytics?metric=threatOverview"Responses
{"threatOverview": { ... }}/cve/stats/epssCVE EPSS distribution & summary
EPSS bucket distribution, top CVEs, and summary counts from CveCatalog. Query params filter results.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Top CVEs limit (default 20, max 100) (default: 20) |
minEpss | number | No | Minimum EPSS score 0–1 (default: 0) |
kev | string | No | Set to `true` to restrict to KEV entries |
exploit | string | No | Filter by SSVC exploitation value (e.g. active) |
Example Request
curl -sS "https://api.ismalicious.com/cve/stats/epss?limit=10&minEpss=0.5"Responses
{"distribution": [], "top": [], "summary": {}}/cve/stats/timeseriesCVE activity time series
Time-bucketed counts by severity, KEV, and active exploit flags from CveCatalog.lastModifiedAt.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
days | integer | No | Lookback window (default 30; max depends on bucket) (default: 30) |
bucket | string | No | `day` (default), `week`, or `month` (default: day) |
Example Request
curl -sS "https://api.ismalicious.com/cve/stats/timeseries?days=14&bucket=day"Responses
[{"day": "2026-01-01", "critical": 0, "high": 0, "medium": 0, "low": 0, "kev": 0, "active_exploit": 0}]Check Endpoints
Threat intelligence check endpoints
/checkFull Threat Analysis
Comprehensive threat intelligence check. Supply one target via query or legacy aliases ip, domain, or hash (same semantics as query). Supports IPv4/IPv6, domain, URL, or file hash (MD5/SHA1/SHA256 hex). Response size depends on enrichment: basic, standard (default), or full. If no target is provided, the API still returns 200 with a minimal body (malicious: false, apiVersion, enrichmentLevel) — prefer validating client-side. For smaller, stable JSON use sub-endpoints (/check/reputation, etc.). Verdict semantics: malicious reflects high-confidence scanner-style signals (Google Safe Browsing, ransomware IOC matches), while blocklistHits / blocklistListed count the feed listings that are threat opinions — every entry of sources[] carries the registry threatClass (threat, the default; infrastructure; policy; allowlist) and fpRisk (low, medium, high), and only threat-class listings that are not ad/tracker-only lists are counted or weighed. Infrastructure: when at least one listing describes what the indicator *is* rather than what it did (Tor exit, VPN egress, cloud/CDN range, DoH resolver, crawler, sinkhole, URL shortener…), the response adds an infrastructure block: attributes (distinct, sorted, e.g. tor-exit, cloud, doh-resolver) and the sources behind them. Those listings never raise malicious, the risk score or the blocklist counters, and the key is absent when no such listing exists. File hashes: a hash no source has seen answers lookupStatus: "unknown" (known otherwise) with riskScore.level inconclusive — absence of evidence, never a clean verdict, so do not release a file on it. The miss is cached for five minutes: ask again after that rather than caching unknown on your side. A 40-hex (SHA-1) or 32-hex (NTLM, same shape as MD5) value that is the hash of a breached password adds pwnedPassword (hashType, count, corpusUpdatedAt, from Have I Been Pwned's Pwned Passwords); it never changes malicious, the risk score or lookupStatus, and is absent otherwise. Data Trust: responses include dataTrust for freshness, source reliability, completeness, and provider agreement, plus evidence for SOC-ready reasons, contradictory signals, and recommendedAction. OTX: With standard or full enrichment, the response may include otx — normalized AlienVault OTX data: community pulses (and optional reputationScore from indicator general). For IPs, full adds LevelBlue Labs labsReputation and a capped passive DNS summary. For domains, full adds passive DNS. For URLs (primary query is http(s)://...), OTX uses the URL indicator. For file hashes, standard/full may include pulse context from file/.../general; full may add a capped fileAnalysis excerpt. Uses the public OTX API; optional OTX_API_KEY improves rate limits. Omitted on basic or when OTX fails. Reports history: checks are not saved automatically — to keep a report, call POST /reports/save (platform API, session or API key) with the check result.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | No | Primary target: IP, domain, URL, or hash |
ip | string | No | Alias of `query` when the value is an IP |
domain | string | No | Alias of `query` for a hostname / domain |
hash | string | No | Alias of `query` for MD5/SHA1/SHA256 hex |
enrichment | string | No | Level of data enrichment: basic, standard, or full (default: standard) |
Example Request
curl -X GET "https://api.ismalicious.com/check?query=8.8.8.8&enrichment=standard" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"malicious": false,
"blocklistHits": 0,
"blocklistListed": false,
"sources": [
{
"id": "gcp-cloud-ip-ranges",
"name": "GCP - Published IP Ranges",
"type": "ip",
"category": "infrastructure",
"categories": [
"infrastructure",
"cloud"
],
"threatClass": "infrastructure",
"matchType": "cidr"
}
],
"infrastructure": {
"attributes": [
"cloud"
],
"sources": [
{
"id": "gcp-cloud-ip-ranges",
"name": "GCP - Published IP Ranges",
"category": "infrastructure",
"threatClass": "infrastructure"
}
]
},
"reputation": {
"malicious": 0,
"suspicious": 0,
"harmless": 85,
"undetected": 15
},
"riskScore": {
"score": 5,
"level": "safe",
"factors": []
},
"confidence": {
"score": 95,
"level": "high"
},
"classification": {
"primary": "safe",
"secondary": []
},
"geo": {
"country": "United States",
"countryCode": "US",
"city": "Mountain View",
"isp": "Google LLC"
},
"otx": {
"source": "alienvault_otx",
"fetchedAt": "2026-03-30T12:00:00.000Z",
"pulseCount": 2,
"moreCount": 0,
"reputationScore": 0,
"labsReputation": {
"source": "alienvault_otx_labs",
"fetchedAt": "2026-03-30T12:00:00.000Z",
"score": 2,
"countryName": "United States",
"malwareSampleCount": 0,
"urlCount": 1
},
"passiveDns": {
"source": "alienvault_otx_passive_dns",
"fetchedAt": "2026-03-30T12:00:00.000Z",
"totalRecords": 1,
"moreCount": 0,
"records": [
{
"hostname": "example.com",
"address": "203.0.113.10",
"recordType": "A",
"firstSeen": "2025-01-01",
"lastSeen": "2026-03-01"
}
]
},
"pulses": [
{
"id": "58f15111d3bb0b0b8ac54662",
"name": "Example pulse",
"description": "Community IOC context (truncated in API).",
"tags": [
"malware",
"ssh"
],
"tlp": "green",
"modified": "2026-03-25T19:07:52.831000",
"references": [
"https://example.com/ref"
],
"authorUsername": "researcher",
"subscriberCount": 120,
"pulseUrl": "https://otx.alienvault.com/pulse/58f15111d3bb0b0b8ac54662"
}
]
},
"apiVersion": "v2",
"enrichmentLevel": "full",
"dataTrust": {
"observedAt": "2026-05-09T10:00:00.000Z",
"freshness": "fresh",
"sourceAgreement": {
"count": 1,
"weightedStrength": 0.95,
"level": "weak",
"scannerDetections": 0,
"scannerSuspicious": 0,
"scannerClean": 85
},
"sources": [
{
"name": "Spamhaus",
"type": "blocklist",
"reliability": 0.95,
"reliabilityLevel": "high",
"noiseProfile": "authoritative"
}
],
"completeness": {
"score": 75,
"present": [
"sources",
"reputation",
"otx"
],
"missing": [
"vulnerabilities"
]
},
"providerAgreement": {
"status": "partial",
"summary": "Cross-provider signals available",
"contradictorySignals": []
}
},
"evidence": {
"verdict": "safe",
"score": 5,
"observedAt": "2026-05-09T10:00:00.000Z",
"reasons": [
"No high-confidence malicious evidence"
],
"contradictorySignals": [],
"sourceSummary": {
"count": 1,
"weightedStrength": 0.95,
"highReliabilityCount": 1,
"noiseWarnings": []
},
"freshness": "fresh",
"recommendedAction": "allow",
"analystStatus": "new"
}
}/check/reputationCheck Reputation
Get reputation data aggregated from the threat-intelligence feed corpus.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | IP address or domain |
Example Request
curl -X GET "https://api.ismalicious.com/check/reputation?query=example.com" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"reputation": {
"malicious": 0,
"suspicious": 2,
"harmless": 80,
"undetected": 18
}
}/check/locationCheck Geolocation
Get geographic location data including country, city, region, ISP, and coordinates.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | IP address or domain |
Example Request
curl -X GET "https://api.ismalicious.com/check/location?query=1.1.1.1" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"geo": {
"country": "Australia",
"countryCode": "AU",
"city": "Sydney",
"region": "New South Wales",
"lat": -33.8688,
"lon": 151.2093,
"isp": "Cloudflare Inc"
}
}/check/whoisCheck WHOIS
Get WHOIS registration data including registrant, registrar, and dates.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | IP address or domain |
Example Request
curl -X GET "https://api.ismalicious.com/check/whois?query=example.com" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"whois": {
"registrar": "GoDaddy.com, LLC",
"registrant": "REDACTED FOR PRIVACY",
"createdDate": "1997-09-15T04:00:00.000Z",
"updatedDate": "2023-08-14T07:00:00.000Z",
"expiresDate": "2028-09-14T04:00:00.000Z",
"nameServers": [
"ns1.example.com",
"ns2.example.com"
]
}
}/check/certificatesCheck Certificates
Get SSL/TLS certificate information including issuer, validity, and chain.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Domain or IP address |
Example Request
curl -X GET "https://api.ismalicious.com/check/certificates?query=google.com" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"certificates": {
"issuer": "Let's Encrypt Authority X3",
"subject": "example.com",
"validFrom": "2024-01-01T00:00:00.000Z",
"validTo": "2024-03-31T23:59:59.000Z",
"serialNumber": "03:A1:B2:C3:D4:E5:F6",
"fingerprint": "SHA256:ABC123..."
}
}/check/vulnerabilitiesCheck Vulnerabilities
Get known vulnerabilities associated with an IP address from CVE databases.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | IP address |
Example Request
curl -X GET "https://api.ismalicious.com/check/vulnerabilities?query=192.168.1.1" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"vulnerabilities": {
"total": 3,
"critical": 1,
"high": 1,
"medium": 1,
"low": 0,
"cves": [
{
"id": "CVE-2024-1234",
"severity": "CRITICAL",
"score": 9.8,
"description": "Remote code execution vulnerability"
}
]
}
}/check/intelowlIntelOwl observable analysis
Proxies an observable to IntelOwl (/api/analyze_observable) when INTELOWL_URL and INTELOWL_API_KEY are configured. Results are cached in Redis (~1h). Use HEAD to probe without body. Set cache_only=true to return only cached data (404 if missing).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | IP, domain, URL, or hash to analyze |
type | string | No | Observable classification: `ip`, `domain`, `url`, or `hash` — inferred from `query` when omitted |
cache_only | string | No | If `true`, skip live IntelOwl and only return cache (default: false) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/check/intelowl?query=8.8.8.8&type=ip"Responses
{"status": "accepted", ...}/check/intelowlIntelOwl route probe (HEAD)
Same route as GET; use HEAD for connectivity checks without a response body.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Observable to check |
Example Request
curl -sSI -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/check/intelowl?query=1.1.1.1"Responses
/check/ransomwareRansomware intelligence (summary or entity)
Without query, or with type=stats, returns aggregated ransomware stats from cache (ransomware:stats) or computed from recent victims. With query, returns a lightweight stub for entity-oriented checks (full deep-dive uses dashboard / GET /ransomware/* routes). Optional include_ttps, include_iocs, limit are parsed for forward compatibility.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | No | Entity or keyword; omit (or use type=stats) for global stats |
type | string | No | Set to `stats` to force statistics response |
include_ttps | string | No | Reserved / forward-compatible |
include_iocs | string | No | Reserved / forward-compatible |
limit | string | No | Reserved / forward-compatible |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/check/ransomware?type=stats"Responses
{
"query": "stats",
"queryType": "stats",
"stats": {
"totalVictims": 0,
"activeGroups": 0,
"attacksThisMonth": 0,
"attacksThisYear": 0
}
}/check/tenantMicrosoft 365 tenant (domain)
Returns azureTenant data from Redis for a domain (not supported for raw IPs).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Domain or URL host to look up |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/check/tenant?query=example.com"Responses
{
"tenant": null,
"message": "Domain is not associated with a Microsoft 365 tenant"
}/check/emailEmail check (Rust API — corpus + DNS)
On api.ismalicious.com this route scores an email from the signals the threat-intel backend owns directly: blocklist listings of the address itself (email feeds), the domain's threat level and disposable status from the corpus, and live DNS posture (MX, SPF, DMARC, DKIM). The score is a weighted computation with per-factor breakdown — same score bands as the full analysis.
For extended email risk analysis (breach catalog, EmailRep reputation, typosquatting patterns), call the Next.js route on the main site with the same auth pattern your app uses for dashboard API calls:
GET https://ismalicious.com/api/check/email?email=user@example.com
(Implementation: apps/web/app/api/check/email/route.ts.)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to analyze |
enrichment | string | No | Echoed in response as enrichmentLevel (default: standard) |
Example Request
curl -X GET "https://api.ismalicious.com/check/email?email=user@example.com&enrichment=standard" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"email": "user@example.com",
"domain": "example.com",
"localPart": "user",
"riskScore": {
"score": 1,
"level": "safe",
"summary": "No corpus or DNS risk signals found",
"factors": [
{
"name": "Blocklist Listings",
"weight": 0.35,
"score": 0,
"contribution": 0,
"description": "Not listed in any email blocklist"
}
]
},
"listings": {
"listed": false,
"count": 0,
"sources": []
},
"domainReputation": {
"threatLevel": null,
"sources": 0,
"disposable": false
},
"validation": {
"syntaxValid": true,
"mxValid": true
},
"mailSecurity": {
"hasSPF": true,
"hasDMARC": true,
"hasDKIM": true,
"grade": "B"
},
"apiVersion": "v1",
"enrichmentLevel": "standard"
}/analyzeUnified analyze (auto-detect type)
On ismalicious.com this route auto-detects crypto, email, phone, hash, URL, IP, or domain and forwards to the matching check handler.
POST https://ismalicious.com/api/analyze
Host split: not available on api.ismalicious.com (Rust). SDK clients must set webBaseUrl: 'https://ismalicious.com/api' (default in SDK) for analyze / checkEmail / checkPhone / checkCrypto; keep baseUrl for /check and other Rust routes.
Body: { "input": "..." } (optional input_type / query aliases). Auth uses the same API key or session pattern as other web API routes.
(Implementation: apps/web/app/api/analyze/route.ts.)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | string | Yes | Raw indicator (URL, domain, IP, email, phone, hash, or crypto address) |
input_type | string | No | Optional force type: crypto | email | phone | hash | url | ip | domain |
Example Request
curl -X POST "https://ismalicious.com/api/analyze" \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-d '{"input":"user@example.com"}'Responses
{
"detectedType": "email",
"email": "user@example.com",
"riskScore": {
"score": 42,
"level": "medium"
}
}/check/phonePhone scam / fraud check
Phone risk analysis on the web app (heuristics + optional IPQS when configured).
GET https://ismalicious.com/api/check/phone?phone=%2B14155552671
(Implementation: apps/web/app/api/check/phone/route.ts.)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | E.164 or common phone number formats |
Example Request
curl -X GET "https://ismalicious.com/api/check/phone?phone=%2B14155552671" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"phone": "+14155552671",
"riskScore": {
"score": 35,
"level": "low"
},
"factors": []
}/check/cryptoCrypto wallet scam check
Checks BTC/ETH wallet addresses against ingested ScamSniffer and related scam indexes.
GET https://ismalicious.com/api/check/crypto?address=0x...
(Implementation: apps/web/app/api/check/crypto/route.ts.)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
address | string | Yes | Bitcoin or Ethereum wallet address |
Example Request
curl -X GET "https://ismalicious.com/api/check/crypto?address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"address": "0x0000000000000000000000000000000000000000",
"chain": "eth",
"riskScore": {
"score": 95,
"level": "critical"
},
"localBlacklistHit": true
}/check/passwordBreached password check (hash in, verdict out)
Whether the password behind a SHA-1 or NTLM hash appears in Have I Been Pwned's Pwned Passwords corpus (over 2 billion hashes per form, held on our servers and refreshed monthly), how many times, and a prevalence level: rare (1–9 sightings), common (10–999), very_common (1,000+).
Send exactly one of sha1, ntlm or hash (40 hex reads as SHA-1, 32 as NTLM). Never send the password itself: a password field is refused with 400. Nothing is cached, stored or saved as a report. For k-anonymity, where only 5 hex digits leave your side, use GET /pwned-passwords/range/{prefix} below — the JavaScript SDK's checkPassword() does that for you.
not_found means the password is not in known breach dumps; it says nothing about its strength. One request of the monthly quota; 503 until the corpus is loaded.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
sha1 | string | No | SHA-1 of the password, 40 hex characters (body) |
ntlm | string | No | NTLM hash of the password (MD4 of its UTF-16LE form), 32 hex characters (body) |
hash | string | No | Either digest; the length picks the type (body) |
Example Request
# SHA-1("password") = 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
curl -X POST "https://ismalicious.com/api/check/password" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{"sha1": "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8"}'Responses
{
"indicatorType": "password",
"hashType": "sha1",
"exposed": true,
"verdict": "compromised",
"count": 52372427,
"prevalence": "very_common",
"recommendation": "Do not use this password: it circulates in breach dumps that attackers replay against every login.",
"source": "Have I Been Pwned — Pwned Passwords",
"sourceUrl": "https://haveibeenpwned.com/Passwords",
"corpusUpdatedAt": "2026-09-30T16:12:04+00:00",
"apiVersion": "v2"
}/pwned-passwords/range/{prefix}Breached password lookup (k-anonymity)
Every hash of Have I Been Pwned's Pwned Passwords corpus under a 5-hex prefix, with its breach count. Hash the password yourself (SHA-1, or NTLM with mode=ntlm), send the first 5 hex digits, and compare digits 6–17 of your hash with entries[].suffix: neither the password nor its full hash leaves your side. Answered from a local copy of the corpus, refreshed monthly.
Anonymous callers get 10 requests per hour and 50 per month per IP (the Free key's quota); an API key pays one request of its monthly quota. 503 until the corpus is loaded.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
prefix | string | Yes | First 5 hex digits of the SHA-1 or NTLM hash (path) |
mode | string | No | `sha1` (default) or `ntlm` |
Example Request
# SHA-1("password") = 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
curl "https://ismalicious.com/api/pwned-passwords/range/5BAA6" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"prefix": "5BAA6",
"mode": "sha1",
"suffixLength": 12,
"entries": [
{
"suffix": "1E4C9B93F3F0",
"count": 52256179
}
],
"source": "Have I Been Pwned — Pwned Passwords",
"corpusUpdatedAt": "2026-10-01T03:12:44+00:00"
}Bulk check
Batch-check multiple domains, IPs, URLs and file hashes (MD5/SHA-1/SHA-256) in one request. Plan limits apply (see **GET** `/bulk/check`).
/bulk/checkBulk limits
Returns plan label, maxEntitiesPerRequest for your subscription, and allPlans limits map. The JSON may include endpoint as /api/bulk/check (legacy string); the real URL is https://api.ismalicious.com/bulk/check. POST the same path with a JSON body to run a batch.
Example Request
curl -X GET "https://api.ismalicious.com/bulk/check" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"endpoint": "/api/bulk/check",
"method": "POST",
"description": "Check multiple entities (domains, IPs, URLs) in a single request",
"authentication": "Required - API key or session",
"limits": {
"currentPlan": "BASIC_MONTHLY",
"maxEntitiesPerRequest": 50,
"allPlans": {
"FREE": 10,
"BASIC_MONTHLY": 50,
"BASIC_YEARLY": 50,
"PRO_MONTHLY": 100,
"PRO_YEARLY": 100,
"ENTERPRISE_MONTHLY": 500,
"ENTERPRISE_YEARLY": 500
}
}
}/bulk/checkRun bulk check
Request body: entities (required array of strings), optional enrichment, optional format (json default, or csv for a CSV body). Per-request entity caps by plan: FREE 10, BASIC 50, PRO 100, ENTERPRISE 500. Each result includes the same SOC evidence summary fields used by single checks: evidence, recommendedAction, analystStatus, and observedAt. confidence is 0–100. Hash rows add lookupStatus (known | unknown): an unknown hash omits riskScore, riskLevel and confidence — there is no evidence either way, which is not a clean verdict.
Request Body
{
"entities": [
"example.com",
"8.8.8.8",
"https://example.org/path"
],
"enrichment": "full"
}Example Request
curl -X POST "https://api.ismalicious.com/bulk/check" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{"entities":["example.com","8.8.8.8"],"enrichment":"full"}'Responses
{
"success": true,
"total": 2,
"processed": 2,
"results": [
{
"entity": "example.com",
"type": "domain",
"isMalicious": false,
"confidence": 12,
"sources": 3,
"categories": [],
"riskScore": 5,
"riskLevel": "safe",
"recommendedAction": "allow",
"analystStatus": "new",
"observedAt": "2026-05-09T10:00:00.000Z",
"evidence": {
"verdict": "safe",
"observedAt": "2026-05-09T10:00:00.000Z",
"reasons": [
"No high-confidence malicious evidence"
],
"contradictorySignals": [],
"sourceSummary": {
"count": 3,
"weightedStrength": 1.72,
"highReliabilityCount": 2,
"noiseWarnings": []
},
"freshness": "fresh",
"recommendedAction": "allow",
"analystStatus": "new"
}
},
{
"entity": "8.8.8.8",
"type": "ip",
"isMalicious": false,
"confidence": 0.05,
"sources": 1,
"categories": []
}
],
"processingTimeMs": 210
}Mail scan
Read one inbound email message against the dataset for phishing and malware: the sender, the server that delivered it, every link host and attachment hash, and the tells of a phishing message. One scan of the scan meter per message.
A second opinion on one message for a mail system or an AI agent that already has its own detection — not a gateway. It never runs an attachment, never fetches a link and never calls a third party while you wait. It answers from what the dataset knows about every host, address, number and file hash the message names, plus the structural tells a phishing message carries, and it says how much of that it could check (coverage).
Cost. One scan of the scan meter per message, whatever its size and however many links and attachments it carries — never a request of your monthly quota. GET /gate/quota reads the same meter (Free 1,000, Basic 25,000, Pro 250,000, Enterprise 1,000,000 scans per month). Over the allowance the API answers 429 with { "error": "Scan quota exceeded", "usage": …, "limit": … } — usage is a number here, unlike the request-quota 429 — plus message, retryAfter, resetsAt (the 1st of next month, 00:00 UTC), docs, upgradeRequired, billingIssue on a suspended subscription, and a cta with your next step: Basic and Pro for a Free account (cta.options, each with its scansPerMonth, and promoCode), Pro from your account for Basic, an Enterprise quote for Pro and Enterprise. Headers: Retry-After, X-Scan-Usage, X-Scan-Limit.
Input. Exactly one of emlBase64 (the raw message, base64, up to 10 MiB decoded), eml (the raw message as UTF-8 text) or message (a message you already parsed, as Microsoft Graph or the Gmail API hand it back: headers as { name, value } in message order, from, replyTo, returnPath, subject, text, html, and attachments with filename, contentType, size, any of sha256 / sha1 / md5, and inline for a part shown inside the body — attachment contents are never needed, and not read: send the raw message to have them read; only a picture's type and name make an inline part one the scan skips). context says what you know about your own receiving system: authservId, trustAuthenticationResults, trustedHops (1–10, default 1) and connectingIp.
Reading the answer.
| Field | Values |
|---|---|
verdict | malicious · suspicious · clean · inconclusive |
recommendedAction | quarantine · review · warn · deliver |
•malicious needs a listing in the dataset at weight 0.90 or more (a link to a listed host, an attachment on the hash tier, a listed sender). The shape of a message — a link whose text names another site, invoice.pdf.exe, a display name that shows another address — can ask for a review, never for a quarantine: a bank's newsletter is full of tracking links.
•clean is a positive claim. It needs your own receiving system's DMARC pass: pass authservId (the id it writes in Authentication-Results) or trustAuthenticationResults: true, because anyone can write that header into a message and nothing else is believed. The sender's domain must also be one the dataset knows as established (among the 100 000 most visited, and not a free mailbox): attackers publish SPF, DKIM and DMARC for the domains they register, so a pass shows who sent the message, not that they can be trusted. Every attachment must also be known software or a picture by its bytes, and the whole message must have been read (no limit hit, no host left unlooked-up). Otherwise a message with nothing against it is inconclusive — absence of evidence is not evidence of innocence.
•deliver means no objection from this scan. Never use it to release a message another engine quarantined.
•reasons lists the strongest findings first, each with a stable code, a severity, a sentence and its evidence (hosts are defanged, evil[.]example). coverage.skipped lists what was not checked and why. injection reports prompt-injection heuristics over the body, hidden text included — the text an AI agent reading the mail is handed.
What it reads in the message itself. The sender and Reply-To domains, every link host and the display name are compared with a table of well-known brands (paypa1.com, paypal.com.account-check.xyz, "PayPal Support" <help@gmail.com>); a hit names the brand and where the real site is, and is dropped when the dataset ranks the domain among the most visited. The sender's published DMARC policy and SPF all qualifier are read from cached DNS, with the organizational domain's DMARC record applying to a subdomain (sender.posture.dmarcPolicy, spfAll, spoofable). Domains that were registered in the last 45 days and looked like an impersonation when they appeared are flagged. A message attached to the one you send (message/rfc822, a reported phish) is read as a message of its own, two levels deep: its links, addresses and attachments are read with the rest and tagged origin: "attached_message".
Attachments. Sent raw (emlBase64 or eml), attachments are also read for structure, never run: the file type from its bytes (attachments[].detectedType), a program under a document name, macro projects and remote templates in Office files, risky entries and passwords in archives, PDF actions, HTML that rebuilds a file in the browser. Addresses found inside a file (a PDF's links, an Office template's address, the URL a script downloads from) are looked up like the links of the body and tagged origin: "attachment". A message sent as message carries digests, not bytes: its attachments are hashed and named, and coverage.skipped says so.
Not checked yet. SPF, DKIM and DMARC are read from the header you vouch for, not verified by the scan; the contents of encrypted or nested archives, of compressed PDF streams and of file types it does not read are not looked into; QR codes and the destination of shortened links are not read. Each of these shows in coverage.
Edge limit. A raw message may be up to 10 MiB, but the web host in front of https://ismalicious.com/api may refuse a smaller body: send the structured message form (headers, bodies, attachment digests) from a Graph or Gmail integration, or call https://api.ismalicious.com directly.
/mail/scanScan one email message
Scans one inbound message. Charges one scan of the scan meter (not a request). 400 when none or several of emlBase64, eml and message are sent, or the input is not an email; 413 over 10 MiB; 429 over the scan allowance (refused before the message is read, and not charged); 503 with Retry-After when the scan is busy. Also served at https://ismalicious.com/api/mail/scan.
Request Body
{
"message": {
"headers": [
{
"name": "Received",
"value": "from mail.evil.example (mail.evil.example [45.83.64.1]) by mx.corp.example with ESMTPS id abc; Thu, 1 Oct 2026 09:00:00 +0000"
},
{
"name": "Authentication-Results",
"value": "mx.corp.example; spf=fail smtp.mailfrom=bounce@evil.example; dmarc=fail header.from=paypal-secure.test"
},
{
"name": "From",
"value": "PayPal Support <service@paypal-secure.test>"
},
{
"name": "Reply-To",
"value": "helpdesk@gmail.com"
}
],
"subject": "Urgent: verify your account",
"text": "Verify now: https://evil.example/login",
"html": "<p>Verify <a href=\"https://evil.example/login\">www.paypal.com</a></p>",
"attachments": [
{
"filename": "invoice.pdf.exe",
"contentType": "application/pdf",
"size": 3,
"sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
}
]
},
"context": {
"authservId": "mx.corp.example",
"trustedHops": 1
}
}Example Request
curl -X POST "https://api.ismalicious.com/mail/scan" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{
"emlBase64": "'"$(base64 -w0 message.eml)"'",
"context": { "authservId": "mx.corp.example" }
}'Responses
{
"verdict": "malicious",
"recommendedAction": "quarantine",
"riskScore": 100,
"headline": "Quarantine: An attachment matches a known malicious file hash.",
"reasons": [
{
"code": "attachment.known_malware",
"severity": "high",
"summary": "An attachment matches a known malicious file hash.",
"evidence": "invoice.pdf.exe (AgentTesla)"
},
{
"code": "link.listed",
"severity": "high",
"summary": "A link points to a host listed as malicious.",
"evidence": "evil[.]example"
},
{
"code": "ip.listed",
"severity": "high",
"summary": "The server that delivered the message is listed as malicious.",
"evidence": "45[.]83[.]64[.]1"
},
{
"code": "attachment.double_extension",
"severity": "high",
"summary": "An attachment's name hides a program behind a document extension.",
"evidence": "invoice.pdf.exe"
}
],
"sender": {
"address": "service@paypal-secure.test",
"domain": "paypal-secure.test",
"displayName": "PayPal Support",
"verdict": "unknown",
"freeProvider": false,
"disposable": false,
"mx": true,
"returnPath": "bounce@evil.example",
"replyTo": [
"helpdesk@gmail.com"
],
"posture": {
"grade": "F",
"hasSpf": false,
"hasDmarc": false,
"hasMx": true
}
},
"authentication": {
"status": "trusted",
"spf": "fail",
"dmarc": "fail",
"headerFrom": "paypal-secure.test"
},
"connectingIp": {
"address": "45.83.64.1",
"verdict": "malicious",
"sources": 1,
"listed": [
"Some Honeypot - Attackers"
]
},
"links": [
{
"url": "https://evil.example/login",
"host": "evil.example",
"verdict": "malicious",
"sources": 2,
"flags": [
"anchor_mismatch"
],
"anchorText": "www.paypal.com",
"shownDomain": "paypal.com"
}
],
"attachments": [
{
"filename": "invoice.pdf.exe",
"contentType": "application/pdf",
"size": 3,
"inline": false,
"digests": {
"sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
"sha1": "a9993e364706816aba3e25717850c26c9cd0d89d",
"md5": "900150983cd24fb0d6963f7d28e17f72"
},
"verdict": "malicious",
"knownGood": false,
"family": "AgentTesla",
"flags": [
"risky_extension",
"double_extension",
"type_mismatch"
]
}
],
"contacts": [],
"injection": {
"score": 0,
"families": [],
"hiddenScore": 0
},
"coverage": {
"ran": [
"sender",
"reply_to",
"authentication",
"sender_posture",
"connecting_ip",
"links",
"attachment_hashes",
"brands",
"newly_registered_domains",
"injection",
"contacts"
],
"skipped": [
{
"check": "attachment_content",
"reason": "1 attachment(s) came as metadata and digests: send the raw message (eml or emlBase64) to have their structure read"
}
],
"truncated": false
},
"latencyMs": 2
}CVE catalog
Authenticated Redis-backed CVE search and recent list (`/cve` counts against monthly quota like `/check`).
/cveLook up, search or list CVEs
Three modes on one path. With id, the canonical single-CVE lookup: one record with CVSS, EPSS, CISA KEV status and due date, exploitation evidence and references[] (GET /cve/{id} is an alias; /check/cve, /vulnerability/{id} and /vulnerabilities/{id} do not exist). With query, a case-insensitive search over id, title and description. Without either, the most recent CVEs (trimmed to limit). Search and list return { "count", "cves": [...] }.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | No | Exact CVE id (CVE-YYYY-NNNNN). Returns the single record; 404 when not in the catalog. |
query | string | No | Search string (alias `q`); omit for recent CVEs |
severity | string | No | CRITICAL, HIGH, MEDIUM or LOW — filters search and list |
limit | integer | No | Max items (default 20, max 100) (default: 20) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/cve?id=CVE-2021-44228"
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/cve?query=log4j&limit=10"Responses
{"id":"CVE-2021-44228","severity":"CRITICAL","cvssScore":10,"epssScore":0.97,"isKev":true,"kev":{"listed":true,"dateAdded":"2021-12-10T00:00:00+00:00"},"references":[{"source":"nvd","url":"https://nvd.nist.gov/vuln/detail/CVE-2021-44228"}]}/cve/recentRecent CVEs
Returns the cve:recent Redis document as a JSON array (or object), truncated to limit.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Max items (default 20, max 100) (default: 20) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/cve/recent?limit=5"Responses
[]Threat intel utilities
ASN/IP intelligence helpers and AlienVault OTX pulse lookups (authenticated).
/threat-intel/asnASN lookup
Pass ip for full ASN/org data via ipwho.is, or asn (e.g. AS15169 or 15169) for a minimal ASN record.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
ip | string | No | IPv4/IPv6 address |
asn | string | No | ASN number or `AS####` string |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/threat-intel/asn?ip=8.8.8.8"Responses
{"ip": "8.8.8.8", "asn": "AS15169", ...}/threat-intel/reverse-ipReverse IP
Domains hosted on an IPv4 address via HackerTarget (one domain per line).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
ip | string | Yes | IPv4 address |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/threat-intel/reverse-ip?ip=8.8.8.8"Responses
{"ip": "8.8.8.8", "domains": ["..."], "total_domains": 0}/threat-intel/historyReputation history
Historical reputation timeline for a domain or IP (entity), optional days window.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
entity | string | Yes | Domain or IP |
days | integer | No | Lookback days (implementation-specific default) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/threat-intel/history?entity=example.com&days=30"Responses
{}Ransomware intelligence
Ransomware.live-backed feeds and analytics (authenticated). Data is cached in Redis when possible.
/ransomware/feedRecent victims feed
limit (default 30, max 100). Optional press=true adjusts filtering (see server).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Victims to return (default: 30) |
press | string | No | Optional flag consumed by handler |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/feed?limit=20"Responses
{"victims": []}/ransomware/statsGlobal ransomware stats
Reads ransomware:stats from Redis.
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" "https://api.ismalicious.com/ransomware/stats"Responses
{}/ransomware/sector-riskSector risk
Without sector, returns all sectors. With sector, returns drill-down for that vertical. Optional press flag.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
sector | string | No | Industry sector name |
press | string | No | Optional modifier |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/sector-risk?sector=Healthcare"Responses
{"sectors": []}/ransomware/sector-risk/historySector risk history
Daily sector-risk snapshots from Redis (ransomware:sector-risk:snapshots). days defaults to 30 (max 120). Optional sector filters to one vertical.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
days | integer | No | Lookback window in days (default: 30) |
sector | string | No | Optional sector name (lowercased server-side) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/sector-risk/history?sector=Technology"Responses
{}/ransomware/sector-victimsSector victims drill-down
Requires sector. Enriches victims, risk level, top groups.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
sector | string | Yes | Sector name |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/sector-victims?sector=Healthcare"Responses
{"sector": "healthcare", "riskLevel": "low", ...}/ransomware/pressPress / newsfeed
limit (default 50, max 100). Optional groups — comma-separated group names to filter.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Entries to return (default: 50) |
groups | string | No | Comma-separated ransomware group names |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/press?limit=10"Responses
{"entries": [], "total": 0}/ransomware/group-profileGroup profile
Requires group — full threat-actor aggregate (TTPs, victims, etc.).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
group | string | Yes | Ransomware group name |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/group-profile?group=lockbit"Responses
{}/ransomware/groupsList ransomware groups
Returns the normalized list of tracked ransomware groups (cached from ransomware.live as ransomware:groups:all, ~6h TTL). limit defaults to 100 (max 500). When the claim history is cached, each group also carries claims30d, claimsPrev30d, lastClaimAt and firstClaimAt, and activity gives the 30-day window totals.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Groups to return (1–500) (default: 100) |
sort | string | No | `victims`: by all-time victim count. `activity`: by claims in the last 30 days, then the latest claim (default: victims) |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/ransomware/groups?limit=50"Responses
{"groups": [], "total": 0}Sources
Threat-source statistics and licensed full export (authenticated + license key for POST).
/sources/statisticsSource statistics
Returns source:statistics from Redis (success, data, timestamp). 404 if cache empty.
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/sources/statistics"Responses
{"success": true, "data": {}, "timestamp": "2026-01-01T00:00:00Z"}/sourcesFull sources list (licensed)
Requires X-License-Key with an active key from license_keys, plus X-API-KEY or session. Returns cached sources:all JSON or embedded sources.json fallback.
Example Request
curl -sS -X POST "https://api.ismalicious.com/sources" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "X-License-Key: YOUR_LICENSE_KEY"Responses
[]Search
Redis SCAN search for keys matching a substring (authenticated). Keywords are passed as a **query** parameter on `POST /search` (no JSON body).
/searchSearch Keywords
POST /search?keywords=<term> — scans Redis keys with pattern *{keywords}* (glob * stripped from input). Caps at 500 hits / 50 iterations. Returns raw Redis key strings in hits.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
keywords | string | Yes | Query parameter (e.g. 'paypal') |
Example Request
curl -X POST "https://api.ismalicious.com/search?keywords=paypal" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS"Responses
{
"keywords": "paypal",
"hits": [
"paypa1-secure.com",
"paypal-login-verify.net",
"secure-paypal-update.org"
],
"total_hits": 127
}STIX / TAXII 2.1
TAXII 2.1 discovery, API roots, collections and objects: STIX bundles that carry the TAXII paging fields, served as `application/taxii+json;version=2.1` to a request whose `Accept` names `application/taxii+json`. **Mirrored** under `/taxii` and `/taxii2` (same handlers). Requires `X-API-KEY` or session; Pro plan for collections (see server enforcement).
/taxiiTAXII discovery (root)
Returns default and api_roots pointing to /taxii/api-root (same for /taxii2).
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Accept: application/taxii+json;version=2.1" \
"https://api.ismalicious.com/taxii"Responses
{"title":"isMalicious","api_roots":["/taxii/api-root"]}/taxii/api-rootAPI root
Lists available collections (collections array) for this API root.
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Accept: application/taxii+json;version=2.1" \
"https://api.ismalicious.com/taxii/api-root"Responses
{"collections":[{"id":"malicious-ips","title":"Malicious IPs"},{"id":"malicious-domains","title":"Malicious Domains"},{"id":"c2-indicators","title":"C2 Indicators"}]}/taxii/api-root/collectionsList collections
Same collection list as embedded in API root (dedicated path).
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/taxii/api-root/collections"Responses
{"collections":[{"id":"malicious-ips","title":"Malicious IPs"},{"id":"malicious-domains","title":"Malicious Domains"},{"id":"phishing-indicators","title":"Phishing Indicators"},{"id":"org-reported-ips","title":"Org-reported IPs"}]}/taxii/api-root/collections/{collectionId}Collection metadata
Metadata for a single collection id (e.g. malicious-domains).
On every shared collection (not the org-reported-* ones) the object also carries three custom properties from the corpus walk (every 12 hours): x_ismalicious_score_histogram (indicator counts per ten-point score band, "0-9" … "90-100" — the same score min_score/max_score filter on, so "60-69" is the size of min_score=60&max_score=69), x_ismalicious_total and x_ismalicious_computed_at. Use them to size a band before polling it. They are absent until the first walk after a deploy.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | Collection UUID or slug |
Example Request
curl -sS -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/taxii/api-root/collections/malicious-domains"Responses
{
"id": "malicious-ips",
"title": "Malicious IP Addresses",
"can_read": true,
"can_write": false,
"media_types": ["application/stix+json;version=2.1"],
"x_ismalicious_score_histogram": { "0-9": 0, "10-19": 0, "20-29": 5811204, "30-39": 190312, "40-49": 301988, "50-59": 51001, "60-69": 219239, "70-79": 0, "80-89": 0, "90-100": 0 },
"x_ismalicious_total": 6573744,
"x_ismalicious_computed_at": "2026-09-12T00:28:41Z"
}/taxii/api-root/collections/{collectionId}/objectsCollection objects (STIX 2.1 bundle with TAXII paging)
Returns one page of the collection (identity + indicators) from Redis-backed threat data, as a STIX 2.1 bundle {"type": "bundle", "id", "objects", "more", "next"}: the identity first, then the indicators newest-created first. It carries the fields of a TAXII 2.1 envelope (more, next, objects), so TAXII clients and bundle readers parse the same body. The content type follows Accept:
•Accept: application/taxii+json;version=2.1 (or application/taxii+json): served as application/taxii+json;version=2.1, with the X-TAXII-Date-Added-* headers to the microsecond and match[type|id|version|spec_version] applied. TAXII 2.1 clients built on taxii2-client, such as the OpenCTI connector and the MISP modules, require this content type.
•Any other Accept, or none: served as application/stix+json;version=2.1, unchanged.
Without match[...] and with limit of 2 or more, both forms return the same objects in the same order and the same pages.
Authentication: X-API-KEY (Base64 apiKey:apiSecret) — Pro subscription required.
Performance: Each request examines at most a fixed number of Redis keys (currently 8,000). If your filters match rarely (e.g. a narrow time window), a page may return fewer than limit indicators even when X-TAXII-Has-More is true — call again with next until the feed is exhausted.
End of a walk: more: false ends the walk, even when that last page is empty. Start the next poll from the first page (without next), with added_after set to when that walk started: with Accept: application/taxii+json, the last page's X-TAXII-Date-Added-Last gives that time, less ten minutes. A next token only continues its own walk: pages follow storage order, not the date added, so re-sending the last token returns that last page again, never newer objects. Each page filters on the added_after it carries: repeat it on every page. Clients built on taxii2-client are the exception: there a page that omits added_after filters on its walk's, and a token sent to another collection or with another added_after starts a new walk.
Date filters: added_after and added_before are compared with the date each indicator was added to the collection (TAXII date_added). Indicators written before we recorded that date use their source's firstSeen, else lastSeen; indicators with none of these are excluded when either filter is set (they cannot be placed in a time window). Indicators without intel timestamps still appear in the bundle when no date query parameters are used.
Full-collection ingestion: For complete feed collection, omit both `added_after` and `added_before`. This returns the most complete dataset — including indicators without intel timestamps — and pagination with next behaves identically. Apply date filters only for incremental/windowed pulls where excluding timestamp-less indicators is acceptable.Recommended polling cadence: once a day. The shared collections are rebuilt by a nightly reload that starts at 02:00 UTC and repopulates the entities this endpoint reads. Responses are computed live — there is no cache in front of them — so the freshness ceiling is that rebuild, not a TTL. Schedule your poll a few hours after it (06:00 UTC is a safe default; the reload's finish time is not tightly bounded) and one call picks up the whole day's new indicators.
Polling more often is permitted and simply returns the same set: added_after is matched against the date each indicator was added to the collection, which only advances when that reload runs. If you would rather notice a delayed or re-run reload without waiting a day, hourly is a sensible ceiling — that buys promptness of noticing, not fresher data. This is guidance, not a quota: no poll interval is enforced, and your plan's per-minute burst limit is the only thing that will stop you.
Exception: the org-reported-* collections are built from your own approved submissions rather than from the nightly reload, so they change as you submit. Poll those as often as your workflow needs.Response headers:
Content-Typeapplication/taxii+json;version=2.1 when Accept names application/taxii+json, application/stix+json;version=2.1 otherwiseX-TAXII-Has-Moretrue if more data exists — pass X-TAXII-Next as the next query parameter.X-TAXII-NextOpaque token (URL-safe Base64 JSON) for the next page.X-TAXII-Date-Added-First / LastEarliest and latest date_added among the indicators in this page. A page without one repeats your added_after, or gives the request time if you sent none. With Accept: application/taxii+json: microsecond precision, and on the last page of a walk Last is where the next poll resumes.VaryAccept: the two forms share a URL.Filters: match[type], match[id], match[version] and match[spec_version] apply only with Accept: application/taxii+json; otherwise they are ignored. match[id] still walks the whole collection to find an id.
Observables collections: each collection id has a twin ending in -observables (malicious-ips-observables, …), not listed by Get Collections. It walks the same indicators and returns each one as a STIX 2.1 observed-data object holding its IP address, domain, URL or file hash in the objects container, for tools that import observables rather than indicator patterns. It carries no revocations, so an indicator retired from isMalicious is never withdrawn from a tool that reads a twin: where removal matters, the blocklists, full snapshots, are the better source.
Manifest and lookups: GET .../collections/{collectionId}/manifest answers the same query with one record per object (id, date_added, version, media_type). GET .../objects/{objectId} and .../versions serve the isMalicious identity; an indicator cannot be looked up by id (404).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | e.g. malicious-domains, malicious-ips, org-reported-ips (org-scoped, API key required) |
limit | integer | No | Max bundle size hint (default 50, max 1001 for Pro; Enterprise has no page limit). Bundle includes one identity object; indicators are capped at approximately limit − 1. Responses stay paginated by size, so a large page comes back with a next token — keep following it to walk the whole collection. |
added_after | string | No | ISO 8601 / RFC 3339. Include only indicators added to the collection on or after this instant (their date_added; firstSeen, else lastSeen, for indicators written before that date was recorded). |
added_before | string | No | ISO 8601 / RFC 3339. Include only indicators added to the collection on or before this instant. Use with added_after for a time window. |
next | string | No | Opaque pagination token from the previous response header X-TAXII-Next. Repeat the same added_after / added_before when paging: each page filters on what it carries. |
min_score | integer | No | Include only indicators whose resolved OpenCTI score is greater than or equal to this value (inclusive). Omit to disable score filtering. Repeat the same min_score when paging with next. |
max_score | integer | No | Upper bound on the same score (inclusive). With min_score it selects a band, e.g. min_score=60&max_score=69. Must be 0–100 and not below min_score (400 otherwise). Repeat it when paging with next. |
Example Request
curl -sS -H "X-API-KEY: $CREDENTIALS" -H "Accept: application/taxii+json;version=2.1" \
"https://api.ismalicious.com/taxii/api-root/collections/malicious-domains/objects?limit=100&added_after=2026-03-01T00:00:00.000Z&added_before=2026-03-03T23:59:59.999Z"Responses
{
"type": "bundle",
"id": "bundle--…",
"objects": [
{
"type": "identity",
"spec_version": "2.1"
},
{
"type": "indicator",
"spec_version": "2.1"
}
],
"more": true,
"next": "eyJ2IjozLCJjdXJzb3IiOiI…"
}Organization Indicators
Submit and consume org-scoped IOCs (PRO / ENTERPRISE). Indicators are auto-approved and isolated per organization.
Report malicious IPs, domains, and file hashes that apply only to your organization. Approved values are served, as soon as they are approved, by the downloads, the counts and the TAXII collections org-reported-ips, org-reported-domains, and org-reported-file-hashes.
Requirements: PRO or ENTERPRISE subscription on the authenticated organization. Daily caps: PRO 200/day, ENTERPRISE 100,000/day (technical guardrail).
Authentication: X-API-KEY (Base64 apiKey:apiSecret) or dashboard session cookie with org membership.
/org/indicatorsSubmit org indicators
Batch submit up to 100 indicators. Duplicates upsert by (organization, type, value). Returns auto-approved rows.
Request Body
{
"indicators": [
{
"type": "ip",
"value": "203.0.113.10",
"category": "malware",
"comment": "IPS correlation"
}
]
}Example Request
curl -sS -X POST "https://api.ismalicious.com/org/indicators" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $CREDENTIALS" \
-d '{"indicators":[{"type":"domain","value":"evil.example.com","category":"phishing"}]}'Responses
{
"message": "Submitted 1 indicator(s)",
"inserted": 1,
"indicators": [
{
"id": "…",
"entityType": "ip",
"value": "203.0.113.10"
}
]
}/org/indicatorsList org indicators
Paginated list filtered by entityType and status.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
entityType | string | No | ip | domain | hash |
status | string | No | approved | revoked |
page | number | No | Page number (default 1) |
limit | number | No | Page size (max 100, default 25) |
Example Request
curl -sS -H "X-API-KEY: $CREDENTIALS" \
"https://api.ismalicious.com/org/indicators?status=approved&limit=50"Responses
{
"indicators": [],
"total": 0,
"page": 1,
"limit": 25
}/org/indicators/{id}Revoke org indicator
Sets status to revoked and removes the value from the org Redis blocklist.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | OrganizationIndicator id |
Example Request
curl -sS -X DELETE -H "X-API-KEY: $CREDENTIALS" \
"https://api.ismalicious.com/org/indicators/{id}"Responses
{
"indicator": {
"id": "…",
"status": "revoked"
}
}/org/blocklists/{entityType}Download org blocklist
Plain text, one IOC per line. entityType: ips, domains, or hashes (aliases ip/domain/hash accepted).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
entityType | string | Yes | ips | domains | hashes |
Example Request
curl -sS -H "X-API-KEY: $CREDENTIALS" \
"https://api.ismalicious.com/org/blocklists/ips"Responses
203.0.113.10
evil.example.com
/org/blocklists/statsOrg blocklist counts
Approved entries per entity type, and when the most recently changed one changed.
Example Request
curl -sS -H "X-API-KEY: $CREDENTIALS" \
"https://api.ismalicious.com/org/blocklists/stats"Responses
{
"ips": 12,
"domains": 4,
"hashes": 1,
"total": 17,
"updatedAt": "2026-05-26T12:00:00.000Z"
}Blocklists
Download threat intelligence blocklists
/blocklist/statsGet Blocklist Stats
Get entry counts and last updated timestamps for all available blocklists. No authentication required.
Example Request
curl -X GET "https://api.ismalicious.com/blocklist/stats"Responses
{
"blocklist-ips-critical.txt": {
"count": 15420,
"lastUpdated": "2024-12-28T07:00:00.000Z"
},
"blocklist-domains-phishing.txt": {
"count": 89234,
"lastUpdated": "2024-12-28T07:00:00.000Z"
},
"blocklist-domains-malware.txt": {
"count": 45123,
"lastUpdated": "2024-12-28T07:00:00.000Z"
}
}/blocklist/download/{filename}Download Blocklist
Download a specific blocklist file.
Auth: optional — this is a public route, no API key required to try it. To get the full lists, authenticate with X-API-KEY: base64(apiKey:apiSecret), or Authorization: Basic with username = apiKey and password = apiSecret (for appliances that cap a single API-key field at 64 characters).
Plan Access:
•Anonymous (no credentials): 10% sample (lite version)
•FREE: 10% sample (lite version)
•BASIC and above: Full blocklist
There is no 401 or plan-restriction 403 on this route: every caller gets a file, and the served slice (lite vs full) follows the credentials presented.
Available Blocklists:
•blocklist-ips-critical.txt - Critical severity IPs
•blocklist-ips-all.txt - All malicious IPs
•blocklist-ips-c2.txt - C2 server IPs
•blocklist-ips-botnet.txt - Botnet IPs
•blocklist-domains-phishing.txt - Phishing domains
•blocklist-domains-malware.txt - Malware domains
•blocklist-domains-ransomware.txt - Ransomware domains
•blocklist-domains-all.txt - All malicious domains
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | Blocklist filename |
Example Request
# Anonymous: 10% lite sample, no key needed
curl -X GET "https://api.ismalicious.com/blocklist/download/blocklist-domains-phishing.txt" \
-o blocklist-domains-phishing.txt
# Authenticated (BASIC+): full blocklist
curl -X GET "https://api.ismalicious.com/blocklist/download/blocklist-domains-phishing.txt" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-o blocklist-domains-phishing.txtResponses
malicious-domain1.com
malicious-domain2.com
malicious-domain3.com
...Submit Sources
Submit new threat intelligence sources
/submitSubmit Sources
Submit new threat intelligence sources to the community database.
Categories: malware, phishing, spam, scam, fraud, botnet, ransomware, c2
Request Body
{
"sources": [
{
"name": "Example Threat Feed",
"type": "ip",
"url": "https://example.com/threats-ips.txt",
"category": "malware"
},
{
"name": "Phishing Domains Feed",
"type": "domain",
"url": "https://example.com/phishing-domains.txt",
"category": "phishing"
}
]
}Example Request
curl -X POST "https://api.ismalicious.com/submit" \
-H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{
"sources": [{
"name": "My Threat Feed",
"type": "domain",
"url": "https://example.com/feed.txt",
"category": "phishing"
}]
}'Responses
{
"message": "Submitted successfully, thanks sharing new sources with us!"
}Streaming (SSE)
Progressive check results over Server-Sent Events
Endpoint: GET /check/stream?query=<target>
Streams the phased report pipeline for one target (IP, domain, URL, or file hash).
Auth: optional. API-key calls (X-API-KEY with Base64 apiKey:apiSecret) draw on your plan's burst and monthly quota. Signed-in dashboard sessions are not metered. Anonymous calls are rate-limited per IP (10 an hour): they replay cached reports, and a live check needs the token the report page issues (otherwise 429). Without that token, anonymous calls also count against a monthly cap per IP equal to the Free plan's quota (50 a month).
Requirements: query is required. IP, domain, URL, and file hash (MD5/SHA1/SHA256) targets all stream — hashes follow a cached-first path with dedicated cache TTLs.
Event format: Each SSE data line is JSON:
{ "type": "reputation", "data": { } }Phases include reputation, basic, insights, optional dns (domains), enrichment, then done. The insights payload may include asnReputation ({ asn, listed, list: "spamhaus-asn-drop", … }) when an ASN resolves — a light network-context signal (also contributes a small capped boost to riskScore when listed is true). The enrichment payload may include otx (AlienVault OTX): pulses and, with full enrichment, labs reputation and passive DNS for IP/domain targets when OTX succeeds (public API; optional OTX_API_KEY for rate limits).
Example:
curl -N -H "X-API-KEY: YOUR_BASE64_CREDENTIALS" \
"https://api.ismalicious.com/check/stream?query=example.com"Dashboard & platform API
Additional routes on the same host power the web app (session cookie) and accept `X-API-KEY` where `require_any_auth` is applied.
These are implemented in apps/rust-api/src/main.rs (session + API key tier). Full request/response schemas are best read from openapi.json when present, or from source.
Reports — GET/POST /reports, GET/DELETE /reports/{id}, GET /reports/export, POST /reports/save, POST /reports/entity-export
Monitoring — GET/POST /monitoring, GET /monitoring/metrics, POST /monitoring/toggle-notify, POST /monitoring/unwatch
CVE Watch — /cve-watch/perimeters (CRUD, plus /{id}/cpes, /{id}/cves, /{id}/sync), /cve-watch/findings (with /{id}, /bulk-status, /stats), /cve-watch/cpe-search, /cve-watch/vendor-search, /cve-watch/global-threats, /cve-watch/recent-publications
Alerts — GET /alerts, GET /alerts/metrics, GET /alerts/funnel, PATCH /alerts/{id}
Action center & cases — GET /action-center/overview, GET/POST /cases, PATCH /cases/{id}, POST /cases/{id}/evidence, GET /risk-brief/latest, GET /trust/assessment/schema
Data-to-action surfaces — GET /platform/data-freshness, GET /platform/data-ops/pipeline, GET /network/intel
User / webhooks — GET /user/analytics, GET/POST /user/webhooks, GET/PATCH/DELETE /user/webhooks/{id}, GET /user/webhooks/events
Billing & subscription — POST /billing/checkout, POST /billing/portal, /subscription/*, Stripe POST /webhook/stripe (called by Stripe, not customers)
Onboarding — /onboarding/status, /onboarding/save-step, /onboarding/complete, /onboarding/complete-wizard
GPT / AI — GET /gpt/check, GET /gpt/search, GET /gpt/email, GET /gpt/ransomware; POST /ai-analysis
MMDB — GET /mmdb/info, GET /mmdb/download
Metrics (auth) — GET /metrics/subscription, GET /metrics/threats, GET /cve/stats/top-products (PG or Redis; meta.source), GET /cve/vendors (paginated, search, sort), GET /cve/products (paginated, vendor filter, search, sort). Populate PG via sync-opencve-catalog; Redis fallback via opencve-global-top-products.
Admin / email / CVE dashboard JSON — /admin/*, /emails/*, /cve/dashboard/* (operators; Infinity / dashboards; separate credentials)
Public utilities — POST /contact, POST /feedback, POST /newsletter, POST /license, GET|POST /cache, GET /debug/headers, GET /internal/whois (internal header auth), cron routes with bearer tokens.
Rate Limits
API rate limiting information
API rate limits vary by subscription plan. Each plan combines a burst limit (short rolling window) and a monthly quota on reputation checks. Every plan — including FREE — comes with API access and an API key.
| Plan | API access | Burst limit | Monthly checks | Monthly scans (/gate, /mail) |
|---|---|---|---|---|
| FREE | Yes | 60 / minute | 50 | 1,000 |
| BASIC | Yes | 60 / minute | 2,000 | 25,000 |
| PRO | Yes | 60 / minute | 10,000 | 250,000 |
| ENTERPRISE | Yes | 5,000 / minute | 1,000,000 | 1,000,000 |
Scans are a separate meter. The "Monthly scans" column applies to the /gate surface (prompt-injection detection and link scanning) and to POST /mail/scan (one scan per email message). Scans are counted on their own quota and never consume your monthly checks — and vice versa.
Rate Limit Headers:
Two sets of headers, one per limit:
•Per-minute burst, on every response: X-RateLimit-Limit (the burst allowance), X-RateLimit-Remaining, and X-RateLimit-Reset, when the window resets, in milliseconds since the Unix epoch.
•Monthly quota, on every response that counts against it: X-Monthly-Usage, X-Monthly-Limit and X-Monthly-Percentage. The quota resets on the 1st of the month at 00:00 UTC. An organization on a negotiated daily window gets X-Daily-Usage / X-Daily-Limit instead.
When Rate Limited:
•A burst 429 carries X-RateLimit-* and Retry-After in seconds: wait, then resume with backoff.
•A monthly-quota 429 carries X-Monthly-*, resetsAt (ISO 8601) and Retry-After until the 1st at 00:00 UTC. Retrying before then only spends calls: stop, or move to a higher plan from the options in the body (see *Error Codes*).
•Without an API key: 10 requests an hour per IP and, on metered routes, a monthly cap per IP equal to the Free plan's quota (50), one month shared by every metered route. The report page's own stream is exempt from the monthly cap. Its 429 carries X-RateLimit-Reason: monthly and resetsAt.
Best Practices:
•Cache responses when possible
•Use batch endpoints for bulk operations
•Back off and retry on a burst 429; never retry a monthly-quota 429 before its reset
•Monitor your usage via the dashboard
Error Codes
API error response formats
All API errors return a consistent JSON format:
{
"error": "Error Type",
"message": "Detailed error message"
}HTTP Status Codes:
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing or invalid API key |
| 403 | Forbidden - Access denied (plan restriction) |
| 404 | Not Found - Resource doesn't exist |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |
Common Errors:
400 Bad Request
{
"error": "Bad Request",
"message": "Missing required parameter: query"
}401 Unauthorized
{
"error": "Unauthorized",
"message": "Invalid API key. Get a free API key in one request: curl -d \"email=you@example.com\" https://ismalicious.com/api/keys/instant",
"docs": "https://ismalicious.com/api-docs/authentication"
}429 Rate Limited — burst (Retry-After in seconds) or monthly quota (Retry-After until the 1st at 00:00 UTC, and resetsAt). cta.primary names what the body offers:
•upgrade — a Free account gets promoCode, a checkout cta.url and cta.options, the plans on offer, each with plan, name, requestsPerMonth, priceMonthlyEur and an absolute checkout url; a Basic account gets cta.url to its account page, where the switch to Pro is prorated.
•contact — Pro and Enterprise: cta.url is the quote form, and upgradeRequired is false.
•billing — a paid subscription on hold, served the Free quota until its payment is settled: billingIssue: true, and cta.url is the account page. No checkout.
Identify an option by its plan, never by comparing its url with cta.url.
{
"error": "Monthly quota exceeded",
"message": "You've used all 50 requests this month. Upgrade to Basic for 2,000 requests/month (€49/month) or Pro for 10,000 requests/month (€99/month), or wait for the reset on the 1st at 00:00 UTC.",
"usage": { "current": 51, "limit": 50, "percentage": 100 },
"retryAfter": 812345,
"resetsAt": "2026-11-01T00:00:00Z",
"upgradeRequired": true,
"promoCode": "QUOTA10",
"promoDiscount": 10,
"cta": {
"primary": "upgrade",
"url": "https://ismalicious.com/subscribe/pro-monthly?promo=QUOTA10&trigger=rate_limit&feature=monthly_quota",
"reason": "Use code QUOTA10 for 10% off your first month — applied at checkout",
"options": [
{
"plan": "BASIC_MONTHLY",
"name": "Basic",
"requestsPerMonth": 2000,
"priceMonthlyEur": 49,
"url": "https://ismalicious.com/subscribe/basic-monthly?trigger=rate_limit&feature=monthly_quota&promo=QUOTA10"
},
{
"plan": "PRO_MONTHLY",
"name": "Pro",
"requestsPerMonth": 10000,
"priceMonthlyEur": 99,
"url": "https://ismalicious.com/subscribe/pro-monthly?promo=QUOTA10&trigger=rate_limit&feature=monthly_quota"
}
]
},
"docs": "https://ismalicious.com/api-docs/rate-limits"
}Enterprise SSO (OIDC)
Configure OpenID Connect SSO for your organization (Enterprise plan). Users sign in at `/auth/sso` with a work email, get redirected to your IdP, and return through `/api/auth/sso/callback`.
Okta (OIDC)
1.In Okta Admin → Applications → Create App Integration → OIDC / Web.
2.Sign-in redirect URI: https://YOUR_DOMAIN/api/auth/sso/callback
3.Copy Issuer (e.g. https://YOUR_OKTA_DOMAIN/oauth2/default), Client ID, and Client secret.
4.In isMalicious → Account → SSO, set Email domain to your corporate domain (e.g. acme.com), paste issuer/client values, and save.
5.Optional: enable Require SSO to block password and social login for that domain.
Microsoft Entra ID (Azure AD)
1.App registrations → New registration → Web redirect URI https://YOUR_DOMAIN/api/auth/sso/callback.
2.Create a client secret under Certificates & secrets.
3.Issuer URL: https://login.microsoftonline.com/TENANT_ID/v2.0
4.Configure the same values in Account → SSO and test from /auth/sso.
User flow
1.User visits /auth/sso and enters user@company.com.
2.POST /api/auth/sso/lookup resolves the organization.
3.Browser redirects to GET /api/auth/sso/start?orgId=... → IdP login.
4.IdP returns to /api/auth/sso/callback → session established via one-time token at /auth/sso/complete.
Callback URL (register with IdP): https://YOUR_DOMAIN/api/auth/sso/callback
/api/organization/ssoRead SSO configuration (Enterprise admin)
Returns redacted OIDC settings for the authenticated organization. Requires Enterprise plan and OWNER/ADMIN role.
Example Request
curl -sS -b cookies.txt https://ismalicious.com/api/organization/ssoResponses
{"connection":{"emailDomain":"acme.com","issuerUrl":"https://login.okta.com/oauth2/default","clientId":"...","hasClientSecret":true,"enforceSso":true,"enabled":true}}/api/auth/sso/lookupDiscover SSO by email
Public endpoint used by the SSO login page. Returns { found, organizationId, domain } when an enabled connection matches the email domain.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User work email (JSON body) |
Example Request
curl -sS -X POST https://ismalicious.com/api/auth/sso/lookup -H "Content-Type: application/json" -d '{"email":"analyst@acme.com"}'Responses
{"found":true,"organizationId":"550e8400-e29b-41d4-a716-446655440000","domain":"acme.com"}Prêt à commencer ?
Obtenez votre clé API gratuite et commencez à protéger votre infrastructure dès aujourd'hui.