SafeON API documentation
Screen any crypto wallet for AML risk with a single HTTP request: a 0–100 risk score, links to sanctioned entities (OFAC), Iranian exchanges, Tether freeze status and source of funds — on TRON, BSC and Ethereum.
https://safeon.irIntroduction
The SafeON public API lets you integrate wallet risk screening into your own system (exchange, wallet, KYC dashboard, bot). The model is simple: you send a wallet address and network, and we return a structured risk report.
- Responses are JSON and language-neutral (English keys).
- Authentication with an API key via the
Authorizationheader. - Each successful check deducts one unit from your account quota (the same quota as your dashboard).
- HTTPS only.
Authentication
To create a key, sign in to your dashboard, go to "API & Developers" and create a "New key". The key is shown only once, at the moment it is created; store it somewhere safe (we keep only its hash, so it cannot be recovered).
Send the key with every request in the Authorization header as a Bearer token:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Quota and rate limits
Every successful call to /api/v1/screen consumes one unit of your plan quota, exactly like a check in the app. Once your quota runs out, you get a 402 quota_exceeded response so you can upgrade your plan.
Each key is rate-limited: by default 30 requests per minute. Beyond that, a 429 rate_limited response is returned with a Retry-After header (seconds until the next attempt). Respect this header on your side and queue or throttle your requests.
The body of every successful response also includes your live quota status (quota.remaining) so you can monitor usage.
Wallet risk screening
Screens a single address and returns a risk report.
Request body (JSON)
| Field | Type | Required | Notes |
|---|---|---|---|
| address | string | Required | Wallet address. TRON (T…) or EVM (0x…) format. |
| network | string | Optional | One of tron · bsc · ethereum · auto. For 0x… addresses, if you omit it or pass auto, the active network is detected automatically from on-chain activity. For a T… address it is always TRON. |
// Example body { "address": "0x053A3068E0849b4fccfD73967F057C11F22D61FA", "network": "auto" }
Response structure (200 OK)
| Field | Type | Notes |
|---|---|---|
| address | string | The screened address. |
| network | string | The network the analysis was run on (tron/bsc/ethereum). |
| risk_score | integer | Risk score from 0 (safe) to 100 (critical). |
| risk_level | string | Risk level: low · medium · high · severe (and unknown when data is insufficient). |
| verdict | string | Suggested verdict: approve · review (manual review) · reject · unknown. |
| flags | object | Boolean flags: self_frozen (the address itself is frozen), freeze_imminent (at risk of a freeze), freeze_recent, insufficient_data. |
| flow | object | Flow summary: number of counterparties, inflow/outflow and USD value, and data coverage (full/sampled). |
| direct_exposure | array | Direct high-risk exposure (address, category, direction, transaction count, USD value). |
| indirect_exposure | array | Indirect (multi-hop) exposure to high-risk or frozen entities. |
| evidence | array | List of scoring evidence (severity, points, text). |
| quota | object | Quota status after this call: used · limit · remaining. |
| generated_at | integer | Response generation time (Unix epoch, seconds). |
// Example response (abridged) { "address": "0x053A3068E0849b4fccfD73967F057C11F22D61FA", "network": "ethereum", "risk_score": 48, "risk_level": "medium", "verdict": "review", "flags": { "self_frozen": false, "freeze_imminent": false, "freeze_recent": false, "insufficient_data": false }, "flow": { "total_counterparties": 1, "inflow_count": 1, "coverage": "full" }, "direct_exposure": [ { "category": "sanctioned", "label": "nobitex.ir", "direction": "in", "value_usd": 1000 } ], "indirect_exposure": [], "evidence": [ { "severity": "high", "points": 40, "text": "..." } ], "quota": { "used": 12, "limit": 100, "remaining": 88 }, "generated_at": 1783852000 }
Supported networks
tron bsc ethereum auto
EVM addresses (0x…) are identical on Ethereum and BSC; if you omit network or set it to auto, SafeON measures on-chain activity and picks the active chain. For full control, pass network explicitly.
Errors
Every error is returned with an HTTP status code and a { "error": "...", "message": "..." } body.
| Code | error | Meaning |
|---|---|---|
| 401 | missing_api_key | Missing Authorization header. |
| 401 | invalid_api_key | Invalid or revoked key. |
| 422 | invalid_address | Invalid address format. |
| 422 | invalid_network | Invalid network value. |
| 402 | quota_exceeded | Quota exhausted — upgrade your plan. |
| 429 | rate_limited | Rate limit exceeded; respect the Retry-After header. |
| 502 | screen_failed | Temporary error fetching chain data; please try again. |
Code examples
cURL
curl -X POST https://safeon.ir/api/v1/screen \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"address":"0x053A3068E0849b4fccfD73967F057C11F22D61FA","network":"auto"}'
Python
import requests
resp = requests.post(
"https://safeon.ir/api/v1/screen",
headers={"Authorization": "Bearer sk_live_xxxx"},
json={"address": "T...", "network": "tron"},
timeout=60,
)
data = resp.json()
print(data["risk_score"], data["risk_level"])
JavaScript (Node)
const res = await fetch("https://safeon.ir/api/v1/screen", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_xxxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ address: "0x...", network: "auto" }),
});
const data = await res.json();
console.log(data.risk_score, data.risk_level);