@peerlytics/sdk
v1.2.0
Published
Lightweight TypeScript SDK for the Peerlytics v1 API - ZKP2P P2P protocol analytics
Downloads
716
Maintainers
Readme
@peerlytics/sdk
TypeScript SDK for the Peerlytics v1 API -- analytics, explorer, and trading data for the ZKP2P P2P protocol on Base. Works in Node.js and browsers.
v1.0 (April 2026) is the first release that targets the Stripe-style v2 API: snake_case wire format, Unix-seconds timestamps, cursor pagination, and dedicated key/webhook endpoints. The SDK adapts the wire format back to a camelCase TS surface so existing TS code keeps working — see "Migration" at the end of this README for the details.
Agent bundle
If you are integrating through an agent (Claude Code, Cursor, etc.), start here:
- Developer portal: https://peerlytics.xyz/developers
- Drop-in skill: https://peerlytics.xyz/skills/peerlytics.md
- Short machine reference: https://peerlytics.xyz/llms.txt
- Full machine reference: https://peerlytics.xyz/llms-full.txt
- OpenAPI 3.1 JSON: https://peerlytics.xyz/api/openapi (also discoverable at
/.well-known/openapi.json) - Starters (Next.js / Vite / Telegram bot, plus runnable example scripts and an x402 agent): https://github.com/ADWilkinson/usdctofiat-peerlytics-starters
- Companion SDK for USDC-to-fiat deposits:
@usdctofiat/offramp(one Peerlytics API key authenticates both products)
Install
npm install @peerlytics/sdk
# or
bun add @peerlytics/sdk
# or
pnpm add @peerlytics/sdkQuick Start
import { Peerlytics } from "@peerlytics/sdk";
const client = new Peerlytics({ apiKey: "pk_live_..." });
// protocol summary
const summary = await client.getProtocolSummary();
// investor diligence view
const overview = await client.getProtocolOverview("all");
// live orderbook
const orderbook = await client.getOrderbook({ currency: "GBP" });
// deposit detail
const deposit = await client.getDeposit("8453_0x777...Ef_42");
// search (supports addresses, tx hashes, deposit IDs, ENS, .peer names)
const results = await client.search("vitalik.eth");x402 pay-per-request
Agents can skip API-key provisioning and pay each request directly with USDC on
Base. Pass a viem signer and the SDK handles the 402 Payment Required
challenge, creates the payment payload, retries with PAYMENT-SIGNATURE, and
keeps the normal camelCase response shape.
import { Peerlytics } from "@peerlytics/sdk";
import { privateKeyToAccount } from "viem/accounts";
const client = new Peerlytics({
auth: {
mode: "x402",
signer: privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`),
onPaymentSettled: (settlement) => {
console.log("paid", settlement.transaction);
},
},
});
const orderbook = await client.getOrderbook({ platform: "venmo" });
const summary = await client.getProtocolSummary({ range: "mtd" });For custom agent runtimes, provide paymentHandler instead of signer and
return the paid retry headers yourself:
const client = new Peerlytics({
auth: {
mode: "x402",
paymentHandler: async ({ paymentRequired }) => ({
"PAYMENT-SIGNATURE": await signWithYourAgentWallet(paymentRequired),
}),
},
});Response shapes
A few gotchas worth knowing up front — especially if you've been hitting the HTTP API directly:
{ data, ... }envelope. Every v1 endpoint wraps the payload in{ data: <payload>, ... }(siblings includemeta,linked, etc.). The SDK unwraps this for you — every method returns the innerdatadirectly. The legacysuccess: trueflag was retired with v2 — branch on HTTP status.getDepositsrequires at least one filter. The API rejects empty queries with a 400missing_filter. Provide at least one ofdepositor,delegate,platform, orcurrency. The SDK throws aValidationErrorbefore hitting the network so you get a clearer stack trace.getIntentsalso requires at least one filter. Provide at least one ofowner,recipient,verifier,depositId, orstatus. Same client-sideValidationErrorbehavior.getActivityreturns an envelope, not a raw array. The response is{ events, count, hasMore, limit, offset, nextCursor, filters }— iterate overresponse.events, notresponseitself. WhenhasMore=true, pass the returnednextCursorback ascursorto walk forward without offset drift.Currency codes vs hashes. On-chain, currencies are stored as
bytes32(either a keccak256 of the ISO code or an ASCII-padded encoding). EveryDepositMarketexposes both:currency(resolved, e.g."GBP") andcurrencyCode(raw hash). Entries insidedeposit.currencies[]also carry a resolvedcurrencyfield alongside the rawcurrencyCode. If you need to build your own mapping, callgetCurrencies()— each entry includes thecode,label,flag, and all hash forms.
Date filtering
Every analytics, listing, and history endpoint accepts a uniform set of
date-window parameters. Either pass from/to or a range shortcut:
// April 2026 leaderboard
await client.getLeaderboard({ from: "2026-04-01", to: "2026-05-01" });
// last 30 days
await client.getLeaderboard({ range: "last_30d" });
// month-to-date with prior-period comparison block
await client.getProtocolSummary({ range: "mtd", compare: "prior_period" });
// LP retro for a single maker, paginated across the window
await client.getMakerHistory("0xMaker...", {
range: "last_90d",
limit: 50,
offset: 0,
});
// Cash-App-only daily volume series
await client.getTimeseries({
entity: "volume",
groupBy: "platform",
platform: "cashapp",
granularity: "day",
from: "2026-04-01",
to: "2026-05-01",
});
// Vault rollup for the launch week
await client.getVaultsOverview({ from: "2026-04-22", to: "2026-04-29" });from and to accept ISO-8601 strings (2026-04-01T00:00:00Z) or unix-seconds
(numeric). Supported range shortcuts: last_7d, last_30d, last_90d,
last_365d, today, yesterday, mtd, qtd, ytd, all. Hard cap: 400 days.
Windowed responses include a window block — { from, to, fromIso, toIso, days,
range, computedFor } — so you can render the resolved bounds verbatim. The
cumulative path remains the default; passing any window param opts you into a
live indexer compute that costs more credits but never returns stale data.
For activity backfills, prefer the cursor over offset:
let cursor: string | null = null;
do {
const page = await client.getActivity({
range: "last_30d",
limit: 200,
cursor: cursor ?? undefined,
});
for (const event of page.events) handle(event);
cursor = page.nextCursor;
} while (cursor);Configuration
const client = new Peerlytics({
baseUrl: "https://peerlytics.xyz", // default
apiKey: "pk_live_...", // API key for authenticated access
headers: { "X-Trace": "abc" }, // custom headers
fetch: customFetch, // custom fetch implementation
});apiKey is kept for backwards compatibility. New code can use
auth: { mode: "api-key", apiKey: "pk_live_..." } or
auth: { mode: "x402", signer }.
API Reference
Analytics
| Method | Description |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| getProtocolSummary(params?) | Cumulative summary by default; pass from/to/range/compare for a windowed payload with optional period delta block. |
| getProtocolOverview(rangeOrOpts) | Legacy TimeRange enum reads cached buckets; OverviewParams (from/to or non-enum range shortcut) computes live. |
| getLeaderboard(params?) | Maker and taker leaderboards. Add from/to/range to recompute every aggregate from intents inside the window. |
| getTimeseries(params) | Hour or day buckets. Pass groupBy=platform\|currency\|maker\|verifier for multi-series + dimension filters. |
| getVaultsOverview(params?) | Cumulative vault overview by default; pass from/to/range for per-vault rollup with feesEarnedUsd, aumChangeUsd. |
Deposits
| Method | Description |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| getDeposits(filters) | Query deposits by depositor, delegate, platform, currency, status, OR a date window (from/to/range). sort=asc\|desc. |
| getDeposit(id, params?) | Deposit detail with intents, payment methods, and linked data |
Intents
| Method | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| getIntents(filters) | Query intents by owner, recipient, verifier, depositId, status, OR a date window (from/to/range). sort=asc\|desc. |
| getIntent(hash) | Intent detail with deposit and related intents |
Explorer
| Method | Description |
| ---------------------------------------- | ------------------------------------------------------------------- |
| getAddress(address, params?) | Address profile: intents, deposits, activity, stats |
| getMaker(address) | Maker portfolio: deposits, allocations, profit, APR |
| getTaker(address) | Taker portfolio: fills, tier, lock score, currency mix |
| getIntegrator(code, opts?) | ERC-8021 integrator rollup: volume, makers, top markets |
| getIntegratorIntents(code, opts?) | Recent intents attributed to an ERC-8021 integrator (up to 20) |
| getIntegratorReferralFees(code, opts?) | Recent treasury referral fees for an ERC-8021 integrator (up to 20) |
| getPlatform(platform, opts?) | Platform rollup: currencies, makers, takers, recent intents |
| getDelegate(address) | Delegate rollup: rate managers, delegated deposits, PnL |
| getVerifier(address, params?) | Verifier stats: intents, breakdown by currency/taker/maker |
| search(query, opts?) | Multi-type search (address, tx hash, deposit ID, ENS, .peer name) |
Recent integrator intents are available two ways:
const intents = await client.getIntegratorIntents("usdctofiat");
const integrator = await client.getIntegrator("usdctofiat");
const recentIntents = integrator.recentIntents ?? [];Use IntegratorData.recentIntents when you already need the full integrator rollup and want a single network call.
Recent referral fees (0.50% treasury take) follow the same pattern:
const fees = await client.getIntegratorReferralFees("usdctofiat");
const integrator = await client.getIntegrator("usdctofiat");
const recentFees = integrator.recentReferralFees ?? [];Each explorer entity also has a canonical URL at peerlytics.xyz/explorer/<entity>/<slug> — the SDK method and the page are siblings.
Orderbook & Market
| Method | Description |
| ------------------------- | ------------------------------------------------- |
| getOrderbook(opts?) | Live orderbook grouped by currency and rate level |
| getMarketSummary(opts?) | Rate statistics per (platform, currency) pair |
The orderbook API now prefers the indexer's denormalized OrderbookEntry projection when available, which keeps platform filters canonicalized (for example, zelle-* variants collapse into one Zelle surface) while preserving the same response shape for SDK consumers.
Vaults & Delegation
| Method | Description |
| ----------------------- | ------------------------------------------------------------- |
| getVaultsOverview() | All vaults: AUM, fees, adoption rate, daily snapshots |
| getVault(id, params?) | Vault detail: rate manager, delegations, oracle/floor configs |
Activity
| Method | Description |
| --------------------------------- | ------------------------------------------------------------------ |
| getActivity(filters?) | Live blockchain events (signals, fulfills, deposits, rate updates) |
| streamActivity(filters?, opts?) | SSE stream of the same events (requires api key, no x402) |
Time-series
| Method | Description |
| --------------------- | ---------------------------------------------------------------- |
| getTimeseries(opts) | Bucketed volume / deposits / intents by hour or day (Pro). |
History
| Method | Description |
| -------------------------- | --------------------------------------------------------------------- |
| getMakerHistory(address) | Maker historical stats, platform/currency breakdowns, recent activity |
| getTakerHistory(address) | Taker historical stats, lock score, tier progression |
Metadata
| Method | Description |
| ----------------- | ----------------------------------------------------------- |
| getCurrencies() | Supported fiat currencies with codes, labels, flags, hashes |
| getPlatforms() | Supported payment platforms with IDs, labels, method hashes |
Account Management
| Method | Description |
| --------------------- | ------------------------------------------------------------- |
| listKeys() | List API keys (each carries a stable opaque id) |
| createKey(label?) | Create a new API key (POST /account/keys) |
| rotateKey(id) | Rotate by opaque id (POST /account/keys/{id}/rotate) |
| deleteKey(id) | Delete by opaque id (DELETE /account/keys/{id}) |
| getCredits() | Credit balance and purchase history |
| createCheckout(pkg) | Create a credit checkout order (starter, growth, scale) |
Webhooks
| Method | Description |
| ----------------------------- | ------------------------------------------------ |
| listWebhooks() | List registered outbound webhooks |
| createWebhook({url,events}) | Register a new endpoint; secret returned once |
| updateWebhook(id, {status}) | Toggle status (POST — PATCH was retired with v2) |
| deleteWebhook(id) | Permanently remove an endpoint |
Canonical event names: deposit.created, intent.signaled, intent.fulfilled, deposit.rate_updated. Legacy aliases (intent.created, intent.filled, rate.updated) are still accepted on registration but normalized server-side.
Migration from v0.x
@peerlytics/[email protected] targets the v2 wire format (April 2026 redesign). The TS surface stays largely camelCase — the SDK adapts the snake_case wire format back for you — but a few things did change:
rotateKey(...)anddeleteKey(...)now take the opaqueid(sha256 of the key, exposed on everyApiKeyInfo), not the raw key. Old code that passed the raw key still works forrotateKeybecause it's URL-encoded into the path; fordeleteKeyyou must pass theid.- Timestamp fields (
createdAt,lastUsedAt,freeCreditsResetAt) are nownumber | string— the v2 server emits Unix seconds (integer); the v1-compat path still emits ISO strings. Always coerce explicitly if you touch the values. - New methods:
listWebhooks,createWebhook,updateWebhook,deleteWebhook. freeCreditsResetAtis seconds (was milliseconds in v1).
Error Handling
import { PeerlyticsError, RateLimitError, NotFoundError, ValidationError } from "@peerlytics/sdk";
try {
await client.getDeposit("missing");
} catch (err) {
if (err instanceof RateLimitError) {
console.log(`Retry after ${err.retryAfter}s`);
} else if (err instanceof NotFoundError) {
console.log("Not found");
} else if (err instanceof ValidationError) {
console.log(`Bad request: ${err.code} - ${err.message}`);
} else if (err instanceof PeerlyticsError) {
console.log(`HTTP ${err.status}: ${err.message}`);
}
}All errors extend PeerlyticsError with status, code, message, and details properties.
Filter Arrays
Array filter values are comma-joined automatically:
// deposits on revolut or wise, in GBP or EUR
await client.getDeposits({
platform: ["revolut", "wise"],
currency: ["GBP", "EUR"],
});
// activity for multiple deposit IDs
await client.getActivity({
depositId: ["42", "43", "44"],
type: ["intent_signaled", "intent_fulfilled"],
});Related
- Dashboard: https://peerlytics.xyz
- Developer docs: https://peerlytics.xyz/developers
- Agent docs: https://peerlytics.xyz/agents
- LLM docs: https://peerlytics.xyz/llms.txt
- Trading: https://usdctofiat.xyz
License
MIT
