Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8327599
Add design spec for x402 payment support (Node-first)
planadecu Jul 14, 2026
1f132bc
Incorporate Opus design-review findings into x402 spec
planadecu Jul 14, 2026
13d2382
Update x402 spec from live-endpoint probe + feedback
planadecu Jul 14, 2026
26e1708
Lock x402 client to @x402/fetch v2 + @x402/evm (Coinbase CDP-recommen…
planadecu Jul 14, 2026
dcb33c8
Add x402 payment support implementation plan
planadecu Jul 14, 2026
daf220d
Fix x402 plan: merge config + client task (green-at-commit)
planadecu Jul 14, 2026
84027e5
feat(config): x402 preset, base-url resolution, conditional apiKey/fetch
planadecu Jul 14, 2026
0cf8348
feat(errors): friendly 402 payment-required message
planadecu Jul 14, 2026
db553ab
chore(x402): optional peer deps, ./x402 subpath export, browser exclude
planadecu Jul 14, 2026
a81babf
feat(x402): createX402Fetch helper (subpath glassnode-api/x402)
planadecu Jul 14, 2026
5f09080
test(x402): opt-in testnet integration test + test:x402 script
planadecu Jul 14, 2026
76ce22b
docs(x402): README section, CHANGELOG, bump to 0.8.0
planadecu Jul 15, 2026
0d23828
docs(examples): x402 SUI active-addresses example (testnet/mainnet vi…
planadecu Jul 15, 2026
dcf1b00
fix(errors): make 402 message accurate for failed x402 payments
planadecu Jul 15, 2026
9d724bc
docs(examples): default x402 example to ETH, add X402_ASSET, skip uns…
planadecu Jul 15, 2026
bacc5d8
feat(errors): surface the server error-body message in GlassnodeApiError
planadecu Jul 15, 2026
8766dc8
docs(examples): x402 example default 24h, configurable metric/resolution
planadecu Jul 15, 2026
14be260
docs(changelog): note GlassnodeApiError error-body surfacing under 0.8.0
planadecu Jul 15, 2026
641cf44
fix(security): redact api_key in logged URLs
planadecu Jul 15, 2026
f65d083
docs(changelog): note api_key log redaction under 0.8.0
planadecu Jul 15, 2026
19a37bf
refactor(x402): don't hardcode the testnet endpoint; supply it via env
planadecu Jul 15, 2026
fab8ea3
fix(errors): don't append raw JSON body when it has no message/error
planadecu Jul 15, 2026
d948126
docs(examples): default x402 asset to BTC; drop X402_ASSET from .env.…
planadecu Jul 15, 2026
b645cff
docs(readme): document x402 config option and error-detail surfacing
planadecu Jul 15, 2026
a4c060f
Merge remote-tracking branch 'origin/main' into feat/x402-payment-sup…
planadecu Jul 15, 2026
72875ed
chore(deps): pin optional x402 peer deps to the v2 major (^2.18.0)
planadecu Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.8.0

- Add opt-in, Node-first **x402 payment support**: `x402: true` config preset (routes to
`https://x402.glassnode.com`) and a new `glassnode-api/x402` subpath export with
`createX402Fetch({ account, maxPaymentPerCall })`. The crypto stack (`@x402/fetch`, `@x402/evm`,
`viem`) is an optional peer dependency; the core package stays `zod`-only.
- `apiKey` is now optional when `x402` is enabled; `fetch` is required in that mode.
- Add a friendly `402` error message. Bulk metrics remain free-API only (unsupported over x402).
- `GlassnodeApiError` now surfaces the server's error-body message (e.g. "Resolution 1h is not
allowed") and exposes it on `.detail`, instead of only a generic status message.
- **Security:** redact the `api_key` query-param value in URLs passed to the optional `logger`
(previously the key could leak into log sinks).

## 0.7.7

- Fix transitive dev-dependency vulnerabilities via `pnpm.overrides`: `flatted` β‰₯3.4.2 (high), `serialize-javascript` β‰₯7.0.5, `picomatch` β‰₯4.0.4, `brace-expansion` β‰₯5.0.6 β€” `pnpm audit` now clean
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This document provides context for Claude when working with this project.

- `/src` - Source code
- `/src/types` - TypeScript type definitions (Zod schemas + inferred types)
- `/test` - Test files (Jest)
- `/test` - Test files (Vitest)
- `/examples` - Example usage patterns
- `/dist` - Compiled output (not checked into git)

Expand Down
70 changes: 61 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const btcPrice = await api.callMetric('/market/price_usd_close', { a: 'BTC' });
- [Error Handling](#error-handling)
- [Retries](#retries)
- [Bulk Metrics](#bulk-metrics)
- [Paid calls with x402](#paid-calls-with-x402)
- [Browser](#browser)
- [Examples](#examples)
- [Development](#development)
Expand Down Expand Up @@ -89,16 +90,17 @@ const data = await api.callMetric('/market/price_usd_close', {

`new GlassnodeAPI(config)`

| Option | Type | Default | Description |
| ------------ | ----------------------------------------------- | --------------------------- | ------------------------------------------------------- |
| `apiKey` | `string` | β€” (**required**) | Your Glassnode API key |
| `apiUrl` | `string` | `https://api.glassnode.com` | Base URL for the API |
| `logger` | `(message: string, ...args: unknown[]) => void` | β€” | Callback for debug logging (e.g. `console.log`) |
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation (custom headers, testing…) |
| `maxRetries` | `number` | `0` | Retries for retryable errors (`429`, `5xx`) |
| `retryDelay` | `number` | `1000` | Base delay in ms between retries (doubles each attempt) |
| Option | Type | Default | Description |
| ------------ | ----------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
| `apiKey` | `string` | β€” (required unless `x402`) | Your Glassnode API key |
| `apiUrl` | `string` | `https://api.glassnode.com` | Base URL for the API |
| `x402` | `boolean` | `false` | Route through the paid x402 endpoint (see [Paid calls with x402](#paid-calls-with-x402)) |
| `logger` | `(message: string, ...args: unknown[]) => void` | β€” | Callback for debug logging (e.g. `console.log`) |
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation (or an x402-wrapped fetch) |
| `maxRetries` | `number` | `0` | Retries for retryable errors (`429`, `5xx`) |
| `retryDelay` | `number` | `1000` | Base delay in ms between retries (doubles each attempt) |

The config is validated at construction time with Zod β€” an invalid config (e.g. an empty `apiKey`) throws immediately.
The config is validated at construction time with Zod β€” an invalid config (e.g. an empty `apiKey`) throws immediately. When `x402` is enabled, `apiKey` is optional but a payment-capable `fetch` is required. Failed requests throw a `GlassnodeApiError` whose message includes the server's error detail (also on `.detail`).

## Methods

Expand Down Expand Up @@ -156,6 +158,56 @@ const marketcaps = await api.callBulkMetric('/market/marketcap_usd');
// [{ t: 1609459200, bulk: [{ a: 'BTC', v: 600000000000 }, { a: 'ETH', v: 100000000000 }] }]
```

## Paid calls with x402

Glassnode also serves a **paid, per-call API over the [x402 protocol](https://x402.org)** at
`https://x402.glassnode.com` β€” no API key required, you pay per request in USDC on Base
($0.01/metadata call, $0.05/metric call). This is **Node-first** and opt-in: the crypto stack
(`@x402/fetch`, `@x402/evm`, `viem`) is an **optional peer dependency**, installed only if you use it.

```bash
pnpm add glassnode-api @x402/fetch @x402/evm viem
```

```typescript
import { GlassnodeAPI } from 'glassnode-api';
import { createX402Fetch } from 'glassnode-api/x402';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const api = new GlassnodeAPI({
x402: true, // β†’ https://x402.glassnode.com
fetch: await createX402Fetch({
account,
maxPaymentPerCall: '0.06', // USDC per-call ceiling (default)
}),
});

// Pays $0.05 USDC on Base, transparently:
const mvrv = await api.callMetric('/market/mvrv', { a: 'BTC', i: '24h' });
```

**`createX402Fetch(options)`**

| Option | Type | Default | Description |
| ------------------- | -------------- | ------------------ | -------------------------------- |
| `account` | `LocalAccount` | β€” (**required**) | viem account that signs payments |
| `maxPaymentPerCall` | `string` | `'0.06'` | Per-call USDC spend ceiling |
| `fetch` | `typeof fetch` | `globalThis.fetch` | Base fetch to wrap |

> **Spend safety:** `maxPaymentPerCall` caps a **single** request β€” it is **not** a cumulative budget, so
> an agent loop can still spend within that ceiling repeatedly. Use a **dedicated, funded-but-limited**
> wallet (never your primary key), and load the key from the environment β€” never hardcode it.

**Notes**

- **Bulk metrics are not available over x402** β€” `callBulkMetric()` only works against the free
`api.glassnode.com`.
- **Other endpoints:** target a non-default x402 endpoint (e.g. a testnet) by passing its URL as
`apiUrl`.
- **Browser** signing is not supported yet (planned).

## Browser

The library ships prebuilt UMD and ESM bundles, so it also runs directly in the browser
Expand Down
Loading
Loading