Quickstart

A verdict in your terminal in four steps. Everything else is on the pages after this one.

1. Get a key

Mint one from your dashboard. It starts with zsk_ and is shown once.

Shell
export ZSCREEN_KEY=zsk_...

2. Screen an address

Paste this. It runs as written.

Request
curl -s https://zscreenapi.zeruai.org/v2/screen \
  -H "Authorization: Bearer $ZSCREEN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"address":"0xd08a234c5215a8122c836187b23f60046a90afd8"}'

GET /v2/screen/{address} does the same thing without a body.

3. Read the answer

A 200 looks like this. Trimmed to what you act on.

200 OK
{
  "address": "0xd08a234c5215a8122c836187b23f60046a90afd8",
  "verdict": "REVIEW",
  "risk_score": 87.4,
  "summary": "Needs a human look. Not on any list, and no contact with a listed address or a mixer. The model scored it 87.4 out of 100.",
  "decided_by": {
    "rule": "model",
    "source": "model",
    "reason": "Not on any list, and no contact with a listed address or a mixer. The model scored it 87.4 out of 100."
  },
  "checks": [
    { "name": "Sanctions list",              "status": "clear", "detail": "No match" },
    { "name": "Known hack and scam wallets", "status": "clear", "detail": "No match" },
    { "name": "Mixers",                      "status": "clear", "detail": "No contact" },
    { "name": "Behaviour",                   "status": "hit",   "detail": "Unusual overall" }
  ],
  "why": [
    { "kind": "decided", "reason": "Not on any list, and no contact with a listed address or a mixer. The model scored it 87.4 out of 100." },
    { "kind": "model", "reason": "Money moved straight back out within a day.", "pushed": "up" }
  ],
  "confidence_label": "HIGH",
  "screening_id": 418822,
  "scored_at": "2026-09-16T09:14:22Z",
  "served_from": "cache"
}

Four fields carry the decision:

  • verdict is CLEAR, REVIEW or FLAGGED. Branch on this.
  • risk_score is 0 to 100 and always agrees with the verdict. A list or contact hit is 100.
  • checks is every check that ran, including the ones that came back clean. Show it to an analyst.
  • why starts with what decided the verdict. The rest is what stood out.

The full body carries more: measured behaviours, caveats, confidence and the model record. The response lists every field.

4. Handle a 202

A wallet we have never seen has to be fetched from the chain first. You get a 202 and a job.

202 Accepted
{
  "address": "0x1f39eb3896756172728a2cfb116a6ec37d807125",
  "status": "queued",
  "job_id": 90114,
  "status_url": "/v2/job/90114",
  "jobs_ahead": 3,
  "detail": "This wallet has no history on file, so it is being fetched from the chain. Poll status_url. A wallet is NOT low risk because it has not been assessed yet."
}

Poll the job until state is done.

Poll
curl -s https://zscreenapi.zeruai.org/v2/job/90114 \
  -H "Authorization: Bearer $ZSCREEN_KEY"
200 OK
{
  "job_id": 90114,
  "address": "0x1f39eb3896756172728a2cfb116a6ec37d807125",
  "state": "done",
  "result": { "verdict": "CLEAR", "risk_score": 4.1, "...": "the same body as above" }
}

Most addresses come back in under a minute. jobs_ahead tells you how busy the queue is.

The one rule

No verdict is not a clean wallet

A 202, a 402, a 503 and every other non-answer carry verdict: null or no verdict field at all. Treating any of them as CLEAR is the one mistake this API is built to make impossible. Branch on the presence of verdict, never on the status code alone.

Next