Next liveBuild Your Chief of Staff · OnlineSave your seat →
Developer Platform

Rate Limits

Understanding and working with Chipp MCP Server rate limits

|View as Markdown
1 min read
#mcp#rate-limits#api#throttling

The Chipp MCP Server enforces rate limits to ensure fair usage and platform stability. Every tool call spends points from a per-minute budget. A cheap read costs 1 point. A heavy tool costs more. Limits vary by subscription tier, and no plan is unlimited.

These limits apply to every Chipp MCP endpoint: build.chipp.ai/mcp, mcp.chipp.ai/mcp, the /mcp/<area> sub-MCPs, and the software factory tools.

Rate Limits by Tier

PlanX-RateLimit-TierPoints per minute, per userPer token or keyPer organizationMCP access
FreeFREENoneNoneNoneNot available
BuilderBUILDER3030150Full access
BuilderSTUDIO6060300Full access
Studio ProSTUDIO_PRO120120600Full access
Do It With YouENTERPRISE120012006000Full access

Each call spends its weight from three budgets at once: your user budget (shared by all your OAuth tokens and API keys), the budget of the token or key you used, and your organization’s budget. A call goes through only if all three have room.

ℹ️

MCP server access requires a Builder plan or higher. Free tier users cannot use the MCP server: requests return 403 with error code tier_blocked.

ℹ️

Enterprise plans have a high default, not unlimited access. If your contract names a different number, we store it for your organization and it replaces the default.

What Each Call Costs

ToolPoints per call
dispatch_ticket_batch10
export_conversations10
generate_image10
newsletter_import_contacts10
run_eval10
web_research10
ask_chief_of_staff5
bulk_update_app_settings5
chat_with_agent5
chat_with_app5
consult_agent5
dispatch_ticket5
generate_system_prompt5
generate_utterances5
get_app_analytics5
get_file_analytics5
get_model_usage5
get_moderation_stats5
get_monetization_stats5
get_response_time_analytics5
get_tag_analytics5
get_usage_by_app5
import_collection_tools5
import_tag_examples5
propose_platform_roll5
propose_ticket_batch5
run_nightly_ci5
run_ticket_ci5
spawn_subagent5
trigger_heartbeat5
add_project_files3
generate_suggestions3
get_nightly_ci_report3
get_release_digest3
get_usage3
publish_app3
query_database3
search_conversations3
search_messages3
web_search3
fetch_url2
get_ci_run_log2
search_kb_evidence2
Every other tool1
wait_for_tickets, watch_deploy, watch_ci_run, watch_release0 points: they use a long-poll slot
initialize, tools/list, ping, notifications0 points: they use a small separate budget of 60 per minute per token

A batch request (a JSON-RPC array) costs the sum of its calls. A request that costs more than your whole per-minute budget is refused, so split it.

Other Limits

LimitValue
Requests in flight at once, per user10
Requests in flight at once, per token or key8
Long-poll calls open at once, per user3
Long-poll calls open at once, per token or key2
Long-poll calls open at once, per organization10
Handshake calls (initialize, tools/list), per token or key per minute60
Requests per minute from one network address (all traffic)1200
Failed sign-ins per minute from one network address30
Repeated limit errors that block a token or address5 within 120 seconds
Block length, first to last repeat1 min, 5 min, 30 min, 2 h

Long-poll tools (wait_for_tickets, watch_deploy, watch_ci_run, watch_release, and the live ticket stream) hold a request open. They do not spend points, so a client that watches tickets cannot use up its own budget. They take one of a small number of long-poll slots instead, and the slot is freed when the call finishes, fails, or your client disconnects.

Handshake calls (initialize, tools/list, ping) have their own small budget per token, so reconnecting often never uses the budget for your tool calls.

Rate Limit Headers

Every response carries rate limit information, on every plan:

HeaderDescription
X-RateLimit-TierYour subscription tier
X-RateLimit-LimitPoints per minute in your user budget
X-RateLimit-RemainingPoints left in the current window
X-RateLimit-UsedPoints used in the current window
X-RateLimit-CostPoints this request spent
X-RateLimit-ResetUnix timestamp when the window resets
X-RateLimit-Concurrency-LimitRequests you can have in flight at once

Example response headers:

plaintext
HTTP/1.1 200 OK
X-RateLimit-Tier: BUILDER
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 20
X-RateLimit-Used: 10
X-RateLimit-Cost: 10
X-RateLimit-Reset: 1704067260
X-RateLimit-Concurrency-Limit: 10

Handling Rate Limits (429 Errors)

When you exceed a limit, the server returns a 429 Too Many Requests response. The limit field names the limit you hit, so your client knows what to do:

json
{
  "error": {
    "code": "rate_limited",
    "limit": "user",
    "message": "Rate limit exceeded (limit: user): your per-minute budget (shared by all your tokens and keys). Retry after 45 seconds. Do not retry sooner.",
    "retry_after_seconds": 45
  }
}

The same text is in the tool error, so an AI assistant that reads it will wait instead of retrying at once. The response also has X-RateLimit-Limit-Hit with the same value as limit.

limitThe limit you hit
useryour per-minute budget (shared by all your tokens and keys)
credentialthe per-minute budget of this one token or key
tenantyour organization’s per-minute budget
ipthe per-minute request limit for your network address
concurrencythe cap on requests in flight at once
long_pollthe cap on long-running watch calls open at once
globalthe server-wide safety limit for this endpoint (not your fault, retry soon)

Repeated 429s

A client that keeps sending requests after a 429 is blocked for a short time. The block grows each time it repeats (see “Other Limits”). While blocked, every request returns 429 with "blocked": true and a Retry-After for the end of the block. Always wait for Retry-After.

Retry-After Header

The Retry-After header tells you exactly when to retry:

plaintext
HTTP/1.1 429 Too Many Requests
Retry-After: 45
X-RateLimit-Limit-Hit: user
X-RateLimit-Reset: 1704067260

Implementing Retry Logic

Basic exponential backoff:

javascript
async function callWithRetry(makeRequest, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await makeRequest();

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 60;
      console.log(`Rate limited. Waiting ${retryAfter}s...`);
      await sleep(retryAfter * 1000);
      continue;
    }

    return response;
  }
  throw new Error('Max retries exceeded');
}

Respecting Retry-After:

python
import time
import requests

def call_mcp(tool_name, args):
    response = requests.post(
        "https://build.chipp.ai/mcp",
        json={"method": "tools/call", "params": {"name": tool_name, "arguments": args}},
        headers={"Authorization": f"Bearer {token}"}
    )

    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        print(f"Rate limited. Sleeping {retry_after}s...")
        time.sleep(retry_after)
        return call_mcp(tool_name, args)  # Retry

    return response.json()

Best Practices

1. Batch Operations

Instead of calling tools one at a time, combine related operations:

plaintext
# Inefficient: 10 separate calls
for app in apps:
    update_app(app.id, {...})

# Better: Use bulk patterns where available
# Or space out calls to stay within limits

2. Use Caching

Cache responses that don’t change frequently:

javascript
const appCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getApp(appId) {
  const cached = appCache.get(appId);
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const app = await mcpCall('get_app', { app_id: appId });
  appCache.set(appId, { data: app, timestamp: Date.now() });
  return app;
}

3. Monitor Your Usage

Check the remaining points before large operations:

javascript
// Check headers from any response
const remaining = parseInt(response.headers['x-ratelimit-remaining']);
const resetTime = parseInt(response.headers['x-ratelimit-reset']);

if (remaining < 5 && operations.length > 5) {
  const waitTime = resetTime - Math.floor(Date.now() / 1000);
  console.log(`Low on requests. Waiting ${waitTime}s before bulk operation...`);
  await sleep(waitTime * 1000);
}

4. Spread Requests Over Time

For non-urgent bulk operations, spread requests evenly:

javascript
async function bulkUpdate(apps, updateFn) {
  const REQUESTS_PER_MINUTE = 25; // Stay under limit
  const DELAY_MS = 60000 / REQUESTS_PER_MINUTE; // ~2.4 seconds

  for (const app of apps) {
    await updateFn(app);
    await sleep(DELAY_MS);
  }
}

5. Use Pagination Wisely

Fetch only what you need:

plaintext
# Good: Small pages, specific queries
list_apps(limit=10, offset=0)
search_conversations(app_id="xxx", query="refund", limit=20)

# Avoid: Large fetches you don't need
list_apps(limit=1000)
export_conversations(app_id="xxx", since="2026-01-01")  # Use sparingly

Upgrading Your Limits

Need higher limits? Upgrade your plan. Each plan’s limit is in Rate Limits by Tier.

Upgrade Your Plan

Common Scenarios

Bulk App Creation

Problem: Creating 50 apps hits rate limit.

Solution (BUILDER tier):

javascript
// 30 requests/minute = ~2 seconds between requests
for (const appConfig of appConfigs) {
  await createApp(appConfig);
  await sleep(2000); // 2 second delay
}
// Total time: ~100 seconds for 50 apps

Analytics Dashboard

Problem: Fetching analytics for 20 apps exceeds limit.

Solution:

javascript
// Option 1: Fetch in batches
const batches = chunk(appIds, 5);
for (const batch of batches) {
  await Promise.all(batch.map(id => getAppAnalytics(id)));
  await sleep(10000); // Wait 10s between batches
}

// Option 2: Cache results
// Analytics don't change by the second - cache for 5+ minutes

CI/CD Integration

Problem: Automated deployments need multiple tool calls.

Solution:

yaml
# In your CI pipeline
steps:
  - name: Update Chipp App
    run: |
      # Use retry logic
      ./update-app.sh --retry-on-rate-limit
    env:
      CHIPP_RETRY_DELAY: 60
      CHIPP_MAX_RETRIES: 3

Rate Limit FAQ

Q: Do rate limits apply per user or per organization? A: Both, and per token. All of a user’s OAuth tokens and API keys share one user budget. Each token or key also has its own budget, and your organization has a budget shared by all its members. Each team member has their own user budget.

Q: Does reading consume the same rate as writing? A: No. Each tool has a weight. A cheap read costs 1 point. Exports, bulk analytics, image generation, ticket dispatch and batches cost more (see “What Each Call Costs”). initialize and tools/list do not spend this budget: they use a small separate one. Watch tools such as wait_for_tickets spend no points and use long-poll slots.

Q: Why did I get a 429 when I have points left? A: Another limit was hit. Read the limit field of the error: concurrency means too many requests in flight, long_poll means too many open watch calls, credential or tenant means that token’s or your organization’s budget is spent, ip means too many requests from your network address, and global is a server-wide safety limit that clears in seconds.

Q: Are unauthenticated or failed requests limited? A: Yes. Requests from one network address are counted whether or not they are signed in, and repeated failed sign-ins from one address are limited and then blocked for a short time.

Q: What happens if I’m rate limited mid-workflow? A: Implement retry logic with exponential backoff. Your MCP client should handle 429 responses gracefully.

Q: Is there an unlimited plan? A: No. Enterprise has a high default budget, and every response still carries the rate limit headers.

Q: Can I request higher limits for a specific use case? A: Enterprise customers can negotiate custom limits for their organization. Contact sales@chipp.ai for details.


Next Steps