# x402 Pay-Per-Request

## When to use x402

Use x402 when the agent has a **Solana wallet with USDC** but no `BIRDEYE_API_KEY`.

| Condition | Use |
|---|---|
| Have `BIRDEYE_API_KEY` | Standard API — always prefer this |
| No API key, agent has Solana wallet + USDC | **x402** |
| Need bulk/batch endpoints (`multi_price` POST, `pair/overview/multiple`) | Standard API only — x402 does not support these |
| Need wallet endpoints (`/wallet/v2/*`, `/v1/wallet/*`) | Standard API only |
| Need WebSocket streams | Standard API only |
| Need non-Solana chain for token/holder/smart-money | Standard API only |

---

## Setup (one line)

```bash
npm install @x402/fetch @solana/web3.js
```

```typescript
import { withPaymentInterceptor } from '@x402/fetch';
import { Keypair } from '@solana/web3.js';

const keypair = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(process.env.SOLANA_PRIVATE_KEY!))
);

// Wrap fetch once — handles 402 → sign USDC → retry automatically
const fetch402 = withPaymentInterceptor(globalThis.fetch, { wallet: keypair });
```

`withPaymentInterceptor` intercepts every HTTP 402, signs a USDC payment on Solana, and retries. No other code changes needed.

---

## How to call endpoints

**Rule: prepend `/x402` to the path. All query params stay the same. No `X-API-KEY` header.**

```typescript
// Standard API key mode
const res = await fetch('https://public-api.birdeye.so/defi/price?address=So111...', {
  headers: { 'X-API-KEY': key, 'x-chain': 'solana', 'Accept': 'application/json' },
});

// x402 mode — same path, add /x402 prefix, drop X-API-KEY
const res = await fetch402('https://public-api.birdeye.so/x402/defi/price?address=So111...', {
  headers: { 'x-chain': 'solana', 'Accept': 'application/json' },
});

const json = await res.json();
// json.success, json.data — same response shape as standard API
```

`User-Agent` header is **not required** for x402 requests (the payment signature authenticates the call).

---

## Supported endpoints

All paths below are prefixed with `https://public-api.birdeye.so/x402`.

**Multi-chain** (pass `x-chain` header as usual):

| Group | Paths |
|---|---|
| Price | `/defi/price` · `/defi/multi_price` (GET only) · `/defi/history_price` · `/defi/historical_price_unix` · `/defi/price_volume/single` · `/defi/v3/price/stats/single` |
| OHLCV | `/defi/ohlcv` · `/defi/v3/ohlcv` · `/defi/v3/ohlcv/pair` · `/defi/ohlcv/base_quote` |
| Token | `/defi/token_overview` · `/defi/token_security` · `/defi/token_creation_info` · `/defi/v3/token/meta-data/single` · `/defi/v3/token/market-data` · `/defi/v3/token/trade-data/single` · `/defi/v3/token/list` · `/defi/v3/token/exit-liquidity` |
| Market | `/defi/token_trending` · `/defi/v2/tokens/new_listing` · `/defi/v3/search` · `/defi/v3/token/meme/list` · `/defi/v3/token/meme/detail/single` · `/defi/networks` |
| Pairs | `/defi/v2/markets` · `/defi/v3/pair/overview/single` |
| Trades | `/defi/v3/token/txs` · `/defi/txs/pair` · `/defi/txs/token` · `/defi/v3/txs` · `/defi/v3/txs/recent` |
| Trader | `/trader/gainers-losers` · `/trader/txs/seek_by_time` · `/defi/v2/tokens/top_traders` |

**Solana-only** (set `x-chain: solana`):

| Group | Paths |
|---|---|
| Holder | `/defi/v3/token/holder` · `/holder/v1/distribution` · `/token/v1/holder/batch` (POST) |
| Smart Money | `/smart-money/v1/token/list` |
| Transfers | `/token/v1/transfer` (POST) · `/token/v1/transfer/total` (POST) |

**Not supported via x402** (use standard API key):
- POST bulk endpoints: `/defi/v3/token/meta-data/multiple`, `/defi/v3/pair/overview/multiple`, etc.
- All `/wallet/v2/*` and `/v1/wallet/*` endpoints
- All WebSocket channels

---

## Payment details

| Property | Value |
|---|---|
| Network | Solana mainnet |
| Currency | USDC only (not SOL, not ETH) |
| Facilitator | Coinbase CDP |
| Pricing | Per-endpoint (set by Birdeye, varies by route) |
| Timeout | 60 seconds max |

Retrying the exact same request within the cache TTL is **free** — the server returns a cached response without charging again.

---

## Error handling

```typescript
try {
  const res = await fetch402(url, { headers: { 'x-chain': 'solana', 'Accept': 'application/json' } });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const json = await res.json() as any;
  if (!json.success) throw new Error(json.message);
  return json.data;
} catch (err: any) {
  if (err.message.includes('402')) {
    // Payment failed — likely insufficient USDC balance in wallet
    console.error('x402 payment failed — check USDC balance on Solana mainnet');
  }
  throw err;
}
```

| Error | Cause |
|---|---|
| 402 not resolved by library | Insufficient USDC in wallet |
| 402 loop | Facilitator rejected payment (network issue or wrong keypair format) |
| 400 | Same query param mistakes as standard API — see `api-reference.md` |
| 404 | Token not on requested chain (same as standard API) |

---

## References

- [Birdeye x402 docs](https://docs.birdeye.so/reference/x402)
- `examples/x402/pay-per-request.ts` — runnable example
- `resources/api-reference.md` — full endpoint param reference (same params apply for x402)
