Rate Limits
Understanding and working within clanker’s rate limits.
Overview
Rate limits protect the platform and ensure fair usage for all users.
API Rate Limits
| Endpoint Type | Limit | Window |
|---|---|---|
| REST API | 100 requests | 1 minute |
| MCP tools | 60 requests | 1 minute |
| Skill execution | 30 requests | 1 minute |
Rate Limit Headers
Every response includes rate limit information:
x-ratelimit-limit: 100
x-ratelimit-remaining: 45
x-ratelimit-reset: 60
| Header | Description |
|---|---|
x-ratelimit-limit | Maximum requests per window |
x-ratelimit-remaining | Remaining requests in current window |
x-ratelimit-reset | Seconds until window resets |
MCP Rate Limits
MCP tools are rate-limited to 60 requests per minute. Rate limit information is conveyed via HTTP response headers on the MCP transport endpoint (/mcp/), using the same x-ratelimit-* headers as the REST API.
Rate Limit Error
When you exceed the rate limit:
HTTP Response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Retry after the x-ratelimit-reset window."
}
}
Execution Limits
| Limit | Value | Description |
|---|---|---|
| Concurrent executions | 1 per workspace | One execution runs per workspace at a time; a user can run executions in different workspaces concurrently |
| Execution timeout | 65 minutes | Maximum execution duration |
| Input size | 100KB | Maximum input payload size |
Concurrent Execution Error
Returned with HTTP 429 when a workspace already has a running execution:
{
"error": "EXECUTION_RUNNING",
"message": "An execution is already running. Wait for it to complete."
}
Best Practices
1. Implement Exponential Backoff
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const resetIn = parseInt(response.headers.get('x-ratelimit-reset') || '60');
const delay = resetIn * 1000 * Math.pow(2, attempt);
console.log(`Rate limited. Waiting ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
2. Check Remaining Before Requests
let rateLimitRemaining = 60;
let rateLimitReset = Date.now();
async function makeRequest(url, options) {
// Wait if no remaining requests
if (rateLimitRemaining <= 0 && Date.now() < rateLimitReset) {
const waitTime = rateLimitReset - Date.now();
console.log(`Waiting ${waitTime}ms for rate limit reset...`);
await new Promise(r => setTimeout(r, waitTime));
}
const response = await fetch(url, options);
// Update rate limit info
rateLimitRemaining = parseInt(response.headers.get('x-ratelimit-remaining') || '100');
rateLimitReset = Date.now() + parseInt(response.headers.get('x-ratelimit-reset') || '60') * 1000;
return response;
}
3. Cache Responses
const cache = new Map();
const CACHE_TTL = 60000; // 1 minute
async function cachedFetch(url, options) {
const cacheKey = `${options?.method || 'GET'}:${url}`;
const cached = cache.get(cacheKey);
if (cached && Date.now() < cached.expiresAt) {
return cached.data;
}
const response = await fetch(url, options);
const data = await response.json();
cache.set(cacheKey, {
data,
expiresAt: Date.now() + CACHE_TTL
});
return data;
}
5. Check Execution Status Before Starting
async function safeExecuteSkill(slug, input) {
// Check if execution is already running
const status = await getExecutionStatus();
if (status?.status === 'running') {
throw new Error('Please wait for current execution to complete');
}
return executeSkill(slug, input);
}
Rate Limits by Subscription
| Tier | API Requests | MCP Requests | Execution Requests |
|---|---|---|---|
| Free | 100/min | — | 30/min |
| Pro | 100/min | — | 30/min |
| Based Mode | 100/min | 60/min | 30/min |
Note: MCP access (60 req/min) is exclusive to Based Mode ($29/month). REST API rate limits are the same for all tiers.
Monitoring Usage
Check Rate Limit Status
curl -I https://clanker.net/api/v1/marketplace/skills \
-H "x-api-key: YOUR_API_KEY"
Response headers show current usage:
x-ratelimit-limit: 100
x-ratelimit-remaining: 98
x-ratelimit-reset: 60
In Your Application
let apiCalls = 0;
const windowStart = Date.now();
// Log usage periodically
setInterval(() => {
const elapsed = (Date.now() - windowStart) / 1000;
console.log(`API calls: ${apiCalls} in ${elapsed}s`);
}, 30000);
Getting Help
If you consistently hit rate limits:
- Review your request patterns
- Implement caching and batching
- Check for request loops or bugs
- Contact support for usage optimization advice