v1 · Public · Free

Ozone Screening API

Batch-screen addresses on every THORChain chain. Each verdict names its sources, provenance and — for traces — the THORChain transactions, and every answer is signed with Ozone's Ed25519 key. Nodes can instead download the signed snapshot and screen locally. No auth, nothing about the request is stored.

EndpointWhat it does
POST /api/v1/screenBatch screening (≤ 100). Body {"addresses": ["…" | {"address","chain"}], "policy": {"flagAt": "high"}}. Signed ozone.screen.v1 answer.
GET /api/v1/screen?address=&chain=Same for one address.
GET /api/screen?address=Original endpoint, kept compatible: flagged + matches[].source, plus the signed v1 answer as "attestation".
GET /api/v1/address/<address>Everything about one address: verdict, every listing with provenance (history included), THORChain flows received and sent, and the small transfers (each under $50) that count toward a TRACE_SMALL_TRANSFERS total.
GET /api/v1/snapshotNewest signed snapshot manifest (version, build time, SHA-256 of the payload).
GET /api/v1/snapshot/<version>Snapshot payload (gzip JSON of every listed and traced address with its reasons).
GET /api/v1/keysPublic keys that sign snapshots and answers — pin them.
GET /api/healthDatabase, snapshot age, per-source sync state, real-time trace cursor.
POST /api/v1/submissionsReport an address or appeal a flag ({kind, address, chain?, message, evidence?, contact?}).
GET /api/flaggedThe flagged counters: all flagged addresses, flagged thor1 addresses, monitored thor1 accounts flagged (+ breakdown).
GET /api/flagged?list=all|thor|usersEach list (JSON, paged with offset/limit ≤ 1000; filter q, source, chain; format=csv for all rows).

Chains: THOR, BTC, ETH, BSC, BASE, AVAX, GAIA, LTC, BCH, DOGE, TRON, XRP, SOL (+ ZEC, XMR, DASH, BSV, BTG, ETC, ARB, BNB for listed addresses). Without a chain hint every valid reading of the address is checked. Risk levels and reasons: methodology. Node integration: INTEGRATION.md in the repository.

Flagged (traced) verdict
{
  "type": "ozone.screen.v1",
  "id": "5f0c…",
  "issuedAt": "2026-09-27T10:00:00.000Z",
  "policy": { "flagAt": "high" },
  "snapshot": { "version": 1790500000, "builtAt": "…", "sha256": "…" },
  "results": [{
    "input": "19qs8FPxK6VfkqHL7TPZDhw3r2rJPU6yhE",
    "keys": ["btc:19qs8FPxK6VfkqHL7TPZDhw3r2rJPU6yhE"],
    "valid": true,
    "status": "flagged",
    "risk": "high",
    "reasons": [{
      "code": "TRACE_SWAP",
      "source": "thorchain_trace",
      "category": "traced",
      "risk": "high",
      "text": "Received 1.2133 BTC.BTC (~$111,000) from 0xb21e…69f4, listed by Bybit Exploiter 65 (ethlabels) via THORChain swap 6F708AFC… on 2025-02-25",
      "ref": "https://runescan.io/tx/6F708AFC…",
      "trace": { "hop": 1, "action": "swap", "txid": "6F708AFC…", "from": "0xb21e…69f4", "originSource": "ethlabels" }
    }],
    "reference": "oz:v1790500000:flagged:TRACE_SWAP"
  }],
  "signature": { "alg": "ed25519", "keyId": "oz…", "sig": "…" }
}
Clean verdict
{
  "input": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
  "keys": ["btc:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
           "bch:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy",
           "bsv:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"],
  "valid": true,
  "status": "clean",
  "risk": "none",
  "reasons": [],
  "snapshot": { "version": 1790500000, "ageSeconds": 212,
                "stale": false, "sha256": "…" }
}

Integration Examples

cURL
curl -s https://ozone.redacted.gg/api/v1/screen \
  -H 'content-type: application/json' \
  -d '{"addresses":["0x098B716B8Aaf21512996dC57EB0615e2383E2f96",
                   {"address":"qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a","chain":"BCH"}],
       "policy":{"flagAt":"high"}}'
JavaScript (verified)
// Online, with signature verification
import { verifyScreenResponse } from '@redacted/ozone-client';

const res = await fetch('https://ozone.redacted.gg/api/v1/screen', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ addresses: [address] })
});
const body = verifyScreenResponse(await res.json(), [OZONE_API_KEY]);
if (!body) throw new Error('unsigned or forged answer');
const [verdict] = body.results;          // status, risk, reasons, snapshot
Node-local
// Node-local: no request to Ozone at screening time
import { OzoneClient } from '@redacted/ozone-client';

const oz = new OzoneClient({
  trustedKeys: [OZONE_SNAPSHOT_KEY],     // pin it (see /api/v1/keys)
  cacheDir: '/var/lib/node/ozone',        // survives restarts and outages
});
await oz.init();                          // verified cache, then refresh
oz.start();                               // refresh every 10 min
const v = oz.screen(address, 'BTC');      // → status, risk, reasons,
                                          //   snapshot.version / ageSeconds

Try it live

POSTs to /api/v1/screen and shows the signed answer.

GET /api/flagged

Two separate numbers, each with its list: all flagged addresses (every key in the newest signed snapshot at risk ≥ high, any chain; ?list=all) and the flagged thor1 addresses among them (?list=thor). Rows carry only what the snapshot publishes: address, chain, risk, reason codes, sources, incident. ?list=users is the separate count of monitored thor1 accounts that are flagged themselves or through a linked L1 address (formerly "Flagged THORChain Users"; the legacy fields totalFlagged, flaggedThorUsers and flaggedAddresses of the plain call keep that meaning).

Data Sources

OFAC SDN
US Treasury, all tickers
UK Sanctions
FCDO list
EU Sanctions
Consolidated list
Sanctions oracle
On-chain, incl. delistings
FBI / IC3
DPRK attributions
Tether & Circle
Issuer freezes
Hack clusters
Bybit + curated
Exploiter labels
eth-labels
ScamSniffer
Phishing / drainers
THORChain tracing
Flows from listed addresses
Same-key twins
TRON↔EVM, BTC/BCH/LTC/DOGE
Maintainer flags
With written reasons