MM Flow

mmflow SDK · TypeScript

TypeScript SDK

Single-file, zero-dep TypeScript client for the mmflow Open API. Fully typed across all 18 endpoints. Works in browser, Node 18+, Deno, Bun. ~6 kB minified. MIT licensed.

Install

zero deps

Copy file into your project

Drop mmflow.ts anywhere in your codebase. No npm dep, no bundler config.

curl -O https://mmflow.ai/sdk/mmflow.ts

Import directly from mmflow.ai

Deno / Bun / browser: import without installing.

import { MmflowClient } from "https://mmflow.ai/sdk/mmflow.ts";

Quickstart

import { MmflowClient } from "./mmflow";

const mm = new MmflowClient();          // default base https://mmflow.ai/api/v1
// const mm = new MmflowClient({ baseUrl: "http://localhost:3000/api/v1" });

// REST
const { data: oi } = await mm.perpsOi({ coins: ["BTC", "ETH"] });

// SSE
const { events, abort } = mm.streamWhales({ minUsd: 250_000 });
for await (const ev of events) {
  if (ev.type === "whale") console.log(ev.data.coin, ev.data.notionalUsd);
  if (ev.type === "bye") break;
}

Recipes

one snippet per resource family

Perps OI snapshot

Cross-venue open interest for two coins.

import { MmflowClient } from "./mmflow";

const mm = new MmflowClient();
const { data } = await mm.perpsOi({ coins: ["BTC", "ETH"] });
for (const row of data) {
  console.log(row.coin, row.venue, row.oiUsd);
}

Funding + liquidations

Concurrent fetches across two endpoints.

const [funding, liqs] = await Promise.all([
  mm.perpsFunding({ coins: ["BTC", "ETH"] }),
  mm.perpsLiquidations({ coins: ["BTC", "ETH"] }),
]);
const fundByCoin = new Map(funding.data.map((r) => [r.coin, r.rate]));
for (const row of liqs.data) {
  const rate = fundByCoin.get(row.coin) ?? 0;
  console.log(row.coin, "liq24h=", row.long24hUsd + row.short24hUsd, "funding=", rate);
}

Options GEX surface

BTC dealer gamma exposure — call wall, put wall, gamma flip.

const { data: gex } = await mm.optionsGex({ underlying: "BTC" });
console.log("spot", gex.spot);
console.log("totalGex", gex.totalGex);
console.log("callWall", gex.callWall);
console.log("putWall", gex.putWall);
console.log("gammaFlip", gex.gammaFlip);

IV smile + term structure

Full vol surface, ready to fit your own smile model.

const { data: skew } = await mm.optionsSkew({ underlying: "BTC" });
for (const exp of skew.termStructure) {
  console.log(exp.expiryLabel, exp.dteDays, "ATM IV=", exp.atmIv);
}
const firstExpiry = Object.keys(skew.smileByExpiry)[0];
const smile = skew.smileByExpiry[firstExpiry];
console.log("strikes in smile:", smile.length);

Cross-venue spot ticker

BTC + ETH price across Coinbase, Binance, Bitfinex, Kraken.

const { data: spot } = await mm.marketsSpot({ coins: ["BTC", "ETH"] });
const byCoin = new Map<string, Record<string, number>>();
for (const row of spot) {
  const cur = byCoin.get(row.coin) ?? {};
  cur[row.venue] = row.price;
  byCoin.set(row.coin, cur);
}
console.log(Object.fromEntries(byCoin));

Flow Pulse REST + live signals

Fetch the compact Flow Pulse read and subscribe to flow signals.

const res = await fetch("/api/v1/flows/summary?coins=BTC");
const { data, meta } = await res.json();

console.log(data.rows[0]?.flowPressureScore, meta.partial);

import { MmflowSocket } from "@mmflow/sdk";
const sock = new MmflowSocket({ apiKey: MMFLOW_KEY });
sock.subscribe("flowSignals", "BTC", (payload) => console.log(payload));
sock.connect();

Polymarket events + book

Top 5 events, then the YES book of the most-liquid one.

const { data: events } = await mm.polymarketEvents({ limit: 5 });
console.log("top events:", events.length);
const top = events[0];
// Each event has yesTokenId+noTokenId via /polymarket/events, but the
// returned 'yesBestBid' field exposes the YES side mid already — to
// load the full ladder you also need the token id which the events
// endpoint includes if you cross-reference via slug or conditionId.
// For demo, use the conditionId as the proxy here:
console.log(top.question, "yes=", top.yesPrice, "no=", top.noPrice);

On-chain valuation

BTC MVRV-Z, NUPL, Puell Multiple in one call.

const { data: v } = await mm.onchainValuation();
console.log("MVRV-Z:", v.mvrvZScore);
console.log("NUPL:", v.nupl);
console.log("Puell:", v.puellMultiple);
console.log("as of:", v.asOfDate);

Live whale tape (SSE)

Stream HL whale prints over $250k. Type-narrowed events.

const { events, abort } = mm.streamWhales({ minUsd: 250_000 });

const timer = setTimeout(abort, 30_000); // cancel after 30s

for await (const ev of events) {
  if (ev.type === "whale") {
    console.log(ev.data.coin, ev.data.side, ev.data.notionalUsd.toFixed(0));
  }
  if (ev.type === "bye") break;
}
clearTimeout(timer);

Live HL trade stream

BTC prints ≥ $50k.

const { events } = mm.streamTrades({ coin: "BTC", minUsd: 50_000 });
for await (const ev of events) {
  if (ev.type !== "trade") continue;
  const t = ev.data;
  console.log(new Date(t.ts).toISOString(), t.side, t.size, "@", t.price);
}

Funding rate change alerts

Fire only when |delta| ≥ 1bp on BTC / ETH.

const { events } = mm.streamFunding({
  coins: ["BTC", "ETH"],
  minDeltaBps: 1,
});
for await (const ev of events) {
  if (ev.type !== "funding") continue;
  const f = ev.data;
  const bps = (f.delta ?? 0) * 1e4;
  console.log(f.coin, "rate=", f.rate, "delta=", bps.toFixed(2), "bps");
}

Errors

Non-2xx HTTP responses throw MmflowApiError with { status, body }. Catch it the same way you'd catch any other typed error:

try {
  await mm.optionsGex({ underlying: "XYZ" as any });
} catch (e) {
  if (e instanceof MmflowApiError) {
    console.error(e.status, e.body);
  }
}

A Python SDK is on the roadmap. REST reference