// public sandbox · real alchemy ingestion · model v0.5.0-gov-expanded · status
SYBILSHIELD

// api docs

Docs

Every example below hits the live sandbox at https://api.sybilshield.org. Replace $KEY with your sandbox key from /dashboard/api-keys.

1 · Getting started

Sign up at /dashboard with an email — no card required. SybilShield is a free public good: you get an API key with a public-sandbox fair-use limit of 100 calls/month. Store the key somewhere safe; it cannot be retrieved later (only rotated).

2 · Authentication

All authenticated endpoints take a bearer token.

curl -H "Authorization: Bearer $KEY" \
  https://api.sybilshield.org/v1/account
{
  "id": "...",
  "email": "you@example.com",
  "plan": "free",
  "api_key_prefix": "sk_live_xxxx...",
  "usage": { "calls_this_month": 3, "limit": 100 }
}

3 · Presets & decisions

Every analysis comes back with a per-address verdict — DROP, REVIEW, or KEEP — computed server-side from the preset you pick at submission time. Pass preset in the POST body; the verdict is then stored against the analysis row (and audit-log entries) so the decision is reproducible after the fact.

PresetDROP ifREVIEW ifTuned for
balancedscore ≥ 80score ≥ 50default — pick if unsure
airdropscore ≥ 85 OR cluster_size ≥ 50score ≥ 60 OR cluster_size ≥ 20aggressive — token distributions
daoscore ≥ 90 OR cluster_size ≥ 30score ≥ 50 OR cluster_size ≥ 10conservative — governance voting
grantcluster_size ≥ 20cluster_size ≥ 5 OR score ≥ 70cluster-first — grant committees

Cluster-only mode. Pass mode: "cluster_only" to skip per-address ML scoring entirely and return only multi-wallet farm groupings. Useful when you care more about "these 47 wallets are one entity" than about per-address risk scoring.

# Submit with a preset + mode
curl -X POST https://api.sybilshield.org/v1/analyses \
  -H "Authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "wave-2 airdrop scan",
    "chains": ["ethereum"],
    "preset": "airdrop",
    "mode": "full",
    "addresses": ["0xabc...", "0xdef..."]
  }'

Threshold overrides. Tune a preset for one analysis with threshold_overrides. Each knob is optional — a number tightens/loosens it, null disables it, omitting it keeps the preset value. Rows decided with an override carry a custom_thresholds rationale code, and the override is stored on the analysis so the decision is reproducible.

# Airdrop preset, but I've already excluded my own CEX
# deposit wallets, so tighten the cluster knob from 50 -> 12
curl -X POST https://api.sybilshield.org/v1/analyses \
  -H "Authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "wave-2 (cex-excluded)",
    "chains": ["ethereum"],
    "preset": "airdrop",
    "threshold_overrides": {
      "drop":   { "cluster_size_gte": 12 },
      "review": { "cluster_size_gte": 5 }
    },
    "addresses": ["0xabc...", "0xdef..."]
  }'

Decision rules live in apps/api/src/lib/presets.ts (canonical) and apps/ml/sybilshield/scoring/presets.py (Python mirror) — open-source on GitHub.

4 · Create an analysis

Submit a list of addresses on one or more chains. Returns immediately with an id andstatus: pending. The worker picks the job up, ingests on-chain data via Alchemy, runs the detection pipeline, and writes scores.

curl -X POST https://api.sybilshield.org/v1/analyses \
  -H "Authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "my first analysis",
    "chains": ["ethereum"],
    "preset": "balanced",
    "addresses": [
      "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      "0xab5801a7d398351b8be11c439e05c5b3259aec9b"
    ]
  }'
{
  "id": "325554d5-559a-40dc-bd34-b3d180d5eb08",
  "status": "pending",
  "address_count": 2,
  "estimated_time_minutes": 2
}

5 · Check analysis status

Poll the resource until status === "complete".

curl -H "Authorization: Bearer $KEY" \
  https://api.sybilshield.org/v1/analyses/$ID
{
  "id": "325554d5-...",
  "status": "complete",
  "name": "my first analysis",
  "chains": ["ethereum"],
  "address_count": 2,
  "summary": {
    "total_scored": 2,
    "sybil_count": 0,
    "suspicious_count": 0,
    "genuine_count": 2,
    "cluster_count": 0
  },
  "processing_time_seconds": 5,
  "completed_at": "2026-05-25T..."
}

Or configure a webhook — see §7 — and skip polling entirely.

6 · Read results

Per-address scores with structured evidence + the preset-aware decision verdict. Paginated; default 100 per page. Filters: ?decision=DROP (or REVIEW/KEEP), or legacy ?label=sybil.

curl -H "Authorization: Bearer $KEY" \
  "https://api.sybilshield.org/v1/analyses/$ID/results?decision=DROP&limit=10"
{
  "data": [
    {
      "address": "0x9bfc0b6c1f1f6c9c30b6e8c4e8c1f2a1b3c4d5e6",
      "chain": "ethereum",
      "sybil_score": 100,
      "label": "sybil",
      "confidence": "1.000",
      "cluster_id": "F-a8b2c1d4",
      "cluster_size": 12,
      "decision": "DROP",
      "decision_confidence": "high",
      "rationale_codes": [
        "shared_funder_cluster",
        "score_ge_85",
        "cluster_size_ge_10"
      ],
      "evidence": [
        {
          "type": "shared_funding",
          "description": "Funded from same source as 11 other addresses within 6h.",
          "confidence": 0.95
        }
      ]
    }
  ],
  "page": 0,
  "limit": 10
}

Decision is the preset-aware verdict (preferred for filtering). Score 0–100 remains available for callers who want to apply their own thresholds. Evidence is a JSON array describing which methods fired and which features pushed the score — populated only for flagged scores.

7 · Export CSV

One row per address, same scoring columns. Auth required (Bearer header).

curl -H "Authorization: Bearer $KEY" \
  "https://api.sybilshield.org/v1/analyses/$ID/results/export" \
  -o analysis-$ID.csv
address,chain,sybil_score,label,confidence,cluster_id,cluster_size
0xd8da6bf26964af9d7eed9e03e53415d37aa96045,ethereum,0,genuine,0.000,,
0xab5801a7d398351b8be11c439e05c5b3259aec9b,ethereum,0,genuine,0.000,,

8 · Webhook behavior

Configure a webhook URL + secret on /dashboard/api-keys. When an analysis completes, we POST a JSON event with a SHA-256 HMAC signature in X-SybilShield-Signature.

POST https://your-server.com/webhook
Content-Type: application/json
X-SybilShield-Signature: sha256=...

{
  "type": "analysis.completed",
  "analysisId": "325554d5-...",
  "data": {
    "total_scored": 2,
    "sybil_count": 0,
    "suspicious_count": 0,
    "genuine_count": 2,
    "cluster_count": 0
  }
}

Verify with HMAC-SHA256 over the raw body, using your webhook_secret as the key. If signatures don't match, reject the event.

9 · Audit log

Every flagged score and every appeal verdict writes an append-only row to the audit log. Read your slice with:

curl -H "Authorization: Bearer $KEY" \
  "https://api.sybilshield.org/v1/audit-log?analysis_id=$ID&limit=100"

10 · Fair-use limits

SybilShield is a free public good. These fair-use limits keep the shared sandbox healthy — they are not a paywall. Running heavier research? Email us for more headroom.

LimitValue
Requests / minute30
Write calls / month100
Addresses / analysis1,000
Concurrent analyses1
Upload size1 MB

What counts toward the monthly limit. Write calls — POST /v1/analyses, POST /v1/score/batch, POST /v1/feedback, and other write endpoints. What does NOT count: all GET reads (/v1/analyses/:id, /v1/analyses/:id/results, /v1/account, etc.) — dashboard polling and result viewing are free.

Error codes. 429 monthly_quota_exceeded (fair-use monthly limit, resets monthly); 429 rate_limit_exceeded with retry_after_seconds; 429 concurrent_limit_exceeded; 400 too_many_addresses; 400 estimated_cu_exceeds_budget.

11 · Current sandbox limitations

The public sandbox is intended for testing the API flow, dashboard, evidence format, and scoring workflow. Real on-chain ingestion runs through Alchemy on 5 chains (Ethereum, Arbitrum, Optimism, Base, Polygon). Production model calibration on a real labeled corpus is handled separately from the sandbox environment.

  • Scores in the sandbox may shift as the model is recalibrated.
  • Cluster IDs are stable within an analysis, not across analyses.
  • Evidence arrays are empty when sybil_score < 40.
  • Webhook retries are manual today (auto-retry with backoff is on the roadmap).

Bug or unclear doc? support@sybilshield.org