One request — no API key
The enrich endpoint takes an address and chain and returns a risk score, category, sanctions status and counterparties. No key is required to start; an optional key only raises the rate limit.
curl -sS -X POST 'https://intelapi.publicaml.org/v1/enrich' \
-H 'Content-Type: application/json' \
-d '{
"addresses": [{ "wallet_address": "0x...", "chain": "ETH" }],
"include": ["aml_score", "category"]
}'{
"entities": [{
"wallet_address": "0x...",
"chain": "ETH",
"aml_score": 87,
"category": "hack",
"sanctioned": false
}]
}The same check in your language
import requests
def aml_check(address, chain="ETH"):
r = requests.post(
"https://intelapi.publicaml.org/v1/enrich",
json={
"addresses": [{"wallet_address": address, "chain": chain}],
"include": ["aml_score", "category"],
},
timeout=10,
)
r.raise_for_status()
return r.json()["entities"][0]
e = aml_check("0x...")
print(e["aml_score"], e["category"], e["sanctioned"])// Node 18+ / browsers — no API key needed
async function amlCheck(address, chain = 'ETH') {
const r = await fetch('https://intelapi.publicaml.org/v1/enrich', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
addresses: [{ wallet_address: address, chain }],
include: ['aml_score', 'category'],
}),
})
if (!r.ok) throw new Error('enrich failed: ' + r.status)
const { entities } = await r.json()
return entities[0]
}
const e = await amlCheck('0x...')
console.log(e.aml_score, e.category, e.sanctioned)package main
import (
"bytes"
"encoding/json"
"net/http"
)
func amlCheck(address, chain string) (map[string]any, error) {
body, _ := json.Marshal(map[string]any{
"addresses": []map[string]string{{"wallet_address": address, "chain": chain}},
"include": []string{"aml_score", "category"},
})
resp, err := http.Post("https://intelapi.publicaml.org/v1/enrich",
"application/json", bytes.NewReader(body))
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out struct{ Entities []map[string]any }
json.NewDecoder(resp.Body).Decode(&out)
return out.Entities[0], nil
}More examples and SDKs on GitHub: github.com/publicaml.
Pattern: block risky addresses before a transaction
The point of a pre-send check is to decline rather than unwind. Screen the recipient before you sign or accept funds and reject anything sanctioned or above your risk threshold.
// Pre-transaction gate: block risky recipients before you send.
async function assertClean(address, chain = 'TRON') {
const e = await amlCheck(address, chain)
if (e.sanctioned || (e.aml_score ?? 0) >= 75) {
throw new Error('High AML risk (' + e.category + ') — transfer blocked')
}
}
await assertClean(recipient) // throws if the address is riskyaml_score is 0–100 (higher = riskier). category is the entity type (exchange, mixer, hack, scam, sanction…). Treat sanctioned: true as an override regardless of score. It is a risk signal, not a legal compliance guarantee — keep your own KYC/AML process alongside it.
Frequently asked questions
Is there a free API to check crypto AML / KYT?
Yes. PublicAML exposes a public enrich endpoint that needs no API key: POST https://intelapi.publicaml.org/v1/enrich with the address and chain, and you get back an aml_score (0–100), category, sanctions status and counterparties. It is a non-profit, so there is no paid tier — an optional key only raises the rate limit.
How do I check a crypto address for AML risk in Python?
POST the address and chain to the enrich endpoint with the requests library and read entities[0].aml_score / category / sanctioned. See the Python example on the page — no key or account is required to start.
Which chains can I screen?
Bitcoin, Ethereum, BNB Chain and TRON. Pass the chain per address ("BTC", "ETH", "BSC", "TRON"). An ERC20 and a BEP20 address look identical, so send the correct chain for the network you are on.
How do I block sanctioned or high-risk addresses before a transaction?
Call the API before you sign or accept funds and reject the transfer when sanctioned is true or aml_score is above your threshold (75 is a common cut-off). The pre-transaction gate example on the page shows the pattern.
Do I need to handle rate limits?
For low volume, no — the public endpoint works without a key. For higher throughput, request an optional key to raise the limit and add simple retry/back-off on HTTP 429.
Start screening — free, no key
Send one request and get a risk score back. No account, no card, no API key.
