The marketplace model
GraveMarket indexes NFT collections across nine chains — solana-mainnet, ethereum-mainnet, polygon-mainnet, base-mainnet, bsc-mainnet, xrpl-mainnet, cronos-mainnet, abstract-mainnet and robinhood-mainnet.
That breadth is the source of most of the mistakes on this page. Code written against one chain reads as correct and quietly misbehaves on the others.
npm install @solanadeads/gravemarketimport { GraveyardClient } from '@solanadeads/gravemarket';
const gmk = new GraveyardClient(); // public API — no key, no origin registrationCollections
Addressable three ways — slug (solana-deads), on-chain address, or internal id. Prefer the slug: it is stable, readable, and what the marketplace URLs use.
const c = await gmk.collections.get('solana-deads');Related reads hang off the same identifier: items, activity, stats, traits, holders, offers, floor.
Items
An item is identified by token_address plus token_id.
token_id is null on Solana
Solana NFTs are one mint address each, so there is no token id. EVM collections have both. Code that builds a key as `${token_address}:${token_id}` produces Abc…:null on Solana — which collides across every unlisted item if you use it as a map key, and looks like a real id in a URL.
Key on token_address alone unless you know you are on an EVM chain.
Listings and prices
listing_price and listing_currency are nullable, and null means not listed — not free, and not zero.
const label = item.listing_price == null
? 'Not listed'
: `${item.listing_price} ${item.listing_currency}`;Never compare or sum prices across chains
listing_currency and floor_currency vary by chain — SOL, ETH, MATIC, XRP and more. A "cheapest item" sort across a multi-chain result set compares SOL to ETH and produces nonsense, and a summed "total volume" is a meaningless number.
Filter to one chain before ranking or aggregating, or convert deliberately with a rate you control. There is no implicit common unit.
This is the same shape as GraveMint's rule that a price is a resolved shape rather than a number — see the GraveMint read model.
Stats, and a caveat worth reading
const s = await gmk.collections.stats('solana-deads');Returns headline stats plus a daily series — and wash_trade_count_7d.
Volume figures are not wash-filtered
The stats endpoint reports wash-trade count separately rather than silently subtracting it. If you are surfacing volume to users, read that number and decide what to do with it. A headline volume that includes wash trades is not wrong data — it is unqualified data, and presenting it as clean is your call to get right.
Activity
Sales and listings, either for one collection or globally:
await gmk.collections.activity('solana-deads', { limit: 50 });
await gmk.activity.list({ limit: 50 });Pairs naturally with a GraveMint drop: "is this still minting, and what is it doing on secondary?" is one question, which is why the MCP server exposes both products.
Traits and rarity
const t = await gmk.collections.traits('solana-deads');Every trait and value with counts — what a rarity filter is built from. Items carry rarity_rank and rarity_score.
⚠️ Rarity rank is per collection and per methodology. Rank 1 in a 10,000-item collection and rank 1 in a 100-item one are not comparable, and the rank means nothing across collections. Show it beside the collection, never in a cross-collection list.
Pagination — cursors, and why a partial page lies
Every list endpoint is cursor-paginated and returns cursor and hasMore.
let cursor;
const all = [];
do {
const page = await gmk.collections.activity('solana-deads', { limit: 50, cursor });
all.push(...page.data);
cursor = page.cursor;
} while (cursor);The SDK also exposes listAll / activityAll async generators that drain for you.
A bare limit silently drops the rest
If you are computing a total, a holder count, a volume figure or a floor, drain the cursor. A first page produces a confidently wrong number with no error — which is worse than a failure, because nothing tells you to look.
How this differs from GraveMint
| GraveMint v1 | GraveMarket v1 | |
|---|---|---|
| purpose | mint a drop on your site | read marketplace state |
| credential | invite-only, origin-bound, collection-scoped | none |
| writes | prepare / execute | read-only |
| pagination | limit + offset | cursor |
| chains | Solana only | nine |
Next
- GraveMarket v1 API — every endpoint, with live consoles
- Collection page recipe — a working React page
- GraveMarket SDK — the typed client
