Kunch API · v1 · Documentation

The blacklist,
as a building block.

Everything published on the Kunch blacklist is available programmatically: single lookups, bulk screening, categories and reports. REST, JSON, boring on purpose. The same data our researchers maintain — no scraped copies, no third-party mirrors.

All systems operational Base URL https://api.kunch.shop/v1

Overview

The Kunch API is a REST API served over HTTPS with JSON request and response bodies. It exposes the research database behind the Kunch blacklist: documented scam operations, their domains, current status, and the sources we verified them against.

Three ideas shape the whole API. First, read-only by default — one endpoint writes anything, and that's the report endpoint. Second, sources travel with the data — every record includes the links that establish it, so your users can check our work the same way you can. Third, no surprises — stable URLs, versioned from day one, and deprecations announced at least six months ahead in the changelog.

Who uses this? Exchanges screening deposit-notes, wallets flagging known phishing domains before a signature, extension builders, and researchers correlating campaign infrastructure. If you're screening user traffic at scale, the paid tiers exist for exactly that.

Authentication

All requests require an API key sent as a bearer token in the Authorization header. Keys come in two kinds: kn_test_ keys return fixture data and never count against production limits; kn_live_ keys hit the real database. Get a free live key by emailing api@kunch.shop — no credit card, no sales call.

# Authenticated request example
curl https://api.kunch.shop/v1/domains/bitconnect.com \
  -H "Authorization: Bearer kn_live_your_key_here"

Keys are scoped per project, and you can rotate them from the dashboard without downtime. If a key leaks, rotate first and ask questions second — we keep the last five keys active per project so cutover is instant.

Quickstart

Screen a domain in three lines. This example checks whether a domain is on the blacklist before your app lets a user interact with it.

# Check a single domain
curl https://api.kunch.shop/v1/domains/bitconnect.com \
  -H "Authorization: Bearer kn_live_your_key_here"
const res = await fetch(
  'https://api.kunch.shop/v1/domains/bitconnect.com',
  { headers: { Authorization: 'Bearer kn_live_your_key_here' } }
);
const record = await res.json();

if (record.listed) {
  console.warn('Blocked:', record.domain, record.status);
}
import requests

res = requests.get(
    "https://api.kunch.shop/v1/domains/bitconnect.com",
    headers={"Authorization": "Bearer kn_live_your_key_here"},
)
record = res.json()

if record["listed"]:
    print("Blocked:", record["domain"], record["status"])

A listed domain returns HTTP 200 with a full case record. An unknown domain returns 404 with an error.type of not_found — that's the difference between "we know it's bad" and "we don't know", and your app should treat those differently.

Rate limits

Every response includes X-RateLimit-Remaining and X-RateLimit-Reset headers. When you hit a limit, you get a 429 with a Retry-After in seconds — back off and retry, and please don't hammer in a loop.

TierRateBulk sizePrice
Free60 req/min25 domains$0
Build600 req/min100 domains$19/mo
Scale6,000 req/min1,000 domains$149/mo
CustomYour numberYour numberTalk to us

GET/v1/domains/{domain}

Returns the full case record for a single domain. Normalise the domain before sending — no scheme, no path, lowercase. Subdomains resolve to their registrable domain, so login.bitconnect.com returns the bitconnect.com record.

Path parameters

NameTypeDescription
domainstringRegistrable domain name, e.g. forsage.io

Response

{
  "domain": "bitconnect.com",
  "listed": true,
  "case_id": "blc-0001",
  "status": "shut_down",
  "categories": ["ponzi", "lending_scheme"],
  "first_seen": "2016-08-01",
  "last_verified": "2026-10-01",
  "summary": "High-yield lending Ponzi (~$2.4B losses). Collapsed Jan 2018; promoter settled with the SEC, founder indicted.",
  "sources": [
    { "label": "SEC Litigation Release 25092", "url": "https://www.sec.gov/news/press-release/2021-176" }
  ]
}

Status is one of active, charged, shut_down or defunct. Treat active as dangerous-now, and the others as dangerous-history — clone domains frequently resurrect dead brands.

GET/v1/domains

Paginated list of every record in the database. Cursor-based pagination: pass back the next_cursor value to get the next page. Results are sorted by last_verified, descending.

Query parameters

NameTypeDescription
categorystringFilter by category, e.g. ponzi
statusstringFilter by status: active | charged | shut_down | defunct
qstringFull-text search across domains and summaries
limitinteger1–100, default 25
cursorstringOpaque cursor from a previous response

Example

# All currently-active phishing domains, newest verifications first
curl "https://api.kunch.shop/v1/domains?category=phishing&status=active&limit=50" \
  -H "Authorization: Bearer kn_live_your_key_here"

POST/v1/domains/bulk

Check up to 100 domains in one call on the Build tier (25 on Free). Deduplicated server-side; results are returned in the same order as the input. Duplicate domains count once toward the limit.

Request

curl -X POST https://api.kunch.shop/v1/domains/bulk \
  -H "Authorization: Bearer kn_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "domains": ["forsage.io", "onecoin.eu", "example-wallet-store.net"] }'

Response

{
  "results": [
    { "domain": "forsage.io", "listed": true,  "case_id": "fsg-0004", "status": "charged" },
    { "domain": "onecoin.eu", "listed": true,  "case_id": "onc-0002", "status": "defunct" },
    { "domain": "example-wallet-store.net", "listed": false }
  ],
  "checked": 3,
  "listed_count": 2
}

Not listed ≠ safe. The blacklist documents operations with two or more independent sources. A brand-new phishing domain created this morning won't be there yet — pair the API with your own heuristics, and report what you find so the next user is covered.

GET/v1/categories

The controlled vocabulary used for record categories. Use it to power filter UIs without hardcoding strings that may grow over time.

{
  "categories": [
    { "id": "ponzi",        "label": "Ponzi & pyramid schemes" },
    { "id": "rug_pull",     "label": "Rug pulls" },
    // 8 more in production: fakebot, phishing, wallet_clone,
    // giveaway, fake_recovery, address_poisoning, fake_ico, romance
  ]
}

POST/v1/reports

Submit a suspected scam for human review. This is the same pipeline as the public report form — every report is read by a researcher, and you'll receive a decision either way. Reports with evidence (transaction hashes, regulator citations, archived pages) get reviewed first.

Request

curl -X POST https://api.kunch.shop/v1/reports \
  -H "Authorization: Bearer kn_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example-ledger-live-download.net",
    "category_guess": "phishing",
    "description": "Cloned Ledger Live download page, seeded via SMS",
    "evidence": ["tx hash or archive link here"]
  }'

Response

{
  "report_id": "rep_01J9F3...",
  "state": "received",
  "review_eta": "5 business days"
}

GET/v1/meta

Database statistics and dataset version. Poll it (gently) to know when your local cache should refresh — the version string changes whenever any record is published, corrected or retracted.

{
  "version": "2026.10.14",
  "records": 9,
  "by_status": { "active": 2, "charged": 2, "shut_down": 3, "defunct": 2 },
  "last_publish": "2026-10-14T09:30:00Z"
}

Errors

Errors use conventional HTTP status codes and a consistent JSON body. The request_id is gold when contacting support — include it.

{
  "error": {
    "type": "rate_limited",
    "code": 429,
    "message": "60 requests per minute exceeded. Retry after 12 seconds.",
    "request_id": "req_01J9F5..."
  }
}
StatusTypeMeaning
400invalid_requestMalformed domain, bad JSON, or an invalid parameter value
401unauthorizedMissing or malformed Authorization header
403forbiddenKey valid but tier doesn't permit this endpoint or bulk size
404not_foundDomain not in the database — not a listing verdict
429rate_limitedOver tier rate; honor Retry-After
500internalOur fault. Retry with backoff; page us via status page

Changelog

DateChange
2026-10-14Bulk endpoint limit raised to 100 on Build tier. Added related_domains to case records.
2026-06-02Reports API opened on all tiers. Reviews ETA added to response.
2026-02-09v1 general availability. Free tier set at 60 req/min.

Breaking changes will appear here at least six months before enforcement, and the affected fields keep working until the announced date. That's the whole policy.

Support

API questions go to api@kunch.shop — include a request_id and we can usually answer the same day. Data disputes (a record looks wrong, or you have evidence for a new one) go to reports@kunch.shop. Uptime and incident history are published on the status page, and the free tier is never throttled below 60 req/min — it's in the changelog when that changes, and it hasn't.

Building something with the API? Our field guide on spotting fake crypto websites covers the heuristics that pair well with a blacklist lookup, and the blacklist page documents what each status value means in practice.