RavenWolf Labs
RugPull Risk API • Public MVP Docs
Intelligence before risk

RugPull Risk API

Public MVP documentation for scoring Ethereum contracts for rug-pull risk signals. This site reflects your live routes, headers, and JSON contracts.

Code-accurate
Routes and payloads match the current server code.
Metered
Unit-based quota with plan caps and 429 handling.
Deep scan jobs
Asynchronous deep scans with job polling.
Fast scoring example
curl -sS "https://api.ravenwolflabs.com/v1/risk/score" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -d '{
    "blockchain": "ethereum",
    "contractAddress": "0x0000000000000000000000000000000000000000",
    "maxBlockScanRange": 2000
  }'
Typical response shape
{
  "riskScore": 42,
  "riskLevel": "MEDIUM",
  "confidence": "MEDIUM",
  "coveragePercent": 85,
  "dataQuality": "GOOD",
  "factors": [
    {
      "id": "RF01_PROXY_UPGRADEABLE",
      "status": "NOT_TRIGGERED",
      "severity": "MEDIUM",
      "evidence": { "isProxy": false }
    },
    {
      "id": "RF06_LIQUIDITY_NOT_LOCKED",
      "status": "UNKNOWN",
      "severity": "HIGH",
      "evidence": { "reason": "not enough data" }
    }
  ],
  "unknowns": [
    "RF06_LIQUIDITY_NOT_LOCKED"
  ],
  "aiExplanation": "MVP: AI explanation not yet enabled"
}
Heads up: MVP supports blockchain: "ethereum" only. Anything else returns a 400.

Overview

The RugPull Risk API evaluates a contract and returns a risk score (0–100), a risk tier, confidence, coverage, and a list of factor evaluations with evidence.

What’s included in the Public MVP
  • Score: POST /v1/risk/score
  • Quote: POST /v1/quote (units required + quota + plan cap evaluation)
  • Usage: GET /v1/usage (units used/remaining + reset time)
  • Deep scan jobs: POST /v1/risk/deep-scan and poll GET /v1/risk/deep-scan/{jobId}

Base URL

All public MVP endpoints are served under:

https://api.ravenwolflabs.com

Authentication

Every public MVP route requires an API key via the header configured in code: Metering.ApiKeyHeaderName = "X-Api-Key".

X-Api-Key: YOUR_API_KEY
401 behavior (per code)
  • Missing X-Api-Key401 Unauthorized
  • Unknown/invalid key → 401 Unauthorized

Plans, units & limits

Requests are metered by “units” based on maxBlockScanRange. Your plan defines: a monthly unit quota and a max scan range per request.

Key knobs (from appsettings.json)
  • DefaultMaxBlockScanRange: 2000
  • BlockUnitSize: 1000
  • Plans.*.MonthlyUnitQuota
  • Plans.*.MaxBlockScanRangePerRequest
What happens when you exceed limits
  • Over plan scan cap → 422 Unprocessable Entity
  • Over quota → 429 Too Many Requests with a JSON body

POST /v1/risk/score

POST /v1/risk/score

Scores a contract and returns a RiskScoreResponse. MVP supports blockchain = "ethereum" only.

Request body (exact contract)
{
  "blockchain": "ethereum",
  "contractAddress": "0x…",
  "maxBlockScanRange": 2000
}
Validation (from code)
  • blockchain must be "ethereum" or returns 400 with {"error":"MVP supports ethereum only"}
  • contractAddress must look like an EVM address (0x + 40 hex chars) or returns 400 with {"error":"Invalid contractAddress"}
  • maxBlockScanRange over plan cap → 422 with details
Example curl
curl -sS "https://api.ravenwolflabs.com/v1/risk/score" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -d '{
    "blockchain": "ethereum",
    "contractAddress": "0x0000000000000000000000000000000000000000",
    "maxBlockScanRange": 2000
  }'
200 response (RiskScoreResponse)
{
  "riskScore": 17,
  "riskLevel": "LOW",
  "confidence": "HIGH",
  "coveragePercent": 92,
  "dataQuality": "GOOD",
  "factors": [
    {
      "id": "RF02_OWNER_CAN_MINT",
      "status": "NOT_TRIGGERED",
      "severity": "HIGH",
      "evidence": {
        "mintSelectorsFound": [],
        "notes": "No mint selectors detected"
      }
    }
  ],
  "unknowns": [],
  "aiExplanation": "MVP: AI explanation not yet enabled"
}
Status codes (per route mapping)
200 401 422 429

POST /v1/quote

POST /v1/quote

Returns a preflight quote showing units required for the request, whether you’re within plan scan caps, and whether you have enough remaining quota.

Request body
{
  "blockchain": "ethereum",
  "contractAddress": "0x…",
  "maxBlockScanRange": 2000
}
Example response (shape matches code)
{
  "plan": "starter",
  "effectiveMaxBlockScanRange": 2000,
  "blockUnitSize": 1000,
  "unitsRequired": 2,
  "allowedByPlanCap": true,
  "allowedByQuota": true,
  "unitsRemaining": 1991,
  "unitsRemainingAfter": 1989,
  "resetsAtUtc": "2026-02-01T00:00:00.0000000+00:00",
  "allowedMaxBlockScanRange": 5000
}
Status codes (per route mapping)
200 401 422

GET /v1/usage

GET /v1/usage

Returns the current billing-period usage and reset timestamp for the API key’s plan.

Example curl
curl -sS "https://api.ravenwolflabs.com/v1/usage" \
  -H "X-Api-Key: YOUR_API_KEY"
Example response (shape matches code)
{
  "plan": "starter",
  "unitsUsedThisPeriod": 14,
  "unitsLimit": 2000,
  "unitsRemaining": 1986,
  "resetsAtUtc": "2026-02-01T00:00:00.0000000+00:00"
}
Status codes (per route mapping)
200 401

POST /v1/risk/deep-scan

POST /v1/risk/deep-scan

Creates an asynchronous deep scan job. Units are charged upfront to prevent job spamming. Returns 202 Accepted and a jobId you can poll.

Request body (same as scoring)
{
  "blockchain": "ethereum",
  "contractAddress": "0x…",
  "maxBlockScanRange": 25000
}
202 response (shape matches code)
{
  "jobId": "b1a2c3d4e5f6...",
  "status": "Pending",
  "plan": "builder",
  "unitsCharged": 25,
  "effectiveMaxBlockScanRange": 25000
}
Important
Deep scan can be disabled in config. If disabled, server returns 422 with: {"error":"deep scan is disabled"}.
Status codes (per route mapping)
202 401 422 429

GET /v1/risk/deep-scan/{jobId}

GET /v1/risk/deep-scan/{jobId}

Fetches a deep scan job. The server enforces basic isolation: a job is only visible to the API key that created it. If the key doesn’t match, you get a 404 (not found).

Example curl
curl -sS "https://api.ravenwolflabs.com/v1/risk/deep-scan/b1a2c3d4e5f6..." \
  -H "X-Api-Key: YOUR_API_KEY"
Example response (shape matches code)
{
  "jobId": "b1a2c3d4e5f6...",
  "status": "Completed",
  "plan": "builder",
  "blockchain": "ethereum",
  "contractAddress": "0x0000000000000000000000000000000000000000",
  "effectiveMaxBlockScanRange": 25000,
  "unitsCharged": 25,
  "createdAtUtc": "2026-01-21T22:10:00.0000000Z",
  "updatedAtUtc": "2026-01-21T22:10:04.0000000Z",
  "resultJson": "{ ... }",
  "error": null
}
Status codes (per route mapping)
200 401 404

Errors

The API uses a small set of consistent error bodies for common conditions. Below are the exact patterns you’ll see based on the current implementation.

Plan scan cap exceeded (422)
{
  "error": "maxBlockScanRange exceeds plan limit",
  "plan": "starter",
  "requestedMaxBlockScanRange": 9000,
  "allowedMaxBlockScanRange": 5000,
  "blockUnitSize": 1000,
  "estimatedUnitsForRequestedRange": 9
}
Quota exceeded (429)
{
  "error": "quota exceeded",
  "plan": "starter",
  "unitsRequested": 12,
  "unitsRemaining": 3,
  "unitsLimit": 2000,
  "resetsAtUtc": "2026-02-01T00:00:00.0000000+00:00"
}
Quick status guide
  • 400 — invalid blockchain or invalid contractAddress
  • 401 — missing/invalid API key
  • 422 — deep scan disabled, or plan scan cap exceeded
  • 429 — quota exceeded (metering)
  • 404 — deep-scan job not found (or not owned by your key)

Response headers

The scoring endpoint sets several headers that are useful for debugging and client behavior.

X-Plan: starter
X-Cache: HIT | MISS
X-Units-Consumed: 0 | <units>
X-Units-Remaining: <remaining>        (only set on MISS after consumption)
X-Units-Reset-At-Utc: 2026-02-01T00:00:00.0000000+00:00  (only set on MISS after consumption)

Cache hits return X-Units-Consumed: 0 and do not charge quota.

Best practices

Client reliability
  • On 429, back off and retry after a short delay.
  • Use POST /v1/quote before expensive scans in automation.
  • Log X-Plan, X-Cache, and unit headers for observability.
Cost control
  • Prefer smaller maxBlockScanRange unless you truly need more history.
  • Leverage caching (90s TTL by default) and avoid rescoring the same address repeatedly.
  • Run deep scans selectively (e.g., near-threshold scores).