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