Tutorial · Threat API · Free key

    Sync a blocklist to DNS or your firewall

    Keep a local copy of every active confirmed-abuse domain for a DNS resolver, firewall, proxy or Pi-hole.

    1Simple: download the feed

    The text feed has one active domain per line. A free key may download feeds 24 times a day, so hourly is fine.

    # /etc/cron.hourly/fraudpol-feed
    curl -fsS "https://ugapgzpffrykyglevlhz.supabase.co/functions/v1/threat-api/v1/feed.txt" -H "X-API-Key: $FP_KEY" \
      -o /etc/blocklists/fraudpol.txt.new \
      && mv /etc/blocklists/fraudpol.txt.new /etc/blocklists/fraudpol.txt

    Use /v1/feed.json for a JSON array instead.

    2Advanced: incremental sync

    For large deployments, fetch only what changed. Keep since fixed while following next_cursor until it is null, then remember the newest updated_at for the next run. A change with listed: false removes a domain.

    import os, requests
    BASE = "https://ugapgzpffrykyglevlhz.supabase.co/functions/v1/threat-api"
    H = {"X-API-Key": os.environ["FP_KEY"]}
    
    def sync(since, blocklist):
        cursor, newest = "", since
        while True:
            r = requests.get(BASE + "/v1/domains", headers=H, timeout=30,
                params={"since": since, "cursor": cursor, "limit": 1000})
            r.raise_for_status()
            page = r.json()
            for c in page["changes"]:
                if c.get("listed") and c.get("status") == "active":
                    blocklist.add(c["domain"])
                else:
                    blocklist.discard(c["domain"])
                newest = max(newest, c.get("updated_at", newest))
            cursor = page["next_cursor"]
            if not cursor:
                return newest  # store and pass as since next time
    Attribution: free use requires showing Data: Fraudpol where the data is presented to people.