This guide walks through building an automated compliance screening pipeline using Range’s Risk API. You’ll learn how to choose the right endpoint for your use case and build a multi-layered screening flow.
Choosing the Right Approach
Range’s risk endpoints are layered - each higher-level endpoint includes the checks of the ones below it:
| Endpoint | What It Includes | Best For |
|---|
| Sanctions & Blacklist Check | OFAC sanctions, stablecoin issuer blacklists | Fast pass/fail sanctions screening |
| Address Risk Score | Everything in Sanctions + proximity analysis + ML-based threat detection | Screening a single address |
| Payment Risk Assessment | Everything in Address Risk + new wallet detection, dormant wallet detection, address poisoning, interaction history, token risk, cross-chain analysis | Assessing a payment between two addresses |
You don’t need to call all three endpoints sequentially - each higher-level endpoint already includes the checks from the ones below it. Choose based on your use case and how much context you have.
Architecture Overview
A compliance pipeline typically uses one of two patterns:
Pattern A: Payment Flow (Recommended for Transactions)
When you have both sender and recipient, use Payment Risk Assessment as the primary check. It includes address risk scoring and sanctions/attribution data for both addresses, plus payment-specific analysis.
Sender + Recipient + Amount
│
▼
┌───────────────────────────┐
│ Payment Risk Assessment │──── High risk? → FLAG / REJECT
│ (8 dimensions including │ Unknown? → FLAG for review
│ address risk + sanctions│
│ for both addresses) │
└───────────────────────────┘
│ Low/Medium
▼
ALLOW
Pattern B: Address Screening (No Payment Context)
When screening a single address (e.g., during onboarding, before allowing deposits), use Address Risk Score - which already includes sanctions and blacklist data.
Inbound Address
│
▼
┌─────────────────────┐
│ Address Risk Score │──── Score ≥ threshold? → FLAG / REJECT
│ (Includes sanctions│
│ + ML + proximity) │
└─────────────────────┘
│ Acceptable
▼
ALLOW
When to Use the Standalone Sanctions Endpoint
The Sanctions & Blacklist Check endpoint is still useful when you need:
- Explicit OFAC compliance documentation - a dedicated sanctions check provides a clear audit record specifically for sanctions screening
- High-volume, low-latency pre-filtering - the sanctions endpoint is the fastest and cheapest check, useful as a first gate before more expensive analysis
- Detailed blacklist event data - with
include_details=true, it returns specific blacklist/unblacklist events from stablecoin issuers that aren’t available through the other endpoints
Screening a Payment
When you have sender, recipient, and amount, the Payment Risk Assessment is the single endpoint you need. It analyzes both addresses across 8 risk dimensions - including malicious connection analysis and attributed address checks (which cover sanctions and blacklist data).
curl -G https://api.range.org/v1/risk/payment \
--data-urlencode "sender_address=SENDER_ADDRESS" \
--data-urlencode "recipient_address=RECIPIENT_ADDRESS" \
--data-urlencode "amount=1000" \
--data-urlencode "sender_network=solana" \
--data-urlencode "recipient_network=solana" \
-H "Authorization: Bearer your_api_key_here"
async function checkPaymentRisk(sender, recipient, amount, network) {
const params = new URLSearchParams({
sender_address: sender,
recipient_address: recipient,
amount: amount.toString(),
sender_network: network,
recipient_network: network,
});
const response = await fetch(
`https://api.range.org/v1/risk/payment?${params}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
return response.json();
}
Decision Matrix
| Payment Risk Level | Risk Factors | Action |
|---|
| Any | malicious_address_sender or malicious_address_recipient flagged HIGH | REJECT - Address flagged as malicious |
| High | Multiple HIGH factors | REJECT - High risk on multiple signals |
| High | Single HIGH factor (e.g., first_interaction) | FLAG - Manual review required |
| Medium | - | FLAG - Elevated risk, needs review |
| Low | - | ALLOW - Low risk |
| Unknown | - | FLAG - Insufficient data, treat with caution |
These thresholds are recommendations. Calibrate them based on your risk appetite, regulatory requirements, and use case. See the calibration guidance below.
Screening a Single Address
When you don’t have payment context (e.g., during onboarding or deposit screening), use Address Risk Score. It includes sanctions and blacklist data along with proximity analysis and ML-based threat detection.
curl -G https://api.range.org/v1/risk/address \
--data-urlencode "address=COUNTERPARTY_ADDRESS" \
--data-urlencode "network=solana" \
-H "Authorization: Bearer your_api_key_here"
async function checkAddressRisk(address, network) {
const params = new URLSearchParams({ address, network });
const response = await fetch(
`https://api.range.org/v1/risk/address?${params}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
return response.json();
}
Decision Thresholds
| Score | Risk Level | Action |
|---|
| 8–10 | Critical / Extremely high | REJECT - Directly or closely connected to malicious activity |
| 6–7 | High | FLAG or REJECT - Depends on risk tolerance |
| 4–5 | Medium | FLAG - Manual review recommended |
| 1–3 | Low / Very low | ALLOW |
Adding a Dedicated Sanctions Check
For compliance teams that need an explicit, documented sanctions screening step - separate from the broader risk analysis - add a standalone Sanctions & Blacklist Check as a fast pre-filter:
async function checkSanctions(address) {
const response = await fetch(
`https://api.range.org/v1/risk/sanctions/${address}?include_details=false`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const data = await response.json();
return {
blocked: data.is_ofac_sanctioned || data.is_token_blacklisted,
ofac: data.is_ofac_sanctioned,
blacklisted: data.is_token_blacklisted,
};
}
This is optional since Address Risk Score and Payment Risk Assessment already include sanctions data, but useful for:
- Creating a separate audit record specifically for sanctions compliance
- Fast pre-filtering before more expensive checks
- Getting detailed blacklist event history with
include_details=true
Log and Audit
For compliance, store the raw API responses alongside your decision. This creates an audit trail showing what data was available at the time of the decision.
async function logComplianceDecision(transaction, checks, decision) {
const record = {
timestamp: new Date().toISOString(),
transaction_id: transaction.id,
counterparty: transaction.counterparty,
checks: {
payment_risk: checks.paymentRisk, // or address_risk for single-address screening
sanctions: checks.sanctions || null, // if standalone sanctions check was used
},
decision: decision, // "allow", "flag", "reject"
reasoning: decision.reasoning,
};
// Store in your compliance database
await complianceDB.insert(record);
}
Key fields to retain:
- Timestamp of each check
- Raw API responses (scores, risk levels, reasoning, individual risk factors)
- Decision and the logic that produced it
- Operator overrides if a flagged transaction was manually approved or rejected
Threshold Calibration
Different use cases warrant different risk tolerances. These thresholds apply to Address Risk Score (riskScore 1–10):
| Use Case | Reject At | Flag At | Rationale |
|---|
| Consumer wallet | Score ≥ 8 | Score ≥ 6 | Protect retail users, fewer false positives |
| Exchange compliance | Score ≥ 6 | Score ≥ 3 | Regulatory obligation, tighter screening |
| Institutional treasury | Score ≥ 4 | Score ≥ 2 | Maximum caution for large value flows |
| DeFi protocol | Score ≥ 8 | Score ≥ 6 | Balance usability with risk mitigation |
Full Pipeline Examples
Payment Flow
async function screenPayment(sender, recipient, amount, network) {
// Payment Risk Assessment - comprehensive check for both addresses
const paymentRisk = await checkPaymentRisk(sender, recipient, amount, network);
// Check for malicious address flags in risk factors
const maliciousFlags = paymentRisk.risk_factors?.filter(
(f) => f.factor.startsWith("malicious_address") && f.risk_level === "high"
);
if (maliciousFlags?.length > 0) {
return { decision: "reject", reasoning: "Address flagged as malicious" };
}
if (paymentRisk.overall_risk_level === "high") {
return { decision: "flag", reasoning: "High payment risk - manual review required" };
}
if (paymentRisk.overall_risk_level === "unknown") {
return { decision: "flag", reasoning: "Insufficient data - proceed with caution" };
}
return { decision: "allow", reasoning: "Payment risk assessment passed" };
}
Address Screening with Optional Sanctions Audit
async function screenAddress(address, network, requireSanctionsAudit = false) {
// Optional: Dedicated sanctions check for audit trail
if (requireSanctionsAudit) {
const sanctions = await checkSanctions(address);
if (sanctions.blocked) {
return {
decision: "reject",
reasoning: `Address is ${sanctions.ofac ? "OFAC sanctioned" : "token blacklisted"}`,
};
}
}
// Address Risk Score - includes sanctions + proximity + ML analysis
const addressRisk = await checkAddressRisk(address, network);
if (addressRisk.riskScore >= 8) {
return {
decision: "reject",
reasoning: `Risk score ${addressRisk.riskScore}/10: ${addressRisk.reasoning}`,
};
}
if (addressRisk.riskScore >= 6) {
return { decision: "flag", reasoning: "Elevated risk - manual review required" };
}
return { decision: "allow", reasoning: "Address screening passed" };
}
What’s Next