Rate Limits & Quotas
Spooled Cloud enforces rate limits and quotas to ensure fair usage and platform stability. Limits vary by plan tier and can be increased for Enterprise customers.
Plan Comparison
| Resource | Free | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Jobs per day | 1,000 | 10,000 | 100,000 | Unlimited |
| Active jobs | 10 | 500 | 5,000 | Unlimited |
| Queues | 2 | 10 | 50 | Unlimited |
| Workers | 1 | 5 | 25 | Unlimited |
| API keys | 2 | 5 | 25 | Unlimited |
| Schedules | 1 | 10 | 50 | Unlimited |
| Workflows | — | 5 | 25 | Unlimited |
| Webhooks | 1 | 5 | 20 | Unlimited |
| Max payload size | 64 KB | 256 KB | 1 MB | 5 MB |
| API rate (req/sec) | 5 | 25 | 100 | 500 |
| API burst | 10 | 50 | 200 | 1000 |
| Job retention | 3 days | 14 days | 30 days | 90 days |
| History retention | 1 days | 7 days | 30 days | 90 days |
Rate Limiting
API rate limits use a sliding window algorithm. When you exceed the limit, requests return
429 Too Many Requests with a Retry-After header.
Rate Limit Headers
Every API response includes rate limit information:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705320000 | Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Handling Rate Limits
Production Ready: All official SDKs (Node.js, Python, Go, PHP) include built-in rate limit handling with automatic retries. See the SDK documentation for details.
import { SpooledClient, RateLimitError } from '@spooled/sdk';
const client = new SpooledClient({ apiKey: process.env.SPOOLED_API_KEY! });
async function createJobWithRetry(payload, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.jobs.create(payload);
} catch (error) {
if (error instanceof RateLimitError) {
const retryAfter = error.getRetryAfter();
console.log(`Rate limited, retrying in ${retryAfter}s...`);
await sleep(retryAfter * 1000);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
} Quota Enforcement
Quotas are enforced at different granularities:
Daily Quotas
Jobs per day resets at midnight UTC. When exceeded:
{
"error": {
"code": "quota_exceeded",
"message": "Daily job quota exceeded",
"quota": {
"limit": 1000,
"used": 1000,
"resets_at": "2025-01-16T00:00:00Z"
}
}
} Resource Quotas
Resource limits (queues, workers, etc.) return:
{
"error": {
"code": "resource_limit_exceeded",
"message": "Maximum queues limit reached",
"limit": 2,
"current": 2,
"resource": "queues"
}
} Payload Size
Job payloads exceeding the limit are rejected:
{
"error": {
"code": "payload_too_large",
"message": "Payload exceeds maximum size",
"max_bytes": 65536,
"actual_bytes": 128000
}
} Automatic Limit Enforcement
Spooled automatically enforces plan limits before operations are executed, ensuring you never exceed your quotas unexpectedly.
Operations with Automatic Enforcement
- • POST /api/v1/jobs
- • POST /api/v1/jobs/bulk
- • gRPC Enqueue
- • POST /api/v1/workflows
- • Counts all jobs in DAG
- • POST /api/v1/schedules/trigger
- • Automatic cron triggers
- • POST /api/v1/jobs/dlq/retry
- • Validates before reactivating
Limit Exceeded Response Format
When a limit is exceeded, all endpoints return 403 Forbidden with details:
{
"error": "limit_exceeded",
"message": "active jobs limit reached (10/10). Upgrade to starter for higher limits.",
"resource": "active_jobs",
"current": 10,
"limit": 10,
"plan": "free",
"upgrade_to": "starter"
}
For gRPC operations, the status code is RESOURCE_EXHAUSTED with the same error details in the message.
Monitoring Usage
Track your usage via the API or dashboard:
# Get current usage and plan limits
curl https://api.spooled.cloud/api/v1/organizations/usage \
-H "Authorization: Bearer YOUR_API_KEY"
# Response
{
"plan": {
"tier": "starter",
"display_name": "Starter"
},
"usage": {
"jobs_today": {
"current": 1250,
"limit": 100000,
"percentage": 1.25
},
"active_jobs": {
"current": 45,
"limit": 100,
"percentage": 45.0
},
"queues": { "current": 8, "limit": 25 },
"workers": { "current": 12, "limit": 25 }
}
} The response includes job counts, resource usage, and API call statistics for the current period.
Increasing Limits
Upgrade Your Plan
The easiest way to get higher limits is to upgrade to a paid plan at /pricing.
Enterprise Custom Limits
Enterprise customers can negotiate custom limits. Contact sales@spooled.cloud to discuss your requirements.
Self-Hosted
Self-hosted deployments can configure plan limits via environment variables on the backend. The values shown above are Spooled Cloud defaults; your self-hosted instance may differ.
Backend env vars: configure plan defaults with
SPOOLED_PLAN_LIMITS_JSON, SPOOLED_PLAN_<TIER>_LIMITS_JSON, or per-field
SPOOLED_PLAN_<TIER>_* variables. Per-org DB overrides (custom_limits) still take highest precedence.
See spooled-backend/.env.example for the full list and precedence.
Best Practices
- Batch operations — Use bulk endpoints (
/jobs/batch) to reduce API calls - Implement backoff — Use exponential backoff when rate limited
- Monitor usage — Set up alerts before hitting quotas
- Use idempotency keys — Safely retry without creating duplicates
- Consolidate payloads — Keep payloads small to stay within limits