Metrics API

Read-only HTTP API for OBS browser sources, stream widgets, and public sites. No wallet login - only a per-project lb_live_ key. Full endpoint list: API reference.

Not building your own overlay? Use Stream Studio (OBS, no API key) or the dashboard Widget builder instead of polling this API by hand.
Skip the curl: use the typed SDK @latrinebot/sdk or the @latrinebot/cli for cron / CI.
import { LatrineClient } from "@latrinebot/sdk";
const client = new LatrineClient({ metricsKey: process.env.LATRINE_METRICS_KEY });
const snap = await client.metrics.get(process.env.PROJECT_ID);
console.log(snap.stats.totalClaimedSol, "SOL claimed");
Or from the shell:
npx @latrinebot/cli status <project-id>
Full setup in the Developers guide.

Get a key

  1. Open the dashboard and select your project.
  2. Scroll to Metrics API.
  3. Click Generate key. Copy the value immediately - it is shown once.

Keys look like lb_live_ plus 48 hex characters. Store them like a password. Revoke in the dashboard if leaked.

Base URL

https://api.latrinebot.com

Authentication

Send the key on every request:

X-Metrics-Key: lb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Wrong or missing keys return 401.

Token CA and ticker

The Metrics API exposes your token identity for overlays. Use these fields to print labels, links to Pump.fun / Dexscreener, or custom graphics.

FieldLocation in responseNotes
mint Top level Contract address (CA), base58. Same as settings.mint.
settings.tokenSymbol Inside settings Ticker without $ (example: LATRINE). Set by the server from DexScreener when the mint is saved in the dashboard. Empty string if not resolved yet.

There is no separate top-level ticker field on Metrics API responses. Always read settings.tokenSymbol.

Tiers and hold cycles

FieldLocationNotes
settings.eligibilityTiers settings Full tier table configured in the dashboard. Each row: mcUsd, minTokens, holdCycles.
activeTier Top level Last LIVE cycle: which tier was active and its holdCycles requirement. null if no LIVE cycle yet.
stats.lastTierMcUsd stats Same tier as activeTier.mcUsd when set.
stats.lastTierMinTokens stats Minimum balance floor for that tier, in whole tokens.
stats.lastTierHoldCycles stats Consecutive cycles required in-band before a wallet is paid (anti-sybil).
// Example label in your overlay
const sym = data.settings?.tokenSymbol;
const label = sym ? `$${sym}` : (data.mint ? data.mint.slice(0, 4) + "..." : "-");
document.getElementById("ticker").textContent = label;

To look up a symbol before creating a project (dashboard session, not metrics key): GET /api/projects/symbol?mint=CA - see API reference.

GET /api/v1/projects/:projectId/metrics

Returns a JSON snapshot for public overlays.

Stats policy: stats are mainnet LIVE counters only. Test or simulation counters are never included (missing values are 0). The mode field is informational; client dashboards always run LIVE on mainnet.

For the same 6 tiles shown on the token page (Airdrops sent, To holders (SOL est.), Holders paid, Eligible now, Tokens burned, Creator fees claimed), use publicStats from either: /api/public/realm/:id/live (token page) or /api/public/widgets/:widgetId/live (embeds). The realm live payload also includes poolSplit, holdFund (goal mode or Dex Vault), policyHistory, and perk flags - see Distribution transparency API.

Response fields

Example response (truncated)

{
  "ok": true,
  "projectId": "uuid",
  "running": true,
  "mode": "LIVE",
  "dataTrust": "live",
  "mint": "So11111111111111111111111111111111111111112",
  "settings": {
    "mint": "So11111111111111111111111111111111111111112",
    "tokenSymbol": "LATRINE",
    "cycleIntervalSec": 60,
    "minHolderBalance": 500000,
    "eligibilityTiers": [
      { "mcUsd": 0, "minTokens": 500000, "holdCycles": 5 },
      { "mcUsd": 50000, "minTokens": 450000, "holdCycles": 6 },
      { "mcUsd": 100000, "minTokens": 400000, "holdCycles": 8 },
      { "mcUsd": 250000, "minTokens": 300000, "holdCycles": 10 },
      { "mcUsd": 500000, "minTokens": 220000, "holdCycles": 12 },
      { "mcUsd": 1000000, "minTokens": 150000, "holdCycles": 15 },
      { "mcUsd": 2500000, "minTokens": 110000, "holdCycles": 22 },
      { "mcUsd": 5000000, "minTokens": 75000, "holdCycles": 30 },
      { "mcUsd": 10000000, "minTokens": 55000, "holdCycles": 40 },
      { "mcUsd": 15000000, "minTokens": 40000, "holdCycles": 50 },
      { "mcUsd": 25000000, "minTokens": 30000, "holdCycles": 60 },
      { "mcUsd": 50000000, "minTokens": 22000, "holdCycles": 80 },
      { "mcUsd": 100000000, "minTokens": 15000, "holdCycles": 100 }
    ]
  },
  "stats": {
    "cycles": 42,
    "totalClaimedSol": 1.25,
    "uniqueHoldersPaidCount": 88,
    "lastMarketCapUsd": 125000,
    "lastTierMcUsd": 0,
    "lastTierMinTokens": 500000,
    "lastTierHoldCycles": 5
  },
  "activeTier": {
    "mcUsd": 0,
    "minTokens": 500000,
    "holdCycles": 5
  },
  "lastEvent": { "tag": "OK", "body": "...", "at": "..." }
}
curl -s -H "X-Metrics-Key: YOUR_KEY" \
  "https://api.latrinebot.com/api/v1/projects/PROJECT_ID/metrics"

GET /api/v1/projects/:projectId/events

Recent public event lines for a scrolling feed. LIVE-cycle lines only (same filter as metrics).

curl -s -H "X-Metrics-Key: YOUR_KEY" \
  "https://api.latrinebot.com/api/v1/projects/PROJECT_ID/events?limit=20"

Example (browser overlay)

const API = "https://api.latrinebot.com";
const PROJECT_ID = "your-project-uuid";
const METRICS_KEY = "lb_live_...";

async function poll() {
  const res = await fetch(`${API}/api/v1/projects/${PROJECT_ID}/metrics`, {
    headers: { "X-Metrics-Key": METRICS_KEY },
  });
  if (!res.ok) throw new Error(await res.text());
  const data = await res.json();
  const sym = data.settings?.tokenSymbol;
  document.getElementById("ticker").textContent =
    sym ? `$${sym}` : "-";
  document.getElementById("status").textContent =
    data.lastEvent?.body ?? "Waiting...";
  document.getElementById("mc").textContent =
    data.stats?.lastMarketCapUsd != null
      ? `$${Math.round(data.stats.lastMarketCapUsd).toLocaleString()}`
      : "-";
  const tier = data.activeTier;
  document.getElementById("hold").textContent =
    tier?.holdCycles != null ? `${tier.holdCycles} cycles` : "-";
}
setInterval(poll, 5000);
poll();

Example (token page tiles via publicStats)

For the exact 6 token page tiles, call the public realm endpoint and read stats from its response. This does not require a metrics key.

const API = "https://api.latrinebot.com";
const PROJECT_ID = "your-project-uuid";

async function pollTokenTiles() {
  const res = await fetch(`${API}/api/public/realm/${PROJECT_ID}/live`, { cache: "no-store" });
  if (!res.ok) throw new Error(await res.text());
  const data = await res.json();
  const s = data.stats || {};
  document.getElementById("airdrops").textContent = String(s.airdropsCount ?? "-");
  document.getElementById("toHolders").textContent = (s.totalDistributedSol ?? 0).toFixed(3);
  document.getElementById("holders").textContent = String(s.uniqueHoldersPaid ?? "-");
  document.getElementById("eligible").textContent = s.lastEligibleCount != null ? String(s.lastEligibleCount) : "-";
  document.getElementById("burned").textContent = s.burnedDisplay || "-";
  document.getElementById("claimed").textContent = (s.totalClaimedSol ?? 0).toFixed(3);
}

Dashboard API vs Metrics API

Metrics APIDashboard API
AuthX-Metrics-KeyAuthorization: Bearer (wallet, Google, or X session)
Use inOBS, public sitesDashboard app only
Control botNoYes (start/stop/settings)
StatsLIVE mainnet onlyDashboard stats for project mode (client projects: LIVE)
PreflightNoGET .../preflight before start
Tickersettings.tokenSymbolproject.settings.tokenSymbol

See API reference for all dashboard routes.

Homepage live block (no key)

GET /api/public/latrine/live powers the $LATRINE block on latrinebot.com. Separate from per-project metrics keys; may include mint and ticker for the showcase token.

Holder perks on public pages (no key)

The token page (/realm/:projectId) and homepage perks panels use additional public routes (no metrics key, no dashboard session): reward-options, reward-preference, and social-claim under /api/public/realm/:id/... or /api/public/latrine/.... They do not expose dev secrets or raw stats buckets. Full payloads: Holder perks API.

Full API reference ? Test page ? Rotate keys if leaked