GHOSTNET
API Reference

Threat Intelligence API

https://ghostnet.thedihmaster.lol v2.5.0

The blocklist behind 2,132 firewalled addresses, plus the client fingerprints behind them, available as a feed. Every call carries your X-API-Key and a fresh proof of work, so a stolen key is worth far less than a stolen password.

Authentication

Send your key as the X-API-Key header on every request. Keys are issued per account with a monthly quota — create one in the developer console.

Open developer console →

Start here — public summary

The network summary needs no key at all. It is the quickest way to confirm you can reach the API.

Shell
curl -s https://ghostnet.thedihmaster.lol/api/summary

The proof-of-work gate

Every feed request is a two-step exchange. It costs a fraction of a second of real CPU work and it cannot be replayed — each challenge is single-use and burned the instant it is spent.

1

Ask

GET /api/pow-challenge with your key returns an identifier, a random challenge_token and a difficulty.

2

Solve

Find any nonce where sha256(identifier + challenge_token + nonce) begins with difficulty zero hex characters.

3

Spend

Send the token and nonce as X-PoW-Challenge and X-PoW-Nonce on the feed request, within 300 seconds.

Challenge response

{
  "identifier": "[email protected]",
  "challenge_token": "a1b2c3d4e5f6...",
  "difficulty": 4,
  "algorithm": "sha256",
  "expires_in_seconds": 300
}
Shell — step 1
curl -s -H "X-API-Key: $KEY" \
  https://ghostnet.thedihmaster.lol/api/pow-challenge

Endpoints

GET/api/summary

Public totals, severity mix and top source countries.

No key required
GET/api/pow-challenge

Issue a single-use proof-of-work challenge, valid 300s.

Key required
GET/api/threats/blocklist

The live feed of firewalled source addresses, masked to /24.

Key + PoW
GET/api/threats/fingerprints

Client signatures behind the IPs. ?family= ja3, ja4, http, ssh, rdp, generic.

Key + PoW

Full example — cURL

Fetches a challenge, solves it in the shell, and spends it on the blocklist. Needs jq and sha256sum.

Bash
#!/usr/bin/env bash
set -euo pipefail
KEY="gk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"   # from your developer console
BASE="https://ghostnet.thedihmaster.lol"

# 1 - fetch a single-use challenge (needs jq)
CH=$(curl -s -H "X-API-Key: $KEY" "$BASE/api/pow-challenge")
ID=$(jq -r .identifier       <<<"$CH")
TOKEN=$(jq -r .challenge_token <<<"$CH")
DIFF=$(jq -r .difficulty      <<<"$CH")
PREFIX=$(printf '0%.0s' $(seq 1 "$DIFF"))

# 2 - solve: sha256(identifier + challenge_token + nonce) must start with $PREFIX
n=0
while :; do
  H=$(printf '%s%s%s' "$ID" "$TOKEN" "$n" | sha256sum)
  [ "${H:0:$DIFF}" = "$PREFIX" ] && break
  n=$((n+1))
done

# 3 - spend the solved challenge on the feed
curl -s -H "X-API-Key: $KEY" \
     -H "X-PoW-Challenge: $TOKEN" \
     -H "X-PoW-Nonce: $n" \
     "$BASE/api/threats/blocklist" | jq .

Full example — Python

A reusable call() that handles the whole challenge → solve → spend cycle. Needs requests.

Python
#!/usr/bin/env python3
"""GhostNet Threat Intelligence API - reference client."""
import itertools
import hashlib
import requests

API_KEY = "gk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"   # from your developer console
BASE = "https://ghostnet.thedihmaster.lol"


def solve(identifier, token, difficulty):
    """Find a nonce whose sha256(identifier+token+nonce) starts with N zeros."""
    target = "0" * difficulty
    for n in itertools.count():
        nonce = str(n)
        digest = hashlib.sha256(f"{identifier}{token}{nonce}".encode()).hexdigest()
        if digest.startswith(target):
            return nonce


def call(path, params=None):
    # 1 - every request needs its own fresh, single-use challenge
    ch = requests.get(f"{BASE}/api/pow-challenge",
                      headers={"X-API-Key": API_KEY}, timeout=10).json()
    # 2 - solve it locally (difficulty 4 is a few hundred ms)
    nonce = solve(ch["identifier"], ch["challenge_token"], ch["difficulty"])
    # 3 - spend the solved challenge on the feed
    resp = requests.get(f"{BASE}{path}", params=params, timeout=10, headers={
        "X-API-Key": API_KEY,
        "X-PoW-Challenge": ch["challenge_token"],
        "X-PoW-Nonce": nonce,
    })
    resp.raise_for_status()
    return resp.json()


if __name__ == "__main__":
    blocklist = call("/api/threats/blocklist")
    print(f"blocklist entries: {len(blocklist)}")

    ja3 = call("/api/threats/fingerprints", {"family": "ja3"})
    print(f"ja3 signatures: {ja3['count']}")

Polling efficiently

The feed is large and changes gradually, so two optional, fully backward-compatible mechanisms let a steady poller pull only what it needs.

A

Conditional GET

Keep the ETag from your last response and send it back as If-None-Match. If nothing changed you get 304 Not Modified — no body, no proof of work, and no quota spent.

B

Delta with ?since=

Pass ?since= an ISO 8601 timestamp or epoch seconds to receive only records newer than your cursor, instead of the whole feed.

Shell
# only download the feed if it changed (304 = unchanged, costs no quota)
curl -s -H "X-API-Key: $KEY" -H "If-None-Match: $ETAG" \
  https://ghostnet.thedihmaster.lol/api/threats/blocklist

# or pull only what is new since a timestamp (ISO 8601 or epoch seconds)
curl -s -H "X-API-Key: $KEY" \
  "https://ghostnet.thedihmaster.lol/api/threats/blocklist?since=2026-08-20T00:00:00"