Free Solana Token Data API: Search, Resolve, Batch and Cache With Carbium

Free Solana Token Data API: Search, Resolve, Batch and Cache With Carbium

Share
Free Solana Token Data API: Search, Resolve, Batch and Cache With Carbium

Type USDC into a Solana token selector right now and ask the public Carbium Token Index for the top three matches. Here is what comes back today:

{
  "items": [
    {"mint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","symbol":"USDC","is_token22":false,"logo_status":"ok"},
    {"mint":"1aRxsFJKegDUmVXMk7nBUw7yRMpAorkbBEnrUL52vYH","symbol":"USDC","is_token22":true, "logo_status":"broken"},
    {"mint":"21vK6XK6nXFPtH3Zc3aesnxNSBx4hzLLkEE5WN8XpjJ8","symbol":"USDC","is_token22":true, "logo_status":"broken"}
  ]
}

Three different mints, all symboled USDC. The canonical one, EPjF… from Circle, comes first, with the logo verified. Below it sit two Token-2022 spam mints with the same symbol and broken logos. A token selector that ranks naively, or worse, falls back to the first match by symbol, can sign a user into a swap they did not mean to make.

That collision is one example of what a real Solana token data layer has to handle. The visible failures are usually small, a missing logo, a duplicated symbol, a portfolio view that fires one lookup endpoint hundreds of times per render. The underlying problem is bigger. Solana token data is a live index of SPL and Token-2022 mints, metadata enrichment, logo availability, volume hints, authorities, and enough rate-limit discipline to keep the frontend from turning every render into an API incident.

Carbium Data is the public token data layer for that job. The first live surface is the Carbium Token Index API at https://tokens.carbium.io: a free, rate-limited REST API for token search, single-mint lookup, 500-mint batch resolves, and a cached logo proxy that never serves a broken image. It does not require an API key on the current public surface.

This is not a replacement for your own product database. It is the fast shared layer you use when your wallet, swap UI, trading dashboard, token selector, agent, or backend job needs Solana token metadata without building an indexer first.

logo
Photo by Mariia Shalabaieva / Unsplash

What Carbium Data is

Carbium Data is part of the broader Carbium Solana infrastructure stack alongside RPC, gRPC streams, the Swap API, and the Carbium DEX. The public Token Index API focuses on one practical surface: helping builders identify and hydrate Solana tokens.

The live API gives you four core routes.

Need Endpoint Why it matters
Search by symbol, name, or mint GET /tokens Token selectors, import-token flows, admin search
Resolve one mint GET /tokens/:mint Canonical token detail pages and backend lookups
Resolve many mints POST /tokens/batch Portfolios, swap UIs, token lists, agent context
Serve a cached logo GET /img/:mint Always-200 image responses so <img> tags never break

The base URL is:

https://tokens.carbium.io

The protocol is plain REST over HTTPS. POST /tokens/batch uses application/json. The current public surface has no API key requirement and permissive CORS, so it can be called from a browser when the public rate limits fit your use case.


How the token index stays current

Under the hood the Token Index is a streaming pipeline, not a static list. Carbium's own Solana RPC plus a gRPC listener feed an enrichment worker that pulls Metaplex and Token-2022 metadata into a SQLite + FTS5 registry. That registry now covers roughly 2M Solana mints and is the source of truth behind every public route.

Three properties of that pipeline matter when you integrate against it. Freshness comes from Yellowstone, not polling, a newly created mint shows up because the listener saw it on chain, not because a job rescanned the network on a fixed cadence. Ranking uses FTS5, which is why search prefers exact mint, then exact symbol, then symbol prefix, then name prefix, then fuzzy: it is a real full-text index, not a substring scan. And every route reads from the same SQLite store, which is why a batch request can resolve up to 500 mints without fanning out to many backend services.

Why Solana token data needs a real API

The naive version of token data is a static JSON list. That stops working once you build anything dynamic.

Solana has established mints, newly created mints, Token-2022 mints, spam mints, missing metadata, broken logos, authority fields, Metaplex metadata, and user input that might be a mint, symbol, or partial name. A good app needs to handle all of that without pretending every row is clean.

Carbium's Token Index API is built for those real integration paths:

Problem Carbium Data behavior
A user types USDC GET /tokens?q=USDC&limit=3 ranks exact symbol matches before weaker matches
Your app already has a mint GET /tokens/:mint returns the token record or 404 if not indexed yet
Your app has many mints POST /tokens/batch resolves up to 500 mints in one request
A logo URL breaks upstream GET /img/:mint returns 200 with an Unknown SVG fallback instead of a 404
A token has incomplete metadata Nullable fields are normal; the client should degrade gracefully
You want to hide Token-2022 rows include_token22=false can be used on search

A live test on 2026-05-22 returned canonical USDC first for q=USDC&limit=3, followed by the two noisier Token-2022 rows with broken logo status. That is useful behavior, but it is also a reminder: store mints as durable IDs. Symbols are display data, not identity.

Why not Jupiter, Helius DAS, or Birdeye?

Three obvious alternatives come up when builders pick a Solana token data source. Jupiter's hosted token list is a static JSON file, fine for curated mainstream tokens, but no FTS5 ranking, no batch resolve, no cached logo proxy, and no opinion about which of three USDC mints is the real one. Helius DAS (Digital Asset Standard) is asset-shaped rather than token-shaped, returns NFTs and compressed assets through the same surface, runs heavier on the wire, and requires an API key, the right tool when you actually need DAS for NFTs, the wrong shape behind an SPL token selector. Birdeye is paywalled at any meaningful tier and is closer to a price and chart data provider than a metadata index. Carbium Data is small, free, no-key, Solana-token-shaped, and built for the four things a swap UI, wallet, or agent actually does with token data: search, resolve, batch hydrate, and render a logo.

Feature map

The current public Token Index API is deliberately small, which is good for builders. You can wire the whole useful surface in one sitting.

Feature Current public behavior
Search Search by mint, symbol, partial name, or empty query for top-by-volume behavior
Ranking FTS5-backed: exact mint, exact symbol, symbol prefix, name prefix, then fuzzy
Pagination page is 1-indexed; limit accepts 1-200 and defaults to 50
Token-2022 filtering include_token22 defaults to true; set false to hide Token-2022 rows
Single lookup Resolve one SPL or Token-2022 mint by base58 pubkey
Batch lookup Resolve up to 500 base58 mint addresses per request
Unknown handling Batch responses separate resolved items from unknown mints; unknowns auto-enqueue for on-chain enrichment
Logo proxy /img/:mint always returns 200, falling back to an Unknown SVG when upstream is unreachable
Token shape Includes identity fields, logo status, Token-2022 flag, nullable metadata, authorities, Metaplex fields, extensions, and tags when available
Auth None on the current public surface
CORS Permissive, callable from browsers
Pricing Free public surface, rate-limited by route

The "free" part matters, but it should not be abused. The correct production posture is to cache, batch, debounce, and talk to Carbium if you need higher sustained throughput or custom data.

Public rate limits

The public Token Index API is free and rate-limited per IP. Current public route budgets are:

Endpoint Budget
GET /tokens 60 requests / minute / IP
GET /tokens/:mint 180 requests / minute / IP
POST /tokens/batch 20 requests / minute / IP, up to 500 mints each
GET /img/:mint 600 requests / minute / IP
Global fallback 300 requests / minute / IP across all routes

429 Too Many Requests response means your integration should back off, respect Retry-After when present, and avoid immediate retry loops.

The main production rule is simple: if you already have mint addresses, do not loop single-mint lookups. Use the batch endpoint.


Code example: search tokens from TypeScript

This example uses native fetch, so it works in modern Node.js runtimes and browser environments.

type CarbiumToken = {
  mint: string;
  symbol: string | null;
  name: string | null;
  decimals: number | null;
  logo_url: string | null;
  logo_status: "ok" | "unchecked" | "broken" | "missing" | string | null;
  is_token22: boolean;
};

const DATA_BASE = "https://tokens.carbium.io";

export async function searchCarbiumTokens(query: string): Promise<CarbiumToken[]> {
  const url = new URL("/tokens", DATA_BASE);
  url.searchParams.set("q", query);
  url.searchParams.set("limit", "5");
  url.searchParams.set("include_token22", "false");

  const response = await fetch(url);

  if (response.status === 429) {
    throw new Error("Carbium Data rate limit hit. Back off before retrying.");
  }

  if (!response.ok) {
    throw new Error(`Carbium Data search failed with HTTP ${response.status}`);
  }

  const data = await response.json() as { items?: CarbiumToken[] };
  return data.items ?? [];
}

const matches = await searchCarbiumTokens("USDC");
console.log(matches.map((token) => ({
  mint: token.mint,
  symbol: token.symbol,
  name: token.name,
  logoStatus: token.logo_status,
})));

Use search for discovery. Once the user selects a token, persist the mint.

Code example: batch hydrate a portfolio

Portfolios, watchlists, routing UIs, and agent memory often start with a set of mint addresses. Batch them.

type BatchResponse = {
  items: Array<{
    mint: string;
    symbol: string | null;
    name: string | null;
    decimals: number | null;
    logo_url: string | null;
    logo_status: string | null;
    is_token22: boolean;
  }>;
  unknown: string[];
};

const DATA_BASE = "https://tokens.carbium.io";

export async function hydrateMints(mints: string[]): Promise<BatchResponse> {
  if (mints.length > 500) {
    throw new Error("Carbium Data batch requests accept up to 500 mints.");
  }

  const response = await fetch(`${DATA_BASE}/tokens/batch`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ mints }),
  });

  if (response.status === 429) {
    throw new Error("Carbium Data rate limit hit. Cache results and retry later.");
  }

  if (!response.ok) {
    throw new Error(`Carbium Data batch failed with HTTP ${response.status}`);
  }

  return await response.json() as BatchResponse;
}

const result = await hydrateMints([
  "So11111111111111111111111111111111111111112",
  "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
]);

for (const token of result.items) {
  console.log(token.symbol, token.mint, token.logo_status);
}

if (result.unknown.length > 0) {
  console.warn("Unknown mints:", result.unknown);
}

One batch request can replace hundreds of single-mint calls. There is also a quieter property worth knowing: mints that come back in unknown are silently enqueued for on-chain enrichment. A freshly-minted SPL that the index has not seen yet may resolve on a subsequent batch without any action on your side, which makes Carbium Data tolerant of long-tail portfolio holdings rather than brittle around them.

Code example: token logos that never break

The cached image proxy is the most directly useful endpoint for frontend reliability. GET /img/:mint always returns 200 and an image. When the upstream gateway is unreachable or the mint has no published logo, the proxy serves an Unknown SVG instead of a 404. A token list rendering 200 image tags will not produce 200 broken-image icons, your <img>simply works.

<img
  src="https://tokens.carbium.io/img/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
  alt="USDC logo"
  width="32"
  height="32"
  loading="lazy"
/>

A live header check returned HTTP/2 200content-type: image/pngcache-control: public, max-age=2592000, immutable, and x-cb-cache: hit for the USDC mint route. Unknown mints are silently enqueued for on-chain resolution on first request, so a mint that returns the Unknown SVG today may serve a real logo tomorrow.

You can drop the onerror fallback handler that every Solana frontend tends to grow over time. The endpoint guarantees the 200, the cache headers give you 30-day browser/CDN caching, and the Unknown SVG keeps the visual grid intact when the underlying data is still resolving.

A token selector hook for production frontends

For a serious Solana frontend, the pattern that pulls the public API together is debounced search, abort-on-keystroke, hidden Token-2022 by default, 429 backoff with no retry loop, and a logo helper that leans on the always-200 property. As a React hook:

import { useEffect, useRef, useState } from "react";

type CarbiumToken = {
  mint: string;
  symbol: string | null;
  name: string | null;
  decimals: number | null;
  logo_url: string | null;
  logo_status: string | null;
  is_token22: boolean;
};

const DATA_BASE = "https://tokens.carbium.io";

export function useCarbiumTokenSearch(
  query: string,
  opts?: { limit?: number; includeToken22?: boolean }
) {
  const [results, setResults] = useState<CarbiumToken[]>([]);
  const [loading, setLoading] = useState(false);
  const abortRef = useRef<AbortController | null>(null);

  useEffect(() => {
    if (query.trim().length < 2) {
      setResults([]);
      return;
    }

    const timer = setTimeout(async () => {
      abortRef.current?.abort();
      const controller = new AbortController();
      abortRef.current = controller;

      const url = new URL("/tokens", DATA_BASE);
      url.searchParams.set("q", query);
      url.searchParams.set("limit", String(opts?.limit ?? 10));
      url.searchParams.set("include_token22", String(opts?.includeToken22 ?? false));

      setLoading(true);
      try {
        const r = await fetch(url, { signal: controller.signal });
        if (r.status === 429) return; // back off; do not retry in a tight loop
        if (!r.ok) return;
        const data = (await r.json()) as { items?: CarbiumToken[] };
        setResults(data.items ?? []);
      } catch (e) {
        if ((e as Error).name !== "AbortError") setResults([]);
      } finally {
        setLoading(false);
      }
    }, 250);

    return () => clearTimeout(timer);
  }, [query, opts?.limit, opts?.includeToken22]);

  return { results, loading };
}

export function carbiumLogoSrc(mint: string) {
  // Always-200 logo proxy. No onerror handler needed.
  return `${DATA_BASE}/img/${mint}`;
}

A few design choices in that hook are worth calling out. Debouncing for 250ms keeps GET /tokens well inside the 60/min budget even when a user types fast. Aborting in-flight requests on each new keystroke avoids both a stale-result race and wasted index work. Hiding Token-2022 by default is the right posture for almost every token selector, the vast majority of legitimate SPL trades happen on non-Token-2022 mints, and the spam pressure is concentrated on Token-2022. Treating a 429 as a soft no-op rather than retrying preserves the public rate-limit contract for everyone else. And carbiumLogoSrc is one line on purpose: because the proxy is always-200, there is no fallback ladder to build.

Cache the resolved tokens in component state, in a query client, or in your backend. Token identity does not change frequently, so a memory cache for the duration of a session is usually enough.


Where Carbium Data fits in the Carbium ecosystem

Carbium Data is not isolated from the rest of the stack. It sits next to Carbium RPC, gRPC streams, the Swap API, CQ1 routing, and the DEX.

That matters because token identity is a shared dependency. A wallet needs it before showing balances. A swap UI needs it before rendering token selectors. A trading bot may need it before deciding whether a mint should enter a strategy. An agent needs it before turning raw mint addresses into human-readable context.

Start with the public Token Index API when you need free Solana token metadata. Move into Carbium RPC, Swap API, or gRPC when the product needs transactions, routes, execution, or real-time streams.

The fastest path is the interactive endpoint page at tokens.carbium.io/endpoints, which has request widgets and code samples in curl, JavaScript, and Python next to every route. For broader platform context, start with Carbium docs and the Carbium Data overview.


Technical Reference & Resources

carbium-tokens · public API
Solana token-metadata API for the Carbium ecosystem. Search, lookup, batch resolve. Free + rate-limited. Try every endpoint live from this page.
carbium-tokens · API reference
Public REST API reference for the Carbium tokens registry. Search, lookup, 500-mint batch resolve, cached image proxy. Free, no auth, CORS-permissive.
The Carbium Ecosystem │ Unified Full-Stack Solana Infrastructure
Carbium unites DEX, API, RPC, Data, and Validator into one Swiss-engineered Solana infrastructure. Fast, reliable, and built for builders.
Carbium Services
High-performance Solana RPC, Swap API, and DEX infrastructure. Swiss-located, self-hosted, engineered for developers.
Skill.md - Carbium DeFi infrastructure Agentic Resources
Carbium Skill.md instructs your AI agents on how to operate effectively within agentic development environments. It serves as a structured guide for creating, managing, and extending skills that empower AI systems to act autonomously and collaboratively.