Mr. Doge

Concepts

Tiers & limits

What each plan unlocks — methods, request volume, and concurrent subscriptions.

Mr. Doge plans gate two things: which methods you can call and how many requests per minute. Upgrading is one click in the dashboard.

At a glance

TierConnections (per key)Requests/min (per key)Subs (per connection)Methods
Free10605Discovery + basic reads
Starter2530025+ trending, live snapshots, live subscriptions
Growth751,000100+ per-match deep subscriptions
Business2505,000500+ AI picks, recommendations

See pricing →

Caps stack: each WebSocket connection independently holds up to its tier's subscription cap. A Free key with 10 connections can hold 10 × 5 = 50 total subscriptions across them. Most apps use one connection per browser tab / mobile app instance.

Method availability by tier

auth, tokens.create, and subscription.cancel are always allowed — no method gating, no rate limiting.

Free

Everything you need to evaluate the SDK:

  • regions.list
  • competitions.list
  • teams.list, teams.get, teams.form
  • matches.list, matches.get, matches.search

Starter

Free, plus:

  • matches.trending — top matches by sport
  • matches.getLive — live snapshot (one-shot, no deltas)
  • matches.subscribeLive — full live subscription with WebSocket deltas

Growth

Starter, plus:

  • matches.subscribe — single-match deep subscription (stats + odds + status pushes)

Business

Growth, plus:

  • ai.picks.list — AI-generated picks with reasoning
  • ai.recommendations.list — recommendations with edge and confidence

Rate limits

Limits are per-key, per-minute, shared across HTTP and WebSocket — you can't double your quota by mixing transports.

When you hit a limit, the server returns rate_limited with metadata:

try {
  await doge.matches.list({ sports: ["soccer"] });
} catch (err) {
  if (err instanceof RateLimitError) {
    await new Promise((r) => setTimeout(r, err.retryAfterMs));
    // retry
  }
}

err.retryAfterMs is the precise wait time — the SDK doesn't auto-retry, but the metadata makes it trivial to implement.

Subscription limits

Each tier caps subscriptions per WebSocket connection — not per key. The check is local to each connection's subscription map; close a subscription (sub.cancel()) or close the connection to free a slot.

TierSubs per connection
Free5
Starter25
Growth100
Business500

Because a key can hold multiple concurrent connections (see connection limits below), the effective max subscriptions per key is subs/conn × max connections:

TierEffective max (subs × conns)
Free50
Starter625
Growth7,500
Business125,000

If a single connection exceeds its cap, matches.subscribeLive / matches.subscribe throws SubscriptionLimitError. Cancel an old subscription, open a new connection, or upgrade.

Connection limits

WebSocket connections are also capped per key. A single browser tab is one connection; a load-balanced backend with 50 instances is 50 connections.

If you need more, use the HTTP transport for bulk reads — it doesn't count against the connection limit.

Forbidden errors

Calling a method outside your tier throws ForbiddenError with context:

try {
  await doge.ai.picks.list({ sports: ["soccer"] });
} catch (err) {
  if (err instanceof ForbiddenError) {
    console.log(err.data);
    // { method: "ai.picks.list", tier: "free" }
  }
}

Use this to show an in-app upgrade prompt.

Upgrading

Upgrades are instant — no key rotation, no redeployment. The dashboard flips your sdkTier in Stripe metadata, and the next request gets the new limits.

JWTs minted before an upgrade keep their old tier until they expire (max 24 hours). For most browser apps this is fine; if you need an immediate flip, invalidate the current token and remount the client.

Next

On this page

Tiers & limits