Free KYT for Crypto Wallets — Check Address Risk Before You Send (JavaScript)
Before a user hits Send, you usually know the destination address. That is the cheapest place to run KYT (Know Your Transaction) / AML screening: if the counterparty looks like a mixer hop, sanctioned cluster, or known scam sink, warn or block.
Most commercial AML/KYT APIs need contracts, keys, and paid credits. You can ship a working pre-send check with a free public endpoint and ~20 lines of JavaScript.
What you will build
A tiny helper that:
- Takes a wallet address + chain (
ETH,BTC, …) - Calls
POST https://intelapi.publicaml.org/v1/enrich - Returns an
aml_score(0–100) plus labels useful for UX
No API key. Rate limit on the free tier is roughly 1k requests/hour — enough for wallets, bots, and demos.
Copy-paste example
/**
* Free KYT / AML address enrichment (no API key).
* Docs / product: https://publicaml.org/
*/
export async function checkAddressRisk(walletAddress, chain = "ETH") {
const res = await fetch("https://intelapi.publicaml.org/v1/enrich", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
addresses: [{ wallet_address: walletAddress, chain }],
}),
});
if (!res.ok) {
throw new Error(`PublicAML enrich failed: ${res.status}`);
}
const { entities } = await res.json();
const entity = entities?.[0];
if (!entity) throw new Error("Empty enrich response");
return {
address: entity.wallet_address,
chain: entity.chain,
amlScore: entity.aml_score, // 0–100
label: entity.label, // e.g. "Bitfinex"
category: entity.category, // e.g. "cex"
breakdown: entity.aml_score_breakdown,
signals: entity.behavioral_signals,
};
}
export function riskLevel(score) {
if (score >= 70) return "high";
if (score >= 40) return "medium";
return "low";
}
// Example
const risk = await checkAddressRisk(
"0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"ETH"
);
console.log(risk.amlScore, risk.label, riskLevel(risk.amlScore));
How to use the score in a wallet UX
| Score | Suggested UX |
|---|---|
| 0–39 | Allow (optional soft info if category is interesting) |
| 40–69 | Warning modal: show label / top risk categories |
| 70–100 | Hard block or require typed confirmation |
Important nuance: a known CEX deposit address can show infrastructure floors in the breakdown while the surfaced aml_score stays low. Always display label + category next to the number so users understand why.
async function beforeSend(to, chain) {
const r = await checkAddressRisk(to, chain);
const level = riskLevel(r.amlScore);
if (level === "high") {
throw new Error(
`Blocked: high KYT risk (${r.amlScore}). Label: ${r.label ?? "unknown"}`
);
}
if (level === "medium") {
// showConfirm(...) in your UI
console.warn("Medium risk destination", r);
}
return r;
}
KYT vs KYC vs AML (30-second version)
- KYC — who the user is (identity docs).
- AML — policies + monitoring to prevent money laundering.
- KYT — risk of this payment path (address / hops / exposure) right before or after a transfer.
Wallet and DeFi UIs mostly need KYT on the destination (and sometimes the user’s own funding sources). This API is address enrichment — perfect for pre-send gates.
Try it from the shell
curl -sS -X POST 'https://intelapi.publicaml.org/v1/enrich' \
-H 'Content-Type: application/json' \
-d '{"addresses":[{"wallet_address":"0x742d35Cc6634C0532925a3b844Bc454e4438f44e","chain":"ETH"}]}' \
| jq '.entities[0] | {aml_score, label, category, chain}'
Where to go next
- Product + dashboard: https://publicaml.org/
- Telegram bot for manual checks: @publicaml
- Add the same call in Python, Go, or a wagmi
sendTransactionwrapper
Free forever for this use case — if you ship a wallet or OTC desk, there is no excuse not to screen the counterparty before broadcast.
Top comments (0)