Skip to main content

Developers

Wallet Score API

Pre-computed wallet analytics and the 0–100 zkCodex score over a single REST endpoint.

Updated 2026-10-10

Overview

The Wallet Analytics & Score API returns aggregated, scored onchain wallet intelligence: transaction metrics, activity streaks, DeFi breakdown, token diversity, earned badges, the 0–100 zkCodex score from the same engine as the site, and the wallet's rank among every wallet zkCodex has scored on that network. No raw log parsing is needed on your side.

Base URLhttps://zkcodex.com/api/v1
FormatJSON
AuthenticationNone. Browser calls from any site are allowed (Access-Control-Allow-Origin: *).
CachingEdge CDN, s-maxage=60, stale-while-revalidate=120

Get wallet score and analytics

Endpoint
GET /api/v1/wallet/{network}/{address}
ParameterInRequiredDescription
networkpathyesNetwork slug (base, arc, arbitrum, ethereum…) or its numeric chain ID (8453 is the same as base). See Supported networks.
addresspathyes42-character EVM hex address (0x…)

The path is the whole request: there are no query parameters. A URL with a query string answers 308 with the bare URL in Location, so every request for a wallet shares the same cached answer.

Examples

cURL
curl -X GET "https://zkcodex.com/api/v1/wallet/base/0xEAa823AB4C4eE00283d8ed7be713ddf8A5ba0Fac" \n  -H "Accept: application/json" \n  --max-time 65
TypeScript
async function getWalletScore(network: string, address: string) {
  const res = await fetch(`https://zkcodex.com/api/v1/wallet/${network}/${address}`, {
    headers: { Accept: "application/json" },
    // A wallet the cache does not hold is scanned for up to 55 s.
    signal: AbortSignal.timeout(65_000),
  });
  if (res.status === 429) {
    throw new Error(`Rate limited; retry in ${res.headers.get("Retry-After")} s`);
  }
  const body = await res.json();
  if (!body.success) throw new Error(body.error);
  return body;
}

// A network slug or its chain ID: "base" and "8453" are the same.
const base = await getWalletScore("base", "0xEAa823AB4C4eE00283d8ed7be713ddf8A5ba0Fac");
console.log(`Base score: ${base.data.score.total} [${base.data.score.tier}]`);
Python
import requests

def get_wallet_score(network: str, address: str):
    url = f"https://zkcodex.com/api/v1/wallet/{network}/{address}"
    # A wallet the cache does not hold is scanned for up to 55 s.
    res = requests.get(url, headers={"Accept": "application/json"}, timeout=65)
    if res.status_code == 429:
        raise RuntimeError(f"Rate limited; retry in {res.headers.get('Retry-After')} s")
    body = res.json()
    if not body["success"]:
        raise RuntimeError(body["error"])
    return body

data = get_wallet_score("base", "0xEAa823AB4C4eE00283d8ed7be713ddf8A5ba0Fac")
print(f"Score: {data['data']['score']['total']} ({data['data']['score']['tier']})")

Response

200 OK
{
  "success": true,
  "data": {
    "address": "0xEAa823AB4C4eE00283d8ed7be713ddf8A5ba0Fac",
    "network": "base",
    "networkName": "Base",
    "chainId": 8453,
    "nativeCurrency": "ETH",
    "isTestnet": false,
    "score": {
      "version": "score-2.1",
      "total": 100,
      "tier": "Legend",
      "rank": 214,
      "ranked": 2548,
      "percentile": "Top 8.4%",
      "breakdown": {
        "activityScore": 88.67,
        "volumeScore": 96,
        "diversityScore": 100,
        "technicalScore": 92.5
      }
    },
    "metrics": {
      "totalTransactions": 2412,
      "uniqueContracts": 733,
      "totalVolumeUSD": 223184.26,
      "totalGasSpentNative": 0.03738,
      "totalGasSpentUSD": 92.7,
      "firstTransactionDate": "2023-07-15T12:37:35.000Z",
      "lastTransactionDate": "2026-08-24T20:08:29.000Z"
    },
    "activity": {
      "activeDays": 473,
      "activeWeeks": 145,
      "activeMonths": 38,
      "currentStreakDays": 1,
      "longestStreakDays": 26
    },
    "defi": {
      "totalDeFiActivities": 482,
      "swapCount": 215,
      "liquidityCount": 42,
      "stakingCount": 18,
      "lpStakingCount": 6,
      "deployedContracts": 8,
      "nftMintCount": 145
    },
    "tokens": {
      "uniqueTokens": 86,
      "tokenTransfers": 1240,
      "tokenDiversity": 68
    },
    "behavior": {
      "transactionFrequency": 2.14,
      "consistencyScore": 0.31
    },
    "badges": {
      "totalEarned": 58,
      "totalAvailable": 69,
      "items": [
        { "id": "first-steps", "numericId": 1, "name": "First Steps", "tier": "bronze", "category": "Transactions", "description": "Made your first transaction on-chain", "earned": true },
        { "id": "base-pioneer", "numericId": 54, "name": "Base Pioneer", "tier": "base", "category": "Base Exclusive", "description": "Made your first transaction on Base", "earned": true }
      ]
    }
  },
  "cached": false,
  "timestamp": "2026-08-25T12:00:00.000Z"
}
FieldMeaning
score.versionThe score model the number comes from (score-2.1). Scores of different versions are not compared.
score.total0–100 with one decimal, the same number the site and the Score NFT show for this wallet and network.
score.tierNewbie (under 15), Bronze, Silver (35+), Gold (55+), Master (75+) or Legend (90+): the Score NFT's boundaries.
score.rankThe wallet's place among the wallets scored on this network, 1 being the best. null until at least 25 wallets are ranked on the network, and for a wallet left out of the ranking.
score.rankedHow many wallets are ranked on this network: a count, given before ranks open too. null only when the ranking could not be read for this answer.
score.percentile"Top 8.4%": rank divided by ranked, rounded up to one decimal, from the real board. null whenever rank is null.
score.breakdownThe four category scores (0–100): activityScore is Activity Consistency, volumeScore DeFi Strategy, diversityScore Ecosystem Engagement, technicalScore Technical Prowess.
defiCounts of the wallet's swaps, liquidity, staking and LP staking (lpStakingCount) actions, contracts deployed and NFT mints on this network.
behavior.transactionFrequencyTransactions the wallet sent per day between its first and its last one.
behavior.consistencyScore0–1: how evenly the wallet's transactions spread over its active days (1 is the same count every day). A wallet with a few very busy days scores near 0 however regular it is; activity.activeDays and the streaks tell how often it is active.
badges.itemsThe earned badges only: id, numericId (the badge's number, 1–69), name, tier, category, description and earned (always true).
cachedAlways false; kept for older clients. A CDN copy repeats the original body, so timestamp (when the answer was computed) tells how old it is.

Only complete scans score

When an explorer returns only part of the wallet's history, the answer is 503 with success: false and no score, never a 0 or an estimate. A complete scan is also stored and ranked, as a scan on the site is.

Limits and errors

LimitDetail
Rate limit6 scans a minute per IP address (an IPv6 address counts by its /64), counted across all of zkCodex's servers. Answers served from the CDN cache, redirects and 400s do not count.
Health checkGET /api/health takes 60 requests a minute per IP address on each server. Its report is cached at the edge for 60 s, and answers from that cache do not count.
Rate-limit headersA 429, and an error after the scan was counted (500, 503, 504), carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the minute window ends) next to Retry-After. A 429 from /api/health carries them too.
Cached answersA 200, a 308 and the /api/health report carry no RateLimit-* counters: one CDN copy is served to every caller, so it never shows one caller's numbers.
CacheA 200 is kept at the edge for 60 s (then served stale for up to 120 s while it refreshes). Errors are never cached (Cache-Control: private, no-store).
Scan timeA wallet the cache does not hold is scanned for up to 55 s, the same budget as a scan on the site. Set your client timeout above that (65 s in the examples).
Query stringsAny query string answers 308 to the bare URL, so cache busters and parameters such as ?fresh=true do not start a new scan.
CORSAccess-Control-Allow-Origin: * on every answer, with Retry-After and the RateLimit-* headers exposed to browser callers. No cookies or credentials are read.
StatusWhen
200The scan was complete: success: true with data.
308The URL had a query string. Location holds the bare URL; follow it.
400The address is not a 42-character 0x address, or the network is not in Supported networks.
429More than 6 scans in a minute from your IP address. The Retry-After header gives the seconds to wait; RateLimit-Remaining is 0.
500The scan failed unexpectedly.
503An explorer returned only part of the wallet's history (or zkCodex's shared explorer allowance for public scans is used up for now), so there is no score. Try again later.
504The scan ran past its 55 s budget. Try again later; a wallet with a very long history may need a few tries.
Every error body
{
  "success": false,
  "error": "Too many wallet scans; try again in a minute"
}

Supported networks

Use the slug or the chain ID in the path: /api/v1/wallet/base/0x… and /api/v1/wallet/8453/0x… return the same wallet. Any other network answers 400.

NetworkSlugChain IDData source
Basebase8453Etherscan V2
Monadmonad143Etherscan V2
Arbitrumarbitrum42161Etherscan V2
Optimismoptimism10Etherscan V2
Celocelo42220Etherscan V2
Ethereumethereum1Etherscan V2
Unichainunichain130Etherscan V2
Inkkraken57073Blockscout
Polygonpolygon137Etherscan V2
ZKsynczksync324Blockscout
Worldchainworldchain480Etherscan V2
Soneiumsoneium1868Blockscout
Avalancheavalanche43114Etherscan V2
Linealinea59144Etherscan V2
Shapeshape360Blockscout
Base Sepoliabasesepolia84532Etherscan V2
Plumeplume98866Blockscout
Scrollscroll534352Blockscout
Katanakatana747474Etherscan V2
XRPL EVMxrpl1440000Blockscout
HyperEVMhyperevm999Etherscan V2
BNB Smart Chainbsc56Etherscan V2
Abstractabstract2741Etherscan V2
GIWA Testnetgiwa91342Blockscout
Arcarc5042Etherscan V2
Arc Testnet (Archived)arc-testnet5042002Blockscout
Robinhood Chainrobinhood4663Etherscan V2

Something unclear or missing here?

Send it to the team in a few seconds. Signed in with your wallet, you get our reply on your profile.