POST/v1/phishing/safe-browsing

Safe Browsing Check

Check URLs against Google Safe Browsing to identify pages flagged for malware, social engineering, unwanted software, or potentially harmful applications.

Try this endpoint in the playground →

Credits

2

Per request

Max URLs

500

Per request

Plans

All Plans

Free, Starter, Professional, Enterprise

Threat Types Checked

Threat TypeDescription
MALWAREPages that host or distribute malicious software
SOCIAL_ENGINEERINGPhishing pages that trick users into revealing sensitive information
UNWANTED_SOFTWAREPages that distribute deceptive or unwanted software
POTENTIALLY_HARMFUL_APPLICATIONPages hosting applications that may be harmful to the user or device

Request Body

FieldTypeRequiredDescription
urlsstring[]YesArray of URLs to check (max 500)

Code Examples

cURL

curl -X POST https://api.dfir-lab.ch/v1/phishing/safe-browsing \
  -H "Authorization: Bearer sk-dfir-your-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://example.com",
      "http://malware-test.example.org/payload",
      "https://legitimate-site.com"
    ]
  }'

Python

import requests

response = requests.post(
    "https://api.dfir-lab.ch/v1/phishing/safe-browsing",
    headers={
        "Authorization": "Bearer sk-dfir-your-key-here",
        "Content-Type": "application/json",
    },
    json={
        "urls": [
            "https://example.com",
            "http://malware-test.example.org/payload",
            "https://legitimate-site.com",
        ]
    },
)

response.raise_for_status()
payload = response.json()
data = payload.get("data", payload)
for url_result in data["results"]:
    threats = url_result.get("threats")
    if isinstance(threats, list) and threats:
        print(f"{url_result['url']}: potential threat — inspect evidence")
    elif url_result.get("state") == "complete" and url_result.get("assessment") == "no_known_threats" and url_result.get("safe") is True and threats == [] and not url_result.get("error"):
        print(f"{url_result['url']}: no known threat-list match; safety unverified")
    else:
        print(f"{url_result.get('url', '(missing URL)')}: unknown or incomplete")

Example Response

{
  "data": {
    "results": [
      {
        "url": "https://example.com/page",
        "safe": true,
        "assessment": "no_known_threats",
        "state": "complete",
        "threats": [],
        "checked_at": "2026-09-15T07:00:00Z",
        "expires_at": "2026-09-15T07:05:00Z",
        "coverage": {
          "reputation": "complete",
          "redirects": "not_requested",
          "content": "not_requested"
        },
        "limitations": [
          "reputation_only",
          "no_match_does_not_establish_safety",
          "destination_content_not_inspected"
        ]
      }
    ],
    "summary": {
      "total": 1,
      "safe": 1,
      "flagged": 0,
      "no_known_threats": 1
    },
    "provider": "google_safe_browsing",
    "threat_types": [
      "MALWARE",
      "SOCIAL_ENGINEERING",
      "UNWANTED_SOFTWARE",
      "POTENTIALLY_HARMFUL_APPLICATION"
    ]
  },
  "meta": {
    "request_id": "example-request",
    "credits_used": 2,
    "credits_remaining": 98,
    "processing_time_ms": 100
  }
}

Results are powered by the Google Safe Browsing Lookup API v4. Coverage depends on the threat lists available at query time. New, targeted, or changed pages may not be listed.

A URL being marked as safe means it was not found in Google's threat lists at the time of the query. It is not a guarantee of safety. This endpoint does not fetch the destination, follow redirects, or inspect page content.

Treat missing fields, provider errors, and incomplete results as unknown. Match every response URL to a requested URL and require complete coverage before describing the lookup as complete. Never label an empty threat list as safe or legitimate. Prefer the explicit assessment and state fields; safe is a deprecated compatibility alias. Provider outages return 503 and malformed provider responses return 502. Threat entries use threatType and platformType. Do not reuse expired results.

A threat match's returned expiry retains at least Google's cacheDuration. Do not shorten that retention to the default no-match lifetime. In aggregate assessments, positive evidence keeps this expiry even when another requested check is incomplete. Expiry calls for a fresh assessment; it is not a safety verdict.