Solana Tracker LogoSolana Tracker
Swap
Developers
⌘K
Affiliate
All Resources
Solana SNS RPC: Primary .sol Names in One Call
UpdatesJSON-RPCJune 4, 20265 min readUpdated October 7, 2026Solana Tracker

Solana SNS RPC: Primary .sol Names in One Call

Solana SNS RPC methods: resolve primary .sol names for up to 100 wallets per call, list owned domains and find any name's owner, with a fetch script.

  • updates,
  • solana-rpc,
  • sns,
  • domains
Solana RPC›Solana Data API›

Solana SNS RPC methods on Solana Tracker turn wallet addresses into .sol names, and names back into owners, with one JSON-RPC call each instead of chained account reads. Use them to label wallets in holder lists and leaderboards while storing the address as identity.

Solana SNS RPC methods

Each method costs 1 credit per call on the same endpoint as standard RPC. Twitter/X .twitter handles are not supported.

MethodParamsReturns
getPrimaryDomain[wallet]{ wallet, domain }, domain is null when no primary is set
getMultiplePrimaryDomains[[wallet, ...]], up to 100{ results: [{ wallet, domain }] } in input order
getAllDomains[wallet]{ wallet, domains: [...] }, including tokenized domains
resolveSolDomain["name.sol"]{ domain, owner, nameAccount } or null

These are provider extensions; a stock node returns -32601 Method not found, and you would instead derive name accounts and read them with getMultipleAccounts. If you already call Data API PnL V2 or holders endpoints, read identity.sns.domain from that response instead of making another call.

Label a page of wallets

This script validates wallets, requests primary names in batches of 100 and falls back to a shortened address (Node 20+, tsx).

# .env: full URL including ?api_key= (https://www.solanatracker.io/account/shared-rpc)
SOLANA_RPC_URL=
WALLETS=9aoUCn5J4sxhvYERCVwVnakPQxyXTHQVXe86CUJYxY8p,FMmaHPDL47V1gXsfh9WjgAT7Er3dfDvarQubTU1Jxc1r
// sns.ts: label wallets with their primary .sol names (up to 100 per call)
export {}; // ES module, so top-level await type-checks

const RPC_URL = process.env.SOLANA_RPC_URL?.trim() ?? "";
if (!RPC_URL) {
  console.error("Missing SOLANA_RPC_URL. Copy the full URL from https://www.solanatracker.io/account/shared-rpc");
  process.exit(1);
}
if (!URL.canParse(RPC_URL)) {
  console.error("SOLANA_RPC_URL is not a valid URL"); // never print it: it contains the key
  process.exit(1);
}

const BASE58 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
const input = process.env.WALLETS ?? "9aoUCn5J4sxhvYERCVwVnakPQxyXTHQVXe86CUJYxY8p,FMmaHPDL47V1gXsfh9WjgAT7Er3dfDvarQubTU1Jxc1r";
const wallets = [...new Set(input.split(",").map((w) => w.trim()).filter(Boolean))];
const invalid = wallets.filter((w) => !BASE58.test(w));
if (wallets.length === 0 || invalid.length > 0) {
  console.error(`WALLETS must be comma-separated base58 addresses. Invalid: ${invalid.join(", ") || "(none given)"}`);
  process.exit(1);
}

type PrimaryResult = { wallet: string; domain: string | null };
type RpcBody = {
  result?: { context: { slot: number }; value: { results: PrimaryResult[] } };
  error?: { code: number; message: string };
};

async function primaryDomains(batch: string[]): Promise<PrimaryResult[]> {
  const res = await fetch(RPC_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getMultiplePrimaryDomains", params: [batch] }),
    signal: AbortSignal.timeout(10_000),
  });
  if (res.status === 429) throw new Error("Rate limited (HTTP 429). Slow down or upgrade the plan.");
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const body = (await res.json()) as RpcBody;
  if (body.error) throw new Error(`RPC error ${body.error.code}: ${body.error.message}`);
  if (!body.result) throw new Error("Response had no result");
  return body.result.value.results;
}

const short = (w: string) => `${w.slice(0, 4)}...${w.slice(-4)}`;

try {
  const rows: { wallet: string; label: string; domain: string }[] = [];
  for (let i = 0; i < wallets.length; i += 100) {
    // Results come back in input order; join by wallet anyway so reordering can never mislabel a row.
    const byWallet = new Map((await primaryDomains(wallets.slice(i, i + 100))).map((r) => [r.wallet, r.domain]));
    for (const wallet of wallets.slice(i, i + 100)) {
      const domain = byWallet.get(wallet) ?? null;
      rows.push({ wallet, label: domain ?? short(wallet), domain: domain ?? "(none)" });
    }
  }
  console.table(rows);
} catch (error) {
  const message = error instanceof Error && error.name === "TimeoutError" ? "Request timed out after 10s" : String(error);
  console.error(message);
  process.exit(1);
}

Run it with node --env-file=.env --import tsx sns.ts. getPrimaryDomain returns the same { wallet, domain } pair directly in value.

Response shape

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 423867852, "apiVersion": "3.0.0" },
    "value": {
      "results": [
        { "wallet": "9aoUCn5J4sxhvYERCVwVnakPQxyXTHQVXe86CUJYxY8p", "domain": "solanatracker.sol" },
        { "wallet": "FMmaHPDL47V1gXsfh9WjgAT7Er3dfDvarQubTU1Jxc1r", "domain": null }
      ]
    }
  }
}

domain is null when no primary name is set or when the favourite domain is no longer owned by the wallet. getAllDomains returns an empty domains array for a wallet with none, and resolveSolDomain returns value: null for an unknown name. It handles root names and one level of subdomain.

Pitfalls

  • Storing names as identity. Names transfer. Store the address, render the name.
  • Treating null as an error. It is a normal result; show the shortened address.
  • Sending money to a cached name. Before any transfer, call resolveSolDomain at that moment and show the resolved owner.
  • One call per row. Batch the visible page; cache labels briefly, null included.
  • Checking only HTTP status. A 200 can carry a JSON-RPC error.

For full identity enrichment beyond SNS, see the wallet identity guide. The PnL leaderboard guide shows SNS labels on ranked wallets. Both the Solana RPC endpoint and the Solana Data API return these names.

FAQ

How many wallets can one call resolve?

getMultiplePrimaryDomains accepts up to 100 wallets and returns results in input order. Split longer lists.

What does a null domain mean?

The wallet has no primary name, or its favourite domain is no longer owned. The address is still valid.

Do these methods work on any Solana RPC provider?

No. They are Solana Tracker extensions; other endpoints return Method not found.

How much does a lookup cost?

One credit per call, batch calls included.

References

  • SNS domains guide
  • getPrimaryDomain
  • getMultiplePrimaryDomains
  • getAllDomains
  • resolveSolDomain
  • PnL V2 SNS identity

Related Guides

Solana Address Lookup Table RPC: Find and Pick LUTs
Updates

Solana Address Lookup Table RPC: Find and Pick LUTs

Read more
Solana Tracker Data API SDK 0.5: Migration Notes
Updates

Solana Tracker Data API SDK 0.5: Migration Notes

Read more
Solana RPC Providers: How to Evaluate and Choose
Solana RPC

Solana RPC Providers: How to Evaluate and Choose

Read more

Products

  • Data API
  • Pump.fun API
  • Solana RPC
  • Dedicated Nodes
  • Yellowstone gRPC
  • Raptor Swap API
  • Enterprise

Trading

  • Swap
  • Latest Tokens
  • Trending
  • Top Gainers
  • Memescope
  • Whale Watch
  • KOL Tracker

Tools

  • Wallet Tracker
  • Rugcheck
  • PnL Leaderboard
  • KOLScan
  • Axiom Leaderboard
  • Photon Leaderboard
  • Bloom Leaderboard
  • FOMO Leaderboard
  • GMGN Leaderboard
  • Pump.fun App Leaderboard
  • Terminal Leaderboard
  • Platform Compare
  • My Positions
  • Teams

Resources

  • Developer Guides
  • Blog
  • Documentation
  • API Reference
  • Status
  • Affiliate Program — 25% recurring, uncapped
Solana TrackerSolana Tracker© 2026
Terms of ServicePrivacy PolicyContact