Mosaic is now liveMCP tools, 760+ targets across oncology, neuro and cardio, preprint on Zenodo.See plans →
Mosaic

Rate limits

Mosaic rate-limits to keep the graph responsive and the unit economics sane. Limits apply equally to the MCP server and the HTTP API — they share quota. The numbers below match what the middleware enforces today; planned tightening is flagged explicitly in the "Roadmap" section.

Request quotas (live)

Every request passes a per-IP token bucket. Authenticated requests additionally pass a per-key bucket sized by tier. Both are sliding 60-second windows.

TierScopeRateTool surface
UnauthenticatedPer IP60 / minutePublic endpoints only
FreePer key60 / minuteLookup tools only (see below)
ProPer key300 / minuteFull tool surface
EnterprisePer key1000 / minuteFull + private deployment

Tier and current usage are visible at /account. Upgrade at /pricing.

Free-tier tool gating

The free tier covers every 1st-order lookup the KG exposes — enough to evaluate Mosaic against your own workflow before paying. KG-native multi-hop tools (resistance bypass, synthetic-lethal whitespace, talent migration, modality gaps, emerging signals) require Pro. Calling a paid tool on a free key returns a structured PaidTierRequired error with the list of free-tier tools and an upgrade link.

Free-tier tools also have per-call result caps (e.g. 10 compounds/papers/patents per response, 5 analogs); Pro raises these to 50, Enterprise to 200. See tools reference for per-tool limits.

When you're throttled

You'll receive a 429 Too Many Requests with this header:

  • Retry-After — seconds to wait before the bucket refills enough for the next call. Honor this directly; it's the simplest correct backoff.

The response body is JSON: {"detail": "Rate limit exceeded. Try again later."} for per-IP throttles, or {"detail": "API key rate limit exceeded."} for per-key throttles.

Backoff that doesn't thrash

Tight retry loops on a 429 will keep you throttled. The minimum-viable client behaviour:

import time, random, httpx

def call(url, headers, payload, max_attempts=5):
    for attempt in range(max_attempts):
        r = httpx.post(url, json=payload, headers=headers, timeout=30)
        if r.status_code != 429:
            r.raise_for_status()
            return r.json()
        # Honor Retry-After if present, else exponential with jitter.
        wait = float(r.headers.get("Retry-After", 0)) or (2 ** attempt + random.random())
        time.sleep(wait)
    raise RuntimeError("rate limited beyond max_attempts")

MCP clients (Claude Desktop, Cursor, Claude Code) handle backoff for you — this code is for direct HTTP API consumers.

Daily cost ceiling

Each paid-tier key carries a per-day budget against the downstream-cost ledger (LLM tokens, third-party API spend incurred fulfilling your request). Free $5/day, Pro $50/day, Enterprise $500/day. This is a cost cap, not a request cap — most workflows will hit the rate limit long before the budget. You can monitor consumption at /account.

Roadmap

Controls planned but not yet shipped — listed so client authors can choose whether to anticipate them:

  • Daily request quotas — a per-key daily request count on top of the per-minute bucket (Free ~500/day, Pro ~5,000/day, Enterprise custom). Today the per-minute bucket is the only request-count cap.
  • X-RateLimit-* response headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset on every authenticated response, so clients can pace without waiting for a 429. Today only Retry-After (on 429) is emitted.
  • Bulk-export Pro add-on — a long-poll job surface that exempts large export queries from the per-minute cap in favour of a per-day throughput limit. Not yet implemented.

See authentication for key issuance and rotation.