TokensAPI

Search & Resolve

Risk-aware token search with tunable policy gates, and a resolve endpoint that answers with confidence or refuses.

All endpoints below use https://api.tokens.xyz/v2 with an x-api-key header; see Authentication. How the pipeline judges, the policy presets, and every reasons, warnings and suppressedBy code are on Risk-aware search.

Both routes share the per-key search provider budget (120 calls per 10 minutes by default) with GET /v2/lists/search-tokens; see Provider lookup budget.

GET /v2/search

Ranked results under a policy, with the suppressed candidates and the gate that removed each.

Auth: x-api-key, scope assets:read.

Query params

  • q (required): symbol, name or mint address, max 100 chars (invalid_query above that).
  • policy (optional): strict, default (default) or degen. See Policies and overrides.
  • Gate overrides (optional, each layered on the preset): minLiquidityUsd (number ≥ 0 or none), minAgeDays (integer ≥ 0 or none), minMarketScore (integer 0–100 or none; only gates tokens whose score is known), requireMarketData, suppressImpersonation, verifiedOnly (booleans). none, null and off disable a numeric gate; booleans also accept 1/0 and yes/no.
  • limit (1–50, default 10): ranked results only; suppressed is never truncated. Values above the max are clamped.
  • includeSuppressed (true | false, default true): false omits the suppressed array.

Out-of-range numbers and unknown policy values are 400; an invalid policy echoes the valid ids in details.policies.

Response

interface SearchResponse {
    query: string;
    interpretation: { intent: 'mint' | 'ticker' | 'name'; normalizedQuery: string; hadSuspiciousCharacters: boolean };
    policy: {
        id: 'strict' | 'default' | 'degen';
        gates: {
            minLiquidityUsd: number | null;
            requireMarketData: boolean;
            suppressImpersonation: boolean;
            minMarketScore: number | null;
            minAgeDays: number | null;
            requireRegistry: boolean;
        };
        overrides: string[]; // gate keys your request changed from the preset
    };
    policyVersion: string;
    scoringVersion: string;
    sources: { provider: 'ok' | 'degraded' | 'disabled'; db: 'ok' | 'degraded'; registry: 'ok' };
    latencyMs: number;
    results: Array<{
        mint: string;
        claims: { symbol: string | null; name: string | null; attestations: Array<{ code: string; detail: string }> };
        market: {
            price: number | null;
            liquidityUsd: number | null;
            volume24hUsd: number | null;
            marketCapUsd: number | null;
            priceChange24hPercent: number | null;
            holderCount: number | null;
            decimals: number | null;
            logoURI: string | null;
            dataAsOf: number | null; // ms epoch of the freshest market data
        };
        score: { total: number; components: Record<string, number> };
        reasons: string[];   // why it ranks: exact_symbol_match, curated_list_member, deep_liquidity, …
        warnings: string[];  // caution signals: possible_impersonation, new_token, unverified, weak_market_score, …
        badges: string[];    // curated:<list>, tier1|tier2|tier3, grade:A|B|C, advisory:<status>
        verified: boolean;   // a curated-registry variant exists for this mint
    }>;
    suppressed?: Array<{
        mint: string;
        symbol: string | null;
        name: string | null;
        liquidityUsd: number | null;
        suppressedBy: string[]; // gates that fired, e.g. gate_min_liquidity, gate_unverified
        warnings: string[];
    }>;
}

Example

# A swap UI: registry-attested tokens with real depth, no suppressed rows
curl -sS "$API_BASE_URL/v2/search?q=bonk&verifiedOnly=true&minLiquidityUsd=250000&includeSuppressed=false" \
  -H "x-api-key: $API_KEY"

# A research tool: everything, with the warnings doing the talking
curl -sS "$API_BASE_URL/v2/search?q=dog&policy=degen&limit=50" -H "x-api-key: $API_KEY"

Notes

  • policy.gates is the effective gate document and policy.overrides lists what you changed, so any result is reproducible from its own envelope.
  • Advisory gates (gate_advisory_blocked, gate_advisory_compromised) and tombstones fire under every policy and cannot be overridden.
  • The vocabularies for reasons, warnings, badges, claims.attestations and suppressedBy are listed under Explanation codes.
  • Cached for 30 s; identical queries inside the window do not count against the budget.

GET /v2/resolve

One answer with confidence, or an explicit refusal.

Auth: x-api-key, scope assets:read.

Query params

  • q (required), policy, and the same gate overrides as /v2/search.
  • limit is validated (limit=0 is a 400) but otherwise ignored: resolve always judges a fixed candidate window and returns at most 5 candidates.
  • includeSuppressed is not accepted here.

Response

Always HTTP 200 when the request is valid; read status.

interface ResolveResponse {
    query: string;
    interpretation: SearchResponse['interpretation'];
    policy: SearchResponse['policy'];
    policyVersion: string;
    scoringVersion: string;
    sources: SearchResponse['sources'];
    latencyMs: number;
    status: 'resolved' | 'ambiguous' | 'no_confident_match';
    // `resolved`: the single answer. `confidence` is 0–1 (exact mint paste → 1).
    best: (SearchResponse['results'][number] & { confidence: number }) | null;
    // `ambiguous`: credible contenders, best first (≤ 5). Empty otherwise.
    candidates: SearchResponse['results'];
}

Example

curl -sS "$API_BASE_URL/v2/resolve?q=USDC" -H "x-api-key: $API_KEY"

Notes

  • A mint-address query is a lookup, never fuzzy: the exact mint or no_confident_match.
  • resolved requires the best candidate to clear the policy's refusal bar and beat the runner-up by a margin; ambiguous means several credible claimers exist, so show candidates and let the user pick. See Resolve: confidence or refusal.
  • Each policy has its own refusal bar: strict refuses most readily, degen least.
  • Cached for 30 s.