Lists
Discover, read, compose, and manage community token lists.
All endpoints on this page are Platform API endpoints authenticated with x-api-key.
Every endpoint requires only the default assets:read scope. Writes are bound to the
API key's project: the key that creates a list owns it, and only keys from that project
can modify it. Anyone can read any published list.
GET /v2/lists
The list catalog: every curated list plus every published community list, metadata only.
- Scope:
assets:read - Query params:
limit(1–500, default 100),offset(community lists only; curated lists always lead page one)
interface ListsResponse {
lists: Array<{
slug: string;
name: string;
description: string | null; // curated lists only; null for community lists
curated: boolean;
owner: { name: string } | { projectId: string };
tokenCount: number;
updatedAt: number | null; // unix ms
}>;
total: number;
}GET /v2/lists/{slug}
One list with hydrated tokens. Curated slugs serve the effective curated membership (registry ∪ admin-added); community slugs serve published lists only (drafts and archived lists 404).
- Scope:
assets:read - Query params:
limit(1–2000, default 500),offset
// Same shape as the v1 variant `advisory` field; see
// /docs/v1/endpoints/asset-by-id#advisories for semantics.
type VariantAdvisory = {
status: 'caution' | 'compromised' | 'blocked';
reason: string;
url: string | null;
since: number; // unix ms
} | null;
interface V2ListToken {
mint: string;
symbol: string | null;
name: string | null;
decimals: number | null;
logoURI: string | null;
verified: boolean; // an active registry variant exists for this mint
rank: number;
advisory: VariantAdvisory; // always present; null when not flagged
note?: string;
addedAt?: number;
}
interface ListDetailResponse {
slug: string;
name: string;
description: string | null; // curated lists only; null for community lists
curated: boolean;
owner: { name: string } | { projectId: string };
tokenCount: number;
updatedAt: number | null;
tokens: V2ListToken[];
}Mints flagged blocked are omitted from tokens during hydration (rank stays dense) and
tokenCount reflects the visible tokens, not the raw membership. caution and
compromised members are returned with advisory populated — treat compromised as
do-not-trade in your own UI.
GET /v2/lists/tokens
The composition call — the one request a consuming app makes for its subscribed lists. Returns the union of the named lists, deduped by mint, each token annotated with which of the requested lists contains it.
- Scope:
assets:read - Query params:
lists(required): comma-separated slugs, max 10. Curated and community slugs mix freely. There is no "all lists" value — apps must name the curators they trust.limit(1–2000, default 500),offset— pagination over the deduped union.
Unknown, draft, or archived slugs are reported in notFound instead of failing the call.
interface ComposeResponse {
lists: ListsResponse['lists'];
notFound: string[];
total: number; // size of the deduped union
tokens: Array<
Omit<V2ListToken, 'note' | 'addedAt'> & {
lists: string[]; // requested slugs containing this mint
}
>;
}blocked mints are omitted here too, so total counts visible tokens only.
Managing lists
Any API key can create lists — project ownership is the security boundary: every key
on a project (regardless of read scopes) may manage that project's lists, and keys
from other projects get 403 on writes. Caps (env-tunable server-side):
250 mints per batch call, 5000 members per list (400 with
details.code: "list_full" beyond it), 100 lists per project (400 with
details.code: "project_lists_limit"), and ~50 provider lookups per batch call
for mints unknown to the registry/token index — over-budget unknowns fail individually
as unknown_mint and can be retried in a later batch. Batch adds and curator search
additionally draw from a per-key provider budget (30 batch calls / 120 searches per
10 minutes by default) and return 429 when it is exhausted.
Recommended automation flow: create the list, load it with one (or a few) batch
calls, then keep it fresh with individual PUT/DELETE /members/{mint} calls as your
own filters decide.
POST /v2/lists
Create a list.
- Body:
{ slug, name, status? }slug: globally unique,^[a-z][a-z0-9-]{2,62}$. Curated ids and route words (all,lists,curated,tokens,search-tokens) are reserved. Prefix by convention:ownership-core.status:draft,unlisted, orpublished(defaultpublished).unlistedhides the list from the catalog andinListsannotations while the directGET /v2/lists/{slug}stays readable by anyone with the link.
- Errors (
400withdetails.code):invalid_slug,reserved_slug,slug_conflict.
PATCH /v2/lists/{slug}
Update name, slug (rename — the old path stops resolving and is held for this owner
for a 30-day window), or status (draft | unlisted | published | archived).
DELETE /v2/lists/{slug}
Permanently deletes the list and its members, releasing the slug for anyone to claim again.
Irreversible — PATCH { status: 'archived' } is the reversible hide-it option.
PUT /v2/lists/{slug}/members/{mint}
Add or update a member. Body (optional): { rank?, note? }. Omitted rank appends to the end.
The mint must resolve somewhere: canonical registry (→ verified: true) → our token index →
live provider lookup (metadata snapshotted onto the member). A mint unknown everywhere is
rejected with 400 and details.code: "unknown_mint"; a malformed address with
details.code: "invalid_mint".
DELETE /v2/lists/{slug}/members/{mint}
Remove a member. 404 when the mint was not in the list.
POST /v2/lists/{slug}/members
Bulk add: { mints: string[] }, max 250 per call, deduped. Mints are resolved in
bulk (registry → token index → budgeted provider lookups). Per-mint failures are
reported without failing the batch:
interface BatchAddResponse {
added: Array<{ mint: string; verified: boolean; snapshot: object | null }>;
failed: Array<{ mint: string; error: 'invalid_mint' | 'unknown_mint' | 'list_full' }>;
}GET /v2/lists/search-tokens
Curator-assist search: decision support for a list owner choosing which mint to add. This is not a public ranking — it defaults to the strict policy and always returns the suppressed set with reasons so the curator sees what was filtered and why.
- Scope:
assets:read - Query params:
q(required): symbol, name, or mint address (max 100 chars)policy(optional):strict(default),default, ordegen— bundles of hard gates + score weights.strictsuppresses low-liquidity/day-old tokens and likely impersonators;degenshows everything and lets the warnings speak.limit(1–50, default 10)
interface SearchTokensResponse {
query: string;
interpretation: { intent: 'mint' | 'ticker' | 'name'; normalizedQuery: string };
policy: string;
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: object[] };
market: object; // price, liquidityUsd, volume24hUsd, marketCapUsd, holderCount, …
score: { total: number; components: object };
reasons: string[]; // e.g. exact_symbol_match, curated_list_member, deep_liquidity
warnings: string[]; // e.g. possible_impersonation, suspicious_characters, new_token, unverified, advisory_caution
badges: string[]; // e.g. curated:majors, grade:A
verified: boolean;
inLists: string[]; // curated + community lists already containing this mint
}>;
suppressed: Array<{
mint: string;
symbol: string | null;
name: string | null;
liquidityUsd: number | null;
suppressedBy: string[]; // which policy gates fired, e.g. gate_min_liquidity, gate_advisory_blocked
warnings: string[];
}>;
}Reading the output
reasonsexplain rank: match quality (mint_match,exact_symbol_match, …), curation (curated_list_member,registry_variant), and market context (market_leader,deep_liquidity,high_activity,established_token).warningsare the curator's caution signals:possible_impersonationandsymbol_collision(claims a symbol a protected token holds),suspicious_characters(homoglyphs/invisible chars in the claim),new_token(younger than 7 days),low_liquidity,no_market_data,unverified(not in the registry),stale_data,high_bot_volume, andadvisory_caution(an operator has attached acautionadvisory to the mint; the result stays ranked).suppressedlists candidates a gate removed (min_liquidity,impersonation,tombstoned, …). Re-run withpolicy=degento see them ranked instead.- Advisory gates fire under every policy, including
degen:gate_advisory_blockedandgate_advisory_compromisedsuppress mints carrying ablockedorcompromisedadvisory. They are still returned undersuppressed[]with the code, because this endpoint exists to build lists and a curator should see that the mint was rejected and why. See Asset by ID → Advisories.