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.
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.
| Tier | Rate | Bulk size | Price |
|---|---|---|---|
| Free | 60 req/min | 25 domains | $0 |
| Build | 600 req/min | 100 domains | $19/mo |
| Scale | 6,000 req/min | 1,000 domains | $149/mo |
| Custom | Your number | Your number | Talk 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
| Name | Type | Description |
|---|---|---|
| domain | string | Registrable 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
| Name | Type | Description |
|---|---|---|
| category | string | Filter by category, e.g. ponzi |
| status | string | Filter by status: active | charged | shut_down | defunct |
| q | string | Full-text search across domains and summaries |
| limit | integer | 1–100, default 25 |
| cursor | string | Opaque 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..."
}
}| Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed domain, bad JSON, or an invalid parameter value |
| 401 | unauthorized | Missing or malformed Authorization header |
| 403 | forbidden | Key valid but tier doesn't permit this endpoint or bulk size |
| 404 | not_found | Domain not in the database — not a listing verdict |
| 429 | rate_limited | Over tier rate; honor Retry-After |
| 500 | internal | Our fault. Retry with backoff; page us via status page |
Changelog
| Date | Change |
|---|---|
| 2026-10-14 | Bulk endpoint limit raised to 100 on Build tier. Added related_domains to case records. |
| 2026-06-02 | Reports API opened on all tiers. Reviews ETA added to response. |
| 2026-02-09 | v1 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.