For agents

MRR Ledger is built to be read by machines. Free, CC BY 4.0, no key needed for the static endpoints.

Endpoints

GET /api/v1/businesses.jsonAll businesses with sourced metrics
GET /api/v1/businesses/{slug}.jsonOne business
GET /api/v1/leaderboard.jsonRanked by annualized revenue
GET /api/v1/leaderboard.csvSame ranking as CSV (one row per ranked business: headline label, raw value_usd and value_text, basis, as_of, source_url, profile_url). Opens in Sheets/Excel/pandas.
GET /api/v1/categories.jsonCategory insights: id/slug, label, count, ranked_count, median_annualized_usd (among ranked), median_annualized_by_tier, members[{slug,name,rank,annualized_revenue_usd,url_profile}], method. Leaderboard deep-link /leaderboard/?category=<slug>. MCP list_categories / get_category.
GET /api/v1/tags.jsonTag insights: slug, label, count, ranked_count, median_annualized_usd (among ranked), median_annualized_by_tier, members[{slug,name,rank,annualized_revenue_usd,url_profile}], method. Sorted by count desc then label. HTML /tags/ shows count≥2 only. Leaderboard deep-link /leaderboard/?tag=<slug>. MCP list_tags / get_tag.
GET /api/v1/feature.jsonToday's featured business
GET /api/v1/features.json14-day feature rotation (entries, card_url, is_today)
GET /api/v1/stats.jsonHub totals: businesses, chartable, lessons, tiers/categories, stale counts, median annualized by tier and by category, revenue_disclosed_count / revenue_undisclosed_count, agent_native_count / agent_added_count
GET /badge/{slug}.svgEmbeddable 20px SVG badge: rank + raw headline metric (marks reported / last synced). /badge/{slug}-plain.svg = "listed", no number. URLs also on each record as badge_url / badge_plain_url.
GET /api/v1/newsletter.jsonThis week's issue: feature, new businesses, number moves, indie corner, lesson, upcoming, and paste-ready markdown
GET /api/v1/growth.jsonBusinesses with multi-point growth series
GET /api/v1/growth.csvEvery growth_series point as CSV (slug, as_of, label, value_usd, annualized_usd, basis, confidence, source_url, profile_url, growth_json_url). Free under CC BY 4.0. Full all-metrics history CSV stays Pro.
GET /api/v1/growth/{slug}.jsonDated annualized revenue points for one business
GET /api/v1/lessons.jsonEvery lesson, attributed, with keyword-tagged themes (rules included)
GET /api/v1/insights.jsonPricing-model buckets with medians, and time to $100M (with sources and method)
GET /api/v1/sources.jsonEvery cited source, which businesses use it, and its last automated link check
GET /api/v1/changes.jsonDated change log: businesses added, new sourced points, revisions (newest first)
GET /api/v1/pricing.jsonPlans, prices and Stripe checkout links (paid plans fulfilled by email)
GET /api/v1/compare.json?a=&b=Compare two businesses by slug (briefs + diff). Needs the Vercel compare function; also MCP compare_businesses
GET /api/v1/search.json · ?q=&category=&tier=&verified_only=&limit=Substring search over name, slug, one_liner, tags, lessons. verified_only=true keeps only businesses whose headline figure is payment-processor verified (headline_verification not null). Static file = full index; query params need the Vercel search function. MCP search_businesses covers the same job for agents.
/openapi.json · /openapi-3.0.jsonOpenAPI 3.1 spec, plus an OpenAPI 3.0.3 copy for tools that only import 3.0
/llms.txt, /llms-full.txtLLM-friendly summaries
/playbook/Human page: lessons library, pricing models, time to $100M
/compare/Human page: side-by-side compare (?a=&b= deep links)
/features/Human page: 14-day featured rotation archive
/mcpMCP server (Streamable HTTP, no key). Canonical endpoint: https://mrrledger.com/mcp (POST JSON-RPC, answered directly; /mcp/ also works). Tools (all read-only, annotated readOnlyHint): search_businesses, get_business, leaderboard, todays_feature, get_growth, search_lessons, whats_new, compare_businesses, hub_stats, list_categories, list_tags, get_tag, get_category. Resources: mrrledger://llms.txt, mrrledger://leaderboard, mrrledger://stats, mrrledger://changes
/.well-known/mcp-server.jsonMCP Registry manifest (server.json format) for registries and clients

Field notes

Free: leaderboard CSV + growth-series CSV (chartable points only). Pro (coming soon, join the waitlist): full all-metrics history CSV, denser history and alerts. Paid API keys (Hobby, Builder, Team) are issued by hand by email for now; see pricing.

API keys and rate limits

Keys are optional. No key means the Free plan: 100 requests per day per IP. Paid keys come with API Hobby, Builder and Team on pricing and are issued by email for now.

PlanRequests/day (UTC)KeysExtras
Free100 per IPnoneStatic JSON/CSV, search, compare, MCP
Hobby ($9/mo)2,0001Commercial use
Builder ($19/mo)10,0001Full history CSV: /api/v1/export.csv + 5 revenue-move webhooks
Team ($79/mo)25,000 shared5Bulk JSONL: /api/v1/export.jsonl + 20 revenue-move webhooks

Send the key as X-API-Key: mrl_live_… or Authorization: Bearer mrl_live_…. The limit is counted on the function endpoints: /api/v1/search.json, /api/v1/compare.json, the export endpoints and /mcp (only tools/call counts). Static files under /api/v1/ stay free and are not metered.

Every metered response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds, next UTC midnight) and X-RateLimit-Plan. Over the limit you get HTTP 429 with Retry-After. A wrong or revoked key gets 401. Counts are approximate.

Pay per call (no signup): x402

Agents can pay with x402 in USDC on Base Sepolia (testnet, test USDC only), with no account and no key. The free tier is unchanged: 100 requests/day per IP and every static /api/v1/*.json file stay free.

WhatPriceHow
search / compare / MCP tools/call past the free 100/day$0.005 per callThe 429 becomes a 402 with a PAYMENT-REQUIRED header; retry with PAYMENT-SIGNATURE
/api/v1/export.csv (full history)$0.25 per downloadPay instead of a Builder key
/api/v1/export.jsonl (bulk)$0.50 per downloadPay instead of a Team key
API pass: hobby, 1 day$0.50POST /api/v1/passes?plan=hobby&days=1 or MCP buy_api_pass; returns an expiring key
API pass: hobby, 30 days$9POST /api/v1/passes?plan=hobby&days=30 or MCP buy_api_pass; returns an expiring key
API pass: builder, 30 days$19POST /api/v1/passes?plan=builder&days=30 or MCP buy_api_pass; returns an expiring key
API pass: team, 30 days$79POST /api/v1/passes?plan=team&days=30 or MCP buy_api_pass; returns an expiring key
Revenue-move watch (webhook), 30 days$1 per business (at most $5), $5 for allPOST /api/v1/watch or MCP watch_revenue; included with Builder and Team keys

MCP: an unpaid call past the quota returns a tool result with isError: true and the PaymentRequired object in structuredContent; retry with params._meta["x402/payment"]. Clients: @x402/fetch (wrapFetchWithPayment) or any x402 v2 client. x402 v1 clients work too: the 402 body is a v1 PaymentRequired (network base-sepolia, maxAmountRequired); pay with X-PAYMENT, receipt in X-PAYMENT-RESPONSE.

Revenue-move alerts (webhooks)

Get a signed POST to your own HTTPS endpoint when a business you watch gets a new or revised revenue figure. It fires after each data refresh, once per change. Webhooks only, no email.

How you get itPriceLimit
x402 (USDC on Base Sepolia), no signup$1 per business per 30 days (never more than $5); $5 for all businesses20 watches per payer, 10 businesses per watch
API Builder keyIncluded5 watches
API Team keyIncluded20 watches

Create: POST /api/v1/watch with {"slugs": ["cursor", "lovable"] | "all", "webhook_url": "https://your.app/hooks/mrrledger"} (optional "kinds": metric_revised, metric_added, metric_removed, business_added, metric_redated). Send your Builder/Team key as X-API-Key, or send no key and pay the 402 with PAYMENT-SIGNATURE. MCP: tool watch_revenue. The answer has a watch_id and a secret. The secret is shown once.

Manage: GET or DELETE /api/v1/watch/{watch_id} with Authorization: Bearer <secret> (or the owning key).

Verify: each delivery has MRL-Signature: t=<unix>,v1=<hex>, where hex = HMAC-SHA256(secret, t + "." + raw body). Reject if the signature differs or t is more than 5 minutes old. Answer 2xx quickly. We retry failures with backoff (1 min to 6 h, 6 tries) and pause a watch after 8 failures in a row. Deliveries are at-least-once, so dedupe on the event id (also in MRL-Event-Id).

const h = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex"); // compare with v1 using timingSafeEqual

Webhook URLs must be https on port 443 with a public DNS name: no IP addresses, no localhost, private, link-local, CGNAT or metadata ranges. We check again before every delivery and never follow redirects.