POST/api/v1/phishing/checkphish

CheckPhish URL Scan

Scan URLs against CheckPhish.ai for phishing detection, brand impersonation, and malware hosting. Uses a submit-and-poll pattern with SSRF protection.

Credits

2

Per request

Max URLs

5

Per request

Timeout

60s

Polling up to 30s

Plans

All

Free, Starter, Professional, Enterprise

Disposition Types

DispositionDescription
cleanURL is not flagged for any threats
phishingURL identified as a phishing page attempting to steal credentials or data
malwareURL is hosting or distributing malicious software
suspiciousURL exhibits suspicious characteristics but is not definitively malicious
unknownCheckPhish could not determine the disposition of the URL

Request Body

FieldTypeRequiredDescription
urlsstring[]YesArray of 1-5 URLs to scan. Must use http:// or https:// scheme. Duplicates are removed before scanning.

How It Works

  1. URLs are validated, deduplicated, and checked against SSRF protection (private/reserved IPs are blocked).
  2. Each URL is submitted to CheckPhish.ai sequentially (rate limit friendly).
  3. The API polls CheckPhish for results (up to 30s per URL) using a submit-and-poll pattern.
  4. Results are returned with disposition, brand detection, insights, and screenshot URL.

Code Examples

cURL

curl -X POST https://api.dfir-lab.ch/v1/phishing/checkphish \
  -H "Authorization: Bearer sk-dfir-your-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://suspicious-site.com",
      "https://another-url.com"
    ]
  }'

Python

import requests

response = requests.post(
    "https://api.dfir-lab.ch/v1/phishing/checkphish",
    headers={
        "Authorization": "Bearer sk-dfir-your-key-here",
        "Content-Type": "application/json",
    },
    json={
        "urls": [
            "https://suspicious-site.com",
            "https://another-url.com",
        ]
    },
    timeout=90,  # Allow for polling time
)

data = response.json()
for result in data["data"]["results"]:
    disposition = result["disposition"]
    brand = result.get("brand", "N/A")
    if disposition != "clean":
        print(f"FLAGGED {result['url']}: {disposition} (brand: {brand})")
    else:
        print(f"CLEAN   {result['url']}")

TypeScript

const response = await fetch(
  "https://api.dfir-lab.ch/v1/phishing/checkphish",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer sk-dfir-your-key-here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      urls: [
        "https://suspicious-site.com",
        "https://another-url.com",
      ],
    }),
  }
);

const { data, meta } = await response.json();

console.log(`Credits used: ${meta.credits_used}`);
console.log(`Summary: ${data.summary.flagged} flagged / ${data.summary.total} total`);

for (const result of data.results) {
  const status = result.disposition === "clean" ? "CLEAN" : "FLAGGED";
  console.log(`[${status}] ${result.url} - ${result.disposition}`);
}

Example Response

{
  "data": {
    "results": [
      {
        "url": "https://suspicious-site.com",
        "jobID": "abc123",
        "status": "done",
        "disposition": "phishing",
        "brand": "PayPal",
        "insights": {},
        "screenshot": "https://...",
        "resolved": true
      },
      {
        "url": "https://another-url.com",
        "jobID": "def456",
        "status": "done",
        "disposition": "clean",
        "brand": "",
        "insights": {},
        "screenshot": "https://...",
        "resolved": true
      }
    ],
    "summary": {
      "total": 2,
      "clean": 1,
      "flagged": 1,
      "pending": 0
    }
  },
  "meta": {
    "request_id": "req_abc",
    "credits_used": 2,
    "credits_remaining": 98,
    "processing_time_ms": 5000
  }
}

Response Fields

FieldTypeDescription
urlstringThe scanned URL
jobIDstringCheckPhish job identifier
status"done" | "pending" | "error"Scan completion status
disposition"clean" | "phishing" | "malware" | "suspicious" | "unknown"Threat classification
brandstringImpersonated brand detected (e.g. PayPal, Microsoft)
insightsobjectAdditional scan insights from CheckPhish
screenshotstringURL to a screenshot of the scanned page
resolvedbooleanWhether the URL resolved successfully

Results are powered by CheckPhish.ai by Bolster. The submit-and-poll pattern means responses may take longer than other endpoints (up to 60 seconds for 5 URLs).

SSRF protection blocks URLs that resolve to private or reserved IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, etc.). Each URL is scanned sequentially to respect upstream rate limits.

A clean disposition means CheckPhish did not flag the URL at the time of the scan. It is not a guarantee of safety.