npm.io
1.3.5 • Published yesterday

@peerlytics/sdk

Licence
MIT
Version
1.3.5
Deps
4
Size
832 kB
Vulns
0
Weekly
541

@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:

The SDK also exports a typed resource map and prompt builder for developer portals, internal tools, and coding agents:

import {
  PEERLYTICS_DEVELOPER_RESOURCES,
  getPeerlyticsAgentPrompt,
  getPeerlyticsDeveloperResources,
} from "@peerlytics/sdk";

PEERLYTICS_DEVELOPER_RESOURCES.authModes; // ["api-key", "x402"]
PEERLYTICS_DEVELOPER_RESOURCES.links.wellKnownOpenApi; // https://peerlytics.xyz/.well-known/openapi.json
PEERLYTICS_DEVELOPER_RESOURCES.headers.webhookSignature; // X-Peerlytics-Signature
PEERLYTICS_DEVELOPER_RESOURCES.upstreamSourceTruths.map((source) => source.label);
// ["@zkp2p/indexer-schema 0.5.0", "zkp2p-indexer", ...]

const webhookPlaybook = getPeerlyticsDeveloperResources("webhooks");
const agentPrompt = getPeerlyticsAgentPrompt("market-maker");

Use upstreamSourceTruths before generating raw protocol fields, GraphQL queries, proof payload mappings, or client behavior. It anchors agent work to the current indexer schema, zkp2p-indexer projections, Curator API, zkp2p-clients, and attestation service.

Install

npm install @peerlytics/sdk
# or
bun add @peerlytics/sdk
# or
pnpm add @peerlytics/sdk

Quick Start

import { Peerlytics } from "@peerlytics/sdk";

const client = new Peerlytics({ apiKey: "pk_live_..." });

// protocol summary
const summary = await client.getProtocolSummary();

// protocol health 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 include meta, linked, etc.). The SDK unwraps this for you — every method returns the inner data directly. The legacy success: true flag was retired with v2 — branch on HTTP status.

  • getDeposits requires at least one filter. The API rejects empty queries with a 400 missing_filter. Provide at least one of depositor, delegate, platform, currency, or a date window (from, to, or range). The SDK throws a ValidationError before hitting the network so you get a clearer stack trace.

  • getIntents also requires at least one filter. Provide at least one of taker, recipient, verifier, depositId, status, or a date window (from, to, or range). The legacy owner alias is still accepted for older callers. Same client-side ValidationError behavior.

  • getActivity returns an envelope, not a raw array. The response is { events, count, hasMore, limit, offset, nextCursor, filters } — iterate over response.events, not response itself. When hasMore=true, pass the returned nextCursor back as cursor to 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). Every DepositMarket exposes both: currency (resolved, e.g. "GBP") and currencyCode (raw hash). Entries inside deposit.currencies[] also carry a resolved currency field alongside the raw currencyCode. If you need to build your own mapping, call getCurrencies() — each entry includes the code, 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, OR a date window (from/to/range). Add status to narrow those results. sort=asc|desc.
getDeposit(id, params?) Deposit detail with intents, payment methods, and linked data
Intents
Method Description
getIntents(filters) Query intents by taker, recipient, verifier, depositId, status, OR a date window (from/to/range). owner remains a legacy alias. 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. The default book is public liquidity only; pass taker to inspect deposits whitelisted for a specific buyer wallet. Generic take links are null for whitelist-hooked liquidity.

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. Legacy aliases (intent.created, intent.filled) are still accepted on registration but normalized server-side. Rate updates remain activity-feed events, not outbound webhook fanout.

Migration from v0.x

@peerlytics/sdk@1.0.0 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(...) and deleteKey(...) now take the opaque id (sha256 of the key, exposed on every ApiKeyInfo), not the raw key. Full secrets are returned only from createKey() and rotateKey(), never from listKeys().
  • Timestamp fields (createdAt, lastUsedAt, freeCreditsResetAt) are now number | 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.
  • freeCreditsResetAt is 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"],
});

License

MIT

Keywords