How to Manage Rate Limiting and API Quotas When Developing Claude Skills
Implement module-level rate constants, pagination controls, and token-bucket throttling to handle external API limits gracefully, while exposing quota monitoring tools that let the LLM adapt its request volume dynamically.
Developing Claude Skills (MCP servers) often requires integrating with external APIs that enforce strict rate limits and quota caps. Without proper safeguards, your skill can hit HTTP 429 errors, trigger temporary bans, or degrade the user experience. The ComposioHQ/awesome-claude-skills repository provides concrete patterns and reference implementations to manage rate limiting and API quotas when developing Claude Skills reliably.
Design-Time Rate Limiting Strategies
Establishing constraints at the design phase prevents excessive API consumption before it happens.
Declare Rate Limits as Module-Level Constants
Store rate limits as centralized constants that every tool references. In mcp-builder/reference/mcp_best_practices.md, the best practice recommends keeping a module-level constant such as RATE_LIMIT = 20 calls per minute and referencing it in every tool that contacts the external service【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/mcp-builder/reference/mcp_best_practices.md†L243-L250】. This ensures consistent enforcement across your skill’s surface area.
Implement Pagination for Large Datasets
Always respect a limit parameter and return pagination metadata so the LLM can request additional pages instead of pulling entire datasets at once. According to the MCP best practices, paginated responses prevent single requests from consuming disproportionate quota【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/mcp-builder/reference/mcp_best_practices.md†L22-L28】.
Enforce Character Limits on Outputs
Guard output size with a CHARACTER_LIMIT constant (approximately 25,000 characters) and truncate gracefully, informing the model that data was trimmed. This prevents token overflow and reduces the payload size for downstream API calls【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/mcp-builder/reference/mcp_best_practices.md†L45-L52】.
Runtime Rate Limiting and Quota Management
Runtime strategies handle dynamic constraints and transient failures.
Throttle API Calls with Token Buckets
Wrap each API call in a throttling helper that tracks recent timestamps and sleeps when the limit is approached. The repository suggests using async-aware rate limiters to enforce per-minute ceilings without blocking the event loop.
Here is a Python implementation using asyncio and a simple token bucket:
import asyncio
import time
from typing import Callable, Awaitable
RATE_LIMIT = 20 # calls per minute
INTERVAL = 60 / RATE_LIMIT # seconds between calls
_last_call = 0.0
async def rate_limited(fn: Callable[..., Awaitable]):
"""Decorator that enforces a per-minute rate limit."""
async def wrapper(*args, **kwargs):
global _last_call
now = time.time()
elapsed = now - _last_call
wait = max(0, INTERVAL - elapsed)
if wait:
await asyncio.sleep(wait)
_last_call = time.time()
try:
return await fn(*args, **kwargs)
except Exception as e:
# Simple exponential back-off for 429 / quota errors
if getattr(e, "status_code", None) == 429:
for i in range(3):
await asyncio.sleep(2 ** i) # 2s, 4s, 8s
try:
return await fn(*args, **kwargs)
except Exception:
continue
raise RuntimeError("Rate limit exceeded after retries") from e
raise
return wrapper
@rate_limited
async def fetch_contacts(api_key: str, offset: int = 0):
# Example API call
return await http_client.get(
"https://api.mailerlite.com/v2/contacts",
headers={"Authorization": f"Bearer {api_key}"},
params={"offset": offset}
)
Implement Exponential Back-Off for 429 Errors
On HTTP 429 ("Too Many Requests") or quota-exceeded responses, retry after a delay that grows exponentially (e.g., 2 seconds → 4 seconds → 8 seconds). Convert rate-limit errors into clear, user-friendly messages that suggest lowering request volume or waiting, as recommended in mcp-builder/SKILL.md【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/mcp-builder/SKILL.md†L11-L15】.
For Node.js/TypeScript environments, use the bottleneck library:
import Bottleneck from "bottleneck";
import fetch from "node-fetch";
const limiter = new Bottleneck({
maxConcurrent: 1,
minTime: 3000 // ≈ 20 calls/minute
});
async function safeFetch(url: string, opts: any) {
try {
const res = await limiter.schedule(() => fetch(url, opts));
if (res.status === 429) {
// exponential back-off retry
for (let i = 0; i < 3; i++) {
await new Promise(r => setTimeout(r, 2 ** i * 1000));
const retry = await fetch(url, opts);
if (retry.status !== 429) return retry.json();
}
throw new Error("Rate limit exceeded");
}
return await res.json();
} catch (e) {
// Convert to user-friendly message
throw new Error(`Unable to fetch data: ${e.message}`);
}
}
Monitor Quotas with Dedicated Tools
Provide a dedicated tool (e.g., GET_USAGE or MAILERLITE_GET_USAGE) that reports remaining calls or credits so the skill can adapt its behavior dynamically. Many composio-skills expose such utilities; for example, the MailerLite automation skill includes a quota tool that exposes remaining API capacity【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/composio-skills/mailerlite-automation/SKILL.md†L121-L123】.
Testing Rate Limit Resilience
Include rate limiting scenarios in your security testing checklist (see the security testing bullet in mcp-builder/reference/mcp_best_practices.md)【^/cache/repos/github.com/ComposioHQ/awesome-claude-skills/master/mcp-builder/reference/mcp_best_practices.md†L910-L913】.
Write unit tests that simulate 429 responses and verify that your back-off logic behaves as expected before deployment.
Summary
- Declare constants – Store
RATE_LIMITandCHARACTER_LIMITas module-level values inmcp-builder/reference/mcp_best_practices.mdto ensure consistent enforcement. - Paginate requests – Always support
limitparameters and pagination metadata to prevent bulk quota consumption. - Throttle dynamically – Use token buckets or libraries like
bottleneckto enforce per-minute limits at runtime. - Retry exponentially – Implement 2s → 4s → 8s back-off for HTTP 429 errors and convert them to user-friendly messages.
- Expose quota status – Provide
GET_USAGEtools (as seen in MailerLite skills) so Claude can adjust request volume proactively.
Frequently Asked Questions
What happens if my Claude Skill exceeds an API rate limit?
The external API returns an HTTP 429 error or a quota-exceeded response, which can halt the skill's execution or trigger temporary IP bans. Implement exponential back-off and graceful error messages to handle these cases without breaking the user workflow.
How do I expose quota information to Claude?
Create a dedicated tool such as GET_USAGE or MAILERLITE_GET_USAGE that queries the external service's quota endpoint and returns remaining credits or calls. This allows the LLM to check capacity before making expensive requests, adapting its strategy dynamically.
Should I implement rate limiting at the design or runtime level?
Implement both. Design-time constraints (constants, pagination limits) prevent excessive requests by architecture, while runtime throttling and back-off handle transient spikes and external API variability. This dual-layer approach is documented in the MCP best practices for robust skill development.
What is the recommended character limit for Claude Skill outputs?
The repository recommends guarding outputs with a CHARACTER_LIMIT of approximately 25,000 characters, truncating gracefully and informing the model when data has been trimmed. This prevents context window overflow and reduces downstream processing load.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →