sol.wales

Public API v1

Read-only JSON over HTTPS. No key needed for modest use; sign in to create a key for a higher limit. Everything the pages show, the API returns too.

Basics

  • Base URL: https://sol.wales/api/v1. All responses are application/json; charset=utf-8.
  • CORS is open for GET and OPTIONS, so browsers can call it directly.
  • Rate limit: 60 requests per minute per IP without a key, 600 per minute with one. Over the limit you get 429 with a Retry-After header. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining.
  • Responses carry Cache-Control: public, max-age=30 (or more): the data itself only changes when the sync runs, every few minutes for a batch of wallets.
  • Amounts are strings in UI units (SOL, or the token's decimals applied). solLamports is a string too (it can exceed 2^53). Times are ISO 8601 in UTC.
  • Errors look like { "error": "message" } with a 4xx/5xx status.

API keys (optional)

Sign in and create a key on your account page. It starts with sw_ and is shown once. Send it as Authorization: Bearer sw_… or X-API-Key: sw_…. An invalid or revoked key returns 401; leaving it out just falls back to the anonymous limit.

Endpoints

GET /api/v1/traders?source=

All actively tracked wallets, ordered like the leaderboard (24h moves, 7d moves, SOL balance). source=discovered returns only wallets found by discovery, source=manual only the ones added by an admin; anything else is a 400.

curl https://sol.wales/api/v1/traders?source=discovered

{
  "traders": [
    {
      "address": "…",
      "label": null,
      "tags": ["auto:balance"],     // auto:balance | auto:move says which threshold promoted it
      "source": "discovered",       // discovered | manual
      "active": true,
      "addedAt": "2026-09-29T10:00:00.000Z",
      "firstSeenAt": "2026-09-29T09:30:00.000Z",  // discovered wallets: first sighting
      "retiredAt": null,
      "retiredReason": null,
      "lastSyncedAt": "2026-09-29T12:00:00.000Z",
      "lastMoveAt": "2026-09-29T11:58:12.000Z",
      "solLamports": "123456789000",
      "sol": 123.456789,
      "tokenCount": 12,
      "moves24h": 8,
      "moves7d": 31
    }
  ],
  "source": "discovered",
  "generatedAt": "…"
}

GET /api/v1/traders/:address

One wallet with its last 60 balance snapshots and last 20 moves. 404 when the address is not tracked.

{
  "trader": { … as above … },
  "snapshots": [{ "at": "…", "solLamports": "…", "sol": 1.5, "tokenCount": 3 }],
  "moves": [ … see below … ]
}

GET /api/v1/traders/:address/moves?since=&limit=

Moves of one wallet, newest first. since is an ISO date or a unix timestamp (seconds or milliseconds); limit defaults to 50, max 200.

{
  "moves": [
    {
      "signature": "…",
      "at": "2026-09-29T11:58:12.000Z",
      "kind": "swap",              // sol_in | sol_out | token_in | token_out | swap | other
      "mint": "…" | null,           // primary asset; null means SOL
      "amount": "1234.5",           // of the primary asset, always >= 0
      "counterparty": "…" | null,   // best guess for plain transfers
      "detail": {                   // the raw deltas the kind was derived from
        "solLamports": "-2500000000",
        "fee": "5000",
        "tokens": [{ "mint": "…", "delta": "1234500000", "decimals": 6 }]
      },
      "trader": { "address": "…", "label": "…" }
    }
  ]
}

GET /api/v1/moves/recent?limit=

The newest moves across all active wallets (default 50, max 200). Poll this to follow everyone at once.

How the data is produced

  • Discovery. Every half hour a job samples the 40 latest signatures of each of jupiter-v6, raydium-amm-v4, orca-whirlpool, pump-fun, fetches a bounded number of those transactions and records their signers as candidates with the size of their SOL move. Candidate balances are read in bulk. A wallet is promoted to the list (source discovered) once it held at least 1,000 SOL, or moved at least 250 SOL in one transaction while still holding 100 SOL, on 2 separate runs. Known exchange hot wallets, programs and non-wallet accounts are never promoted. Every run is capped at 150 RPC calls.
  • Sync. Every few minutes the 25 least-recently-synced wallets are re-read: SOL balance, the number of SPL token accounts with a balance, and the 25 most recent signatures. Transactions not seen before are fetched with jsonParsed encoding and classified from the wallet's pre/post SOL and token balances. A snapshot is stored when the balance changed or an hour passed.
  • Retirement. A wallet whose balance stays under 100 SOL for 7 days becomes active: false with a retiredReason; its history stays available at /api/v1/traders/:address.
  • Window: only the latest 25 signatures per sync are inspected. A wallet doing hundreds of transactions in ten minutes will have gaps. Failed transactions are skipped.
  • Swap means one asset left and another arrived in the same transaction; mint/amount describe what arrived. SOL changes under 0.005 SOL are treated as fees/rent, not transfers. Only the classic SPL token program is counted for tokenCount.
  • Wallet labels are written by hand by the site's admins; discovered wallets have none. Nothing is inferred about who owns a wallet, and being on the list only means the thresholds above were met.
  • Pages and the API read from the database only; they never query an RPC node on request.

Fair use

Cache what you fetch, honour Retry-After, and identify your client with a User-Agent. Abuse reports and questions: hbouma01@gmail.com.

Disclaimer

Not financial advice. sol.wales shows public on-chain data as read from a Solana RPC node, on a delay and with a limited window per wallet. Wallet labels are curated by hand and can be wrong; transaction classification is heuristic. Nothing here is a recommendation to buy, sell or copy anything. No guarantees of accuracy, completeness or availability. You act on your own responsibility.