# GraveMarket SDK



**`@solanadeads/gravemarket`** · version **0.3.1** · install with `npm i @solanadeads/gravemarket`

Guide adapted from the published README for clarity. Code examples are preserved.

Every method, with its signature and types: [method reference](https://docs.deads.io/sdk/gravemarket-reference.md).




Read-only TypeScript SDK for the [GraveMarket](https://gravemarket.io) marketplace public API. Wraps public `GET` endpoints and returns fully typed responses.

- **Zero runtime dependencies.** Native `fetch`. Works in Node 18+, browsers, Cloudflare Workers, Deno, and Bun.
- **Typed.** Full `.d.ts` for every method and response.
- **Cursor pagination.** Async iterator helpers for every paged endpoint.

## Install

```bash
npm install @solanadeads/gravemarket
# or
pnpm add @solanadeads/gravemarket
```

Requires **Node 18+** (uses native `fetch` and `AbortController`).

## Quick start

```ts
import { GravemarketClient } from '@solanadeads/gravemarket';

const client = new GravemarketClient();

// Browse collections — reference by slug or contract address
const page = await client.collections.list({ limit: 25, chain: 'solana-mainnet' });
const first = page.data[0];
console.log(first.name, first.slug, first.contract_address, first.floor_price);

// Fetch an NFT — by mint address (Solana) or `contract:tokenId` (EVM)
const item = await client.items.get('<mint-address>');

// Just the rarity rank/score/tier
const rarity = await client.items.rarity('<mint-address>');
// { rank: 12, score: 184.2, tier: 'legendary', total_supply: 5000 }
```

> Legacy `Graveyard*`-prefixed exports (e.g. `GraveyardClient`, `GraveyardApiError`) remain available as aliases for backward compatibility with existing integrations.

## Options

```ts
new GravemarketClient({
  apiKey: 'gm_...',                          // recommended — get one at https://gravemarket.io/developers
  baseUrl: 'https://api.deads.io',           // default (api.solanadeads.com serves the same API)
  timeoutMs: 30_000,                         // default 30s
  fetch: customFetch,                        // optional — defaults to globalThis.fetch
  headers: { 'X-My-Header': '1' },           // optional default headers
});
```

## API keys

The SDK works anonymously by default, but **applying for an API key is recommended — anonymous access will be disabled in the future.** Generate one at [gravemarket.io/developers](https://gravemarket.io/developers) and pass it via the `apiKey` option. The SDK sends it as the `X-API-Key` header on every request.

When the API responds with HTTP 429, the SDK throws a `GravemarketRateLimitError` exposing `retryAfterMs`, `limit`, and `remaining`:

```ts
import { GravemarketRateLimitError } from '@solanadeads/gravemarket';

try {
  await client.collections.list();
} catch (err) {
  if (err instanceof GravemarketRateLimitError) {
    await new Promise((r) => setTimeout(r, err.retryAfterMs ?? 1000));
    // retry
  }
}
```

## Namespaces

| Namespace | Methods |
|---|---|
| `client.collections` | `meta`, `launches`, `sparklines`, `list`, `get`, `items`, `activity`, `stats`, `traits`, `holders`, `offers`, `traitFloors`, `traitPricingSummary`, `crossChain`, `analytics`, `floor`, `listAll`, `activityAll` |
| `client.items` | `get`, `activity`, `offers`, `listings`, `traits`, `neighbors`, `priceHistory`, `rarity` |
| `client.orders` | `aggregatedListings`, `bestPrice`, `aggregatedFloor`, `offersSummary`, `onchainStatus` |
| `client.activity` | `list`, `listAll` |
| `client.search` | `query`, `traits` |
| `client.analytics` | `platform`, `collection`, `compare`, `heatmap`, `flow` |
| `client.smartMoney` | `feed`, `leaderboard`, `inflows` |
| `client.walletAnalytics` | `pnl`, `pnlLots` |
| `client.trust` | `collections`, `wallet` |
| `client.recommendations` | `similar` |
| `client.platform` | `tokenPrices`, `price`, `tokenPrice`, `fees`, `fee`, `chains`, `branding`, `analyticsConfig`, `status` |
| `client.affiliates` | `config`, `leaderboard` |
| `client.config` | `client` |

### Smart Money

Track what the top wallets are doing right now.

```ts
// Live whale activity feed (cursor-paginated)
const { feed, next_cursor } = await client.smartMoney.feed({ chain: 'solana-mainnet', limit: 25 });
for (const row of feed) {
  console.log(row.side, row.collection?.name, row.price_usd, row.from_address);
}

// Top whales by metric over period
const { rankings } = await client.smartMoney.leaderboard({ metric: 'realized_pnl', period: '30d' });

// Collections with the most net whale inflow
const { inflows } = await client.smartMoney.inflows({ period: '7d' });
```

### Wallet PnL

Per-wallet profit/loss with FIFO lot tracking.

```ts
const summary = await client.walletAnalytics.pnl(wallet, { period: '30d' });
console.log(summary.realized_pnl_usd, summary.unrealized_pnl_usd, summary.win_rate_pct);

// Paginate the underlying lots (open + closed)
const lots = await client.walletAnalytics.pnlLots(wallet, { closed_only: true, limit: 100 });
```

### Trust

Wash-trade fingerprints for collections and wallets.

```ts
// Top trustworthy / suspicious collections
const cleanest = await client.trust.collections({ sort: 'cleanest', period: '7d', limit: 20 });
const sus     = await client.trust.collections({ sort: 'most_suspicious', period: '30d' });

// Wallet wash-rate (returns null pct for wallets under 10 trades)
const t = await client.trust.wallet(wallet);
console.log(t.wash_pct_30d, t.in_cluster);
```

### Adjusted volume toggle

Platform / collection analytics accept `volume: 'reported' | 'adjusted'` (default `adjusted`). Adjusted volume excludes sales flagged by the wash-trade detector.

```ts
const reported = await client.analytics.platform({ period: '7d', volume: 'reported' });
const adjusted = await client.analytics.platform({ period: '7d' }); // adjusted by default
```

### Collection-level analytics

```ts
// Daily series + stats snapshot + holder history for a collection
const a = await client.analytics.collection('apes-collection', { period: '30d' });

// Side-by-side comparison of up to 4 collections
const cmp = await client.analytics.compare({ collection_ids: 'a,b,c', period: '7d' });

// 7×24 liquidity heatmap (UTC) per chain × window
const h = await client.analytics.heatmap({ chain: 'solana-mainnet', metric: 'volume', days: 30 });

// Net flow / buyer-seller mix / hold-time histogram for a collection
const f = await client.analytics.flow('apes-collection-uuid', { period: '7d' });
```

## Identifying entities

Reference entities by their public identifiers:

| Entity | Identifier |
|---|---|
| Collection | `slug` or `contract_address` |
| Item (NFT) | `token_address` (Solana mint), or `contract_address:token_id` (EVM) |
| Wallet | `wallet_address` |
| Affiliate | `affiliate_code` |
| Trait | `trait_type` + `trait_value` |

## Pagination

Cursor-paginated endpoints return `{ data, cursor, hasMore }`. Several namespaces expose `*All` methods returning async iterators:

```ts
for await (const batch of client.collections.listAll({ chain: 'solana-mainnet' })) {
  for (const c of batch) console.log(c.slug, c.floor_price);
}
```

Or use `paginate()` directly:

```ts
import { paginate } from '@solanadeads/gravemarket';

for await (const batch of paginate((cursor) =>
  client.activity.list({ cursor, type: 'sale', limit: 100 }),
)) {
  // ...
}
```

## Error handling

```ts
import { GravemarketApiError, GravemarketTimeoutError } from '@solanadeads/gravemarket';

try {
  await client.items.get('does-not-exist');
} catch (err) {
  if (err instanceof GravemarketTimeoutError) {
    // request timed out
  } else if (err instanceof GravemarketApiError) {
    console.error(err.code, err.status, err.message);
  } else {
    throw err;
  }
}
```

The SDK auto-unwraps the `{ success, data }` envelope. If the server returns `success: false`, a `GravemarketApiError` is thrown with the server's `code`, `message` and (when it sends one) `details`.

### One contract, several chains

A CREATE2 contract can live at the same address on more than one chain, so an EVM
`contract:token_id` can name more than one NFT. The API never guesses: it answers `409`
`AMBIGUOUS_ITEM`, with the chains in `details.candidates`. Pass the one you mean:

```ts
const WANTED_CHAIN = 'robinhood-mainnet'; // the chain YOUR app means — never "the first one"

try {
  await client.items.get('0xabc…:7');
} catch (err) {
  if (err instanceof GravemarketApiError && err.code === 'AMBIGUOUS_ITEM') {
    const { candidates } = err.details as { candidates: { chain: string }[] };
    if (candidates.some((c) => c.chain === WANTED_CHAIN)) {
      await client.items.get('0xabc…:7', { chain: WANTED_CHAIN });
    }
  }
}
```

If you already know the chain, pass `{ chain }` up front and skip the round trip.

Every `client.items` method and `client.recommendations.similar` accept `chain`.

## License

MIT

---
Source: https://docs.deads.io/sdk/gravemarket
Markdown: https://docs.deads.io/sdk/gravemarket.md
