Skip to main content
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.
Prerequisites: You need a Risk API key and familiarity with the Address Risk Score, Sanctions & Blacklist Check, and Payment Risk Assessment endpoints.

Choosing the Right Approach

Range’s risk endpoints are layered - each higher-level endpoint includes the checks of the ones below it:
EndpointWhat It IncludesBest For
Sanctions & Blacklist CheckOFAC sanctions, stablecoin issuer blacklistsFast pass/fail sanctions screening
Address Risk ScoreEverything in Sanctions + proximity analysis + ML-based threat detectionScreening a single address
Payment Risk AssessmentEverything in Address Risk + new wallet detection, dormant wallet detection, address poisoning, interaction history, token risk, cross-chain analysisAssessing 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: 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 LevelRisk FactorsAction
Anymalicious_address_sender or malicious_address_recipient flagged HIGHREJECT - Address flagged as malicious
HighMultiple HIGH factorsREJECT - High risk on multiple signals
HighSingle 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

ScoreRisk LevelAction
8–10Critical / Extremely highREJECT - Directly or closely connected to malicious activity
6–7HighFLAG or REJECT - Depends on risk tolerance
4–5MediumFLAG - Manual review recommended
1–3Low / Very lowALLOW

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 CaseReject AtFlag AtRationale
Consumer walletScore ≥ 8Score ≥ 6Protect retail users, fewer false positives
Exchange complianceScore ≥ 6Score ≥ 3Regulatory obligation, tighter screening
Institutional treasuryScore ≥ 4Score ≥ 2Maximum caution for large value flows
DeFi protocolScore ≥ 8Score ≥ 6Balance 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

Last modified on March 2, 2026