@ismalicious/sdk

Official JavaScript/TypeScript SDK for the isMalicious threat intelligence API.
Installation
npm install @ismalicious/sdk
yarn add @ismalicious/sdk
pnpm add @ismalicious/sdk
Quick Start
import { IsMalicious } from "@ismalicious/sdk";
const client = new IsMalicious({
apiKey: "your-api-key",
apiSecret: "your-api-secret",
});
const result = await client.check("suspicious-domain.com");
console.log(result.malicious);
console.log(result.reputation);
console.log(result.riskScore?.score);
Note: Get your API key and secret from your isMalicious dashboard. The credentials are automatically Base64 encoded by the SDK for authentication.
Features
- Full TypeScript support with comprehensive type definitions
- Automatic retry with exponential backoff
- Rate limit handling with built-in tracking
- Multiple entity types: domains, IPs, file hashes, emails, phones, crypto wallets, and passwords (breach exposure, k-anonymity)
- Enrichment levels: basic, standard, and full intelligence
- Gate for AI agents: prompt-injection scan and link reputation (
client.gate), on a separate scan meter
- Mail scan: one inbound email message read for phishing and malware (
client.mail), one scan of the same meter per message
Usage
Check a Domain or IP
const result = await client.check("example.com");
const enriched = await client.check("8.8.8.8", {
enrichment: "full",
});
console.log(enriched.riskScore?.score);
console.log(enriched.classification?.primary);
console.log(enriched.confidence?.level);
console.log(enriched.evidence?.recommendedAction);
Explain a Verdict
const result = await client.check("example.com", {
enrichment: "standard",
});
console.log(result.dataTrust?.freshness);
console.log(result.dataTrust?.sourceAgreement.level);
console.log(result.evidence?.reasons.slice(0, 3));
console.log(result.evidence?.recommendedAction);
Check a File Hash
const hashResult = await client.checkHash("d41d8cd98f00b204e9800998ecf8427e");
console.log(hashResult.malicious);
console.log(hashResult.hashInfo?.fileType);
console.log(hashResult.hashInfo?.detectionStats);
Check a Password
Whether a password appears in known data breaches (Have I Been Pwned's Pwned
Passwords corpus, over 2 billion hashes), and how many times.
const result = await client.checkPassword(candidatePassword);
if (result.exposed) {
console.log(`Seen ${result.count} times in breaches (${result.prevalence})`);
}
await client.checkPasswordHash({ ntlm: "8846F7EAEE8FB117AD06BDD830B7586C" });
const range = await client.getPasswordRange("5BAA6");
verdict: "not_found" means the password is not in known breach dumps; it
says nothing about its strength. Hashing uses Web Crypto when the runtime has
it, and a built-in SHA-1 otherwise (Node 18).
Analyze / Email / Phone / Crypto (web API)
These methods hit the Next.js host (https://ismalicious.com/api by default via webBaseUrl), not api.ismalicious.com.
const client = new IsMalicious({
apiKey: "your-api-key",
apiSecret: "your-api-secret",
});
const any = await client.analyze("user@example.com");
await client.checkEmail("user@example.com");
await client.checkPhone("+14155552671");
await client.checkCrypto("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
Keep baseUrl (default https://api.ismalicious.com) for check, stream, monitoring, CVE, ransomware, and TAXII.
Bulk Check
const bulkResult = await client.checkBulk([
"suspicious-domain.com",
"8.8.8.8",
"d41d8cd98f00b204e9800998ecf8427e",
]);
console.log(
`${bulkResult.maliciousCount} of ${bulkResult.totalChecked} are malicious`,
);
for (const item of bulkResult.results) {
console.log(`${item.entity}: ${item.malicious ? "MALICIOUS" : "clean"}`);
}
Search
const searchResult = await client.search("phishing");
console.log(searchResult.hits);
Get Blocklist
Downloads pre-generated blocklists
(GET /blocklist/download/blocklist-{ips|domains|urls|hashes}-{level|category}[-format].txt).
Anonymous requests receive a 10% sample; a Basic plan or higher receives the
full list.
const domains = await client.getBlocklist();
const criticalIps = await client.getBlocklist({
entityType: "ips",
level: "critical",
});
const malwareHosts = await client.getBlocklist({
category: "malware",
format: "hosts",
});
const phishingAdguard = await client.getBlocklist({
category: "phishing",
format: "adguard",
});
Levels: critical, high, medium, low, all. Categories include
malware, phishing, spam, botnet, ransomware, c2, … (see the
BlocklistCategory type for the full list). The legacy type option is
deprecated but still accepted: "full" maps to level "all", other values
map to the category of the same name.
Configuration Options
const client = new IsMalicious({
apiKey: "your-api-key",
apiSecret: "your-api-secret",
baseUrl: "https://api.ismalicious.com",
webBaseUrl: "https://ismalicious.com/api",
timeout: 30000,
retries: 3,
fetch: customFetch,
});
Check Options
interface CheckOptions {
enrichment?: EnrichmentLevel;
intelowl?: boolean;
trackReports?: boolean;
}
Error Handling
import {
IsMalicious,
AuthenticationError,
RateLimitError,
ValidationError,
} from "@ismalicious/sdk";
try {
const result = await client.check("example.com");
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Invalid API key");
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
} else if (error instanceof ValidationError) {
console.error(`Invalid input: ${error.message}`);
}
}
Rate Limiting
The SDK automatically tracks rate limit information from API responses:
const result = await client.check("example.com");
const rateLimit = client.getRateLimitInfo();
console.log(
`${rateLimit?.remaining} of ${rateLimit?.limit} requests remaining`,
);
console.log(`Resets at: ${rateLimit?.reset}`);
Response Types
CheckResponse
interface CheckResponse {
malicious: boolean;
reputation: {
malicious: number;
suspicious: number;
harmless: number;
undetected: number;
timeout?: number;
};
apiVersion: string;
enrichmentLevel?: "basic" | "standard" | "full";
processingTime?: number;
sources?: MaliciousSource[];
riskScore?: RiskScore;
classification?: ThreatClassification;
confidence?: ConfidenceScore;
dataTrust?: DataTrustProfile;
evidence?: SocEvidence;
blocklistHits?: number;
blocklistListed?: boolean;
infrastructure?: InfrastructureAttribution;
geo?: GeoLocation;
whois?: WhoisInfo;
certificates?: CertificateInfo[];
hashInfo?: HashInfo;
mitre?: MITREMapping;
}
Listing classes
Every entry of sources[] carries an optional threatClass — "threat"
(the default when absent), "infrastructure", "policy" or "allowlist" —
and an optional fpRisk ("low" by default, "medium", "high"), the
publisher's own false-positive warning. A Tor exit list, a cloud provider's
published ranges or an ad-blocking list describe what the entity is or what
a customer may choose to block; they are not a malicious verdict, so only
"threat" listings count towards riskScore and blocklistHits (and, for
hashes, malicious); a non-threat listing never raises malicious on a
domain or an IP. The others are summarised under infrastructure:
interface InfrastructureAttribution {
attributes: InfrastructureAttribute[];
sources: Array<{
id?: string;
name: string;
category?: string;
threatClass: ThreatClass;
}>;
}
Both class types accept any string so that a class added to the registry
before this SDK is updated still parses; compare against the known values
rather than switching exhaustively.
const result = await client.check("13.107.6.152");
const threats = (result.sources ?? []).filter(
(s) => (s.threatClass ?? "threat") === "threat",
);
if (threats.length === 0 && result.infrastructure) {
console.log(
`Not a threat; known as ${result.infrastructure.attributes.join(", ")}`,
);
}
Modules
The SDK provides specialized modules for different API capabilities:
Monitoring
Watch and unwatch domains/IPs for threat notifications:
const watchResult = await client.monitoring.watch("suspicious-domain.com", {
type: "domain",
});
const watched = await client.monitoring.list();
console.log(watched.domains.length, watched.ips.length);
await client.monitoring.unwatch({ id: "watch-record-id", type: "domain" });
await client.monitoring.toggleNotify({
id: "watch-record-id",
type: "domain",
notifyOnChange: false,
});
AI Analysis
Get AI-powered threat analysis with MITRE ATT&CK mapping:
const checkResult = await client.check("suspicious-domain.com", {
enrichment: "full",
});
const analysis = await client.ai.analyze(checkResult);
console.log(analysis.summary);
console.log(analysis.techniques);
console.log(analysis.recommendations);
const stream = await client.ai.analyzeStream(checkResult, {
onData: (event) => process.stdout.write(event.content),
onComplete: () => console.log("\nAnalysis complete"),
onError: (err) => console.error(err),
});
stream.close();
Gate (isinjected) — scan before you act
Prompt-injection detection plus link reputation for AI agents, over the same
30M-indicator dataset. Scans are a separate meter from the request quota:
scan() and url() never decrement the monthly /check allowance, and
quota() reads the scan meter (Free 1,000, Basic 25,000, Pro 250,000,
Enterprise 1,000,000 scans/month).
const scan = await client.gate.scan(pageText, {
sourceUrl: "https://example.com/page",
});
console.log(scan.verdict);
console.log(scan.injection.families);
console.log(scan.links);
if (scan.sanitized_content) useInstead(scan.sanitized_content);
const rep = await client.gate.url("https://evil.example/login");
console.log(rep.verdict);
const q = await client.gate.quota();
console.log(`${q.remaining} of ${q.limit} scans left (${q.plan})`);
Over the scan allowance the API answers 429 with { error, usage, limit },
raised as RateLimitError.
Mail scan — read one message before it is delivered
One inbound email message against the dataset, for a mail system or an agent
that already has its own detection (it is not a gateway). It reads the sender
and Reply-To, the server that delivered the message, every link host and
every attachment hash, the tells of a phishing message (link text that names
another site, a domain or a display name that imitates a well-known brand,
invoice.pdf.exe, a display name that shows another address) and prompt
injection aimed at an AI reading the mail, and it reports what it could not
check. It never runs an attachment, fetches a link or calls a third party while
you wait.
One scan of the scan meter per message, however many links and attachments
it carries, and never a request of the monthly quota; client.gate.quota()
reads the same meter.
const result = await client.mail.scan(
{ message: { headers, text, html, attachments } },
{ authservId: "mx.corp.example" },
);
console.log(result.verdict);
console.log(result.recommendedAction);
console.log(result.headline);
for (const reason of result.reasons) console.log(reason.code, reason.summary);
console.log(result.coverage.skipped);
await client.mail.scan({ eml: rawMessage });
await client.mail.scan({ emlBase64: base64Message });
How to read the answer:
malicious needs a listing in the dataset: the shape of a message alone asks
for a review at most.
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. Without it, a message with nothing against it is inconclusive,
which is not safe. Even with it, clean is kept for a sender domain the
dataset knows as established (among the 100 000 most visited, not a free
mailbox): attackers publish DMARC for the domains they register.
deliver means no objection from this scan. Never use it to release a
message another engine quarantined.
trustedHops and connectingIp say which server delivered the message to
yours; hosts in reasons[].evidence are defanged (evil[.]test).
- From a raw message,
attachments[].detectedType says what the bytes are and
flags what was found in them (disguised_program, macro_project,
remote_template, archive_risky, pdf_launch, html_smuggling…), never by
running anything. Addresses found inside a file or an attached message are
links of their own, tagged origin and originName.
sender.posture.spoofable says nothing the sender's domain publishes would
make a receiver reject a message forged with it.
Over the scan allowance the API answers 429 with { error, usage, limit },
raised as RateLimitError; a message over 10 MiB is refused with 413.
Threat Event Stream (deprecated)
Deprecated. client.stream subscribes to GET /stream, which the
server backs with a simulated preview feed (threat_stream.rs, "mock
threat feed") — the events are generated, not observed. The module stays
for compile compatibility and may be removed in the next major. Use
client.checkStream for live report enrichment or client.taxii for the
real indicator feed.
Requires a Pro or Enterprise plan (the server answers 403 otherwise).
const subscription = await client.stream.subscribe(
{
types: ["threat.new", "watchlist.alert"],
severity: "high",
categories: ["malware", "ransomware"],
},
{
onEvent: (event) => {
console.log(
`${event.type}: ${event.data.entity} - ${event.data.category}`,
);
},
onError: (error) => {
console.error("Stream error:", error);
},
onClose: () => console.log("Stream closed"),
},
);
subscription.close();
Ransomware Intelligence
Access ransomware victim data and statistics:
const feed = await client.ransomware.getFeed({
limit: 50,
groups: ["lockbit", "blackcat"],
});
const stats = await client.ransomware.getStats();
console.log(`Total victims: ${stats.totalVictims}`);
const check = await client.ransomware.check("company.com");
CVE/Vulnerability Lookup
Search and retrieve CVE information:
const results = await client.cve.search("remote code execution", {
severity: "CRITICAL",
limit: 10,
});
const recent = await client.cve.getRecent(7, 20);
const cve = await client.cve.lookup("CVE-2024-1234");
Webhooks
Manage webhook notifications:
const webhook = await client.webhooks.create({
name: "My alerting webhook",
url: "https://your-app.com/webhook",
events: ["threat.detected", "monitor.alert"],
});
console.log(webhook.secret);
const webhooks = await client.webhooks.list();
await client.webhooks.update(webhook.id, {
events: ["threat.detected"],
});
await client.webhooks.delete(webhook.id);
Reports
Save and export threat reports:
const saved = await client.reports.save({
entity: "malicious-domain.com",
type: "domain",
});
console.log(saved.id, saved.usage.current, saved.usage.limit);
const reports = await client.reports.list();
const csv = await client.reports.export({ format: "csv" });
await client.reports.delete(saved.id);
TAXII (STIX/TAXII 2.1)
Access threat intelligence in STIX format:
const apiRoot = await client.taxii.getApiRoot();
const collections = await client.taxii.getCollections();
const objects = await client.taxii.getObjects("malicious-domains", {
type: ["indicator", "malware"],
limit: 100,
});
const { objects: matches } = await client.taxii.getObjects(
"malicious-domains",
{ id: "indicator--12345" },
);
Requirements
- Node.js 18+ (or provide a custom
fetch implementation)
License
MIT