npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@peerlytics/sdk

v1.2.0

Published

Lightweight TypeScript SDK for the Peerlytics v1 API - ZKP2P P2P protocol analytics

Downloads

716

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:

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();

// 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 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, or currency. 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 owner, recipient, verifier, depositId, or status. 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, 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(...) and deleteKey(...) now take the opaque id (sha256 of the key, exposed on every ApiKeyInfo), not the raw key. Old code that passed the raw key still works for rotateKey because it's URL-encoded into the path; for deleteKey you must pass the id.
  • 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"],
});

Related

License

MIT