API Documentation
v1.0.0

Getting Started

The TopWallets API v1 exposes three features: token scans with their top 300 wallets, individual wallet scans, and cross-token wallet correlation. Responses use one envelope: { data, meta?, request_id } on success, { error: { code, message, details? }, request_id, documentation_url } on failure.

https://www.topwallets.ai/api/v1

OpenAPI specification

The machine-readable contract lives at /openapi/v1.json (OpenAPI 3.1). Import it into Postman or Insomnia, or generate a typed client from its operationIds.

Authentication

Every request needs a Bearer key issued by the TopWallets team (format tw_live_…, tied to an active Pro or Premium profile). Keys are stored hashed and shown once at creation; never ship them to a browser.

Authorization: Bearer tw_live_YOUR_KEY

Missing or unknown key → 401. Valid key on a free plan, with a missing profile, or when the subscription cannot be verified → 403 FORBIDDEN_PLAN (the API fails closed).

Rate Limits & Daily Quotas

60 requests per minute per key (fixed window, enforced durably across all server instances; per-key overrides possible). Responses carry X-RateLimit-Limit / -Remaining / -Reset (omitted while the counter store is briefly unreachable, fail-open); a 429 includes Retry-After.

PlanScans / day (token scan trigger + wallet scan)Reads / day (top-wallets + wallet GET)
Pro1050
Premium50500

Quotas reset at UTC midnight and share the same pools as web-app usage. Reusing an already-running token scan costs nothing. Correlations are per-minute limited and metered but have no daily cap in v1.

Token Scan → Top Wallets (~300)

Asynchronous job: trigger, poll, read. A typical scan analyzes ~300 wallets in 2–5 minutes.

StepEndpointNotes
POST
/tokens/{mint}/scans202 + scan_id. Idempotent: a scan active within the last 15 min is reused (reused: true, no quota consumed).
GET
/scans/{scan_id}Poll every Retry-After (5 s). stalled: true after 20 min without progress ⇒ safe to re-trigger. A failed scan can also be re-triggered (a fresh trigger consumes quota).
GET
/tokens/{mint}/top-wallets?limit=300Token + up to 300 wallets with full stats and 1d/7d/30d history. Readable mid-scan (partial results).
BASE="https://www.topwallets.ai/api/v1"
KEY="tw_live_YOUR_KEY"
MINT="DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"

SCAN_ID=$(curl -s -X POST "$BASE/tokens/$MINT/scans" \
  -H "Authorization: Bearer $KEY" | jq -r '.data.scan_id')

while :; do
  STATUS=$(curl -s "$BASE/scans/$SCAN_ID" -H "Authorization: Bearer $KEY")
  S=$(echo "$STATUS" | jq -r '.data.status')
  STALLED=$(echo "$STATUS" | jq -r '.data.stalled')
  [ "$S" != "processing" ] && break
  [ "$STALLED" = "true" ] && echo "stalled — re-trigger the scan" && break
  sleep 5
done

curl -s "$BASE/tokens/$MINT/top-wallets?limit=300" \
  -H "Authorization: Bearer $KEY"

Wallet Scan

POST /wallets/{address}/scan is synchronous (< 60 s): fetch, score, store, return. GET /wallets/{address} is the cheap database-only read. data.source tells you what you got: fresh (provider, this request), cache (< 1 h old, bypass with {"force_refresh": true}) or stale-fallback (provider failed, older data served).

Note: the POST consumes a daily scan even when the response comes from cache; use the GET for free-form reads (it only consumes a daily read).

curl -s -X POST "https://www.topwallets.ai/api/v1/wallets/4Nd1mYbJ6PXvDra3DnzgUtQVtUUSDtdQZFLz4WqQbfvF/scan" \
  -H "Authorization: Bearer tw_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"force_refresh": false}'

Correlation

GET /correlations?tokens=mint1,mint2&limit=100 returns wallets that traded at least 2 of the given tokens (2–5 mints), ordered by overlap count. Set-overlap counting, not statistical correlation: winners / first_buyers restrict each token's list to its top-100 by realized PnL / earliest buyers. Paginate with limit (max 500) and offset (max 100000); meta.truncated signals more rows. Unscanned mints appear in meta.tokens_missing; trigger a token scan for each, then re-query.

curl -s "https://www.topwallets.ai/api/v1/correlations?tokens=DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263,EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&limit=100" \
  -H "Authorization: Bearer tw_live_YOUR_KEY"

Error Codes

CodeStatusMeaning
UNAUTHORIZED401Missing, unknown or revoked API key
FORBIDDEN_PLAN403The key's profile is not on a Pro/Premium plan
QUOTA_EXCEEDED403Daily quota exhausted; details carry used, limit and resets_at
VALIDATION_ERROR400Invalid parameter; details list each field and the received value
TOKEN_NOT_SCANNED404No report for this token yet; details.trigger is the call to make
SCAN_NOT_FOUND404Unknown scan id
WALLET_NOT_FOUND404Wallet not in the index yet
RATE_LIMITED429Per-minute limit hit; honor Retry-After
UPSTREAM_ERROR502The upstream data provider failed
UPSTREAM_TIMEOUT504The provider or the correlation query timed out
INTERNAL_ERROR500Unexpected error; report the request_id

Every error body carries a documentation_url and the request_id to include in support requests.

Legacy Bot API

The original bot endpoints stay available for existing integrations. New integrations should use the v1 API above.

https://www.topwallets.ai/api/bot/solana
GET /top-wallets

Description

Returns a list of top-performing wallets based on their trading performance over a 7-day period.

Query Parameters

ParameterTypeDescription
limitnumberMaximum number of wallets to return (default: 100, max: 100)

Example Requests

curl -X GET \
  "https://www.topwallets.ai/api/bot/solana/top-wallets?limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Format

{ "success": true, "message": "Successfully fetched top wallets", "data": [ { "all": { "score": number, "winrateScore": number, "pnlScore": number, "roiScore": number, "consistencyScore": number, "address": string, "formattedAddress": string, "winrate": number, "tokenTraded": number, "averageHoldingTime": string, "realizedPnl": string, "unrealizedPnl": string, "realizedRoi": string, "unrealizedRoi": string, "combinedRoi": string, "realizedPnlRaw": number, "unrealizedPnlRaw": number, "realizedRoiRaw": number, "unrealizedRoiRaw": number, "combinedRoiRaw": number, "combinedPnlRaw": number, "totalInvested": number | string | null, "totalInvestedFormatted": string | null, "averageBuyAmount": number | string | null, "averageBuyAmountFormatted": string | null, "totalWins": number | null, "totalLosses": number | null, "lossPercentage": number | null, "name": string | null, "twitter_url": string | null, "picture_url": string | null, "type": "normal" | "kols", "isFavorite": boolean, "updatedAt": string }, "30d": { /* Same structure as 'all' */ }, "7d": { /* Same structure as 'all' */ }, "1d": { /* Same structure as 'all' */ } } // ... more wallet trading data ] }

Rate Limits

10 requests per minute

Error Codes

StatusDescription
401
Unauthorized - Invalid API key
404
No top wallets found
500
Internal server error