All authenticated Swarms API responses include rate limit headers. These headers let you monitor your usage programmatically — build retry logic, display quota dashboards, or throttle requests before hitting limits.
Rate limit headers are included on every authenticated response, including error responses. You do not need to make a separate API call to check your quota.
Every response includes these headers:
| Header | Type | Description |
|---|
X-RateLimit-Limit-Minute | integer | Maximum requests allowed per minute for your tier |
X-RateLimit-Remaining-Minute | integer | Requests remaining in the current minute window |
X-RateLimit-Limit-Day | integer | Maximum requests allowed per day for your tier |
X-RateLimit-Remaining-Day | integer | Requests remaining in the current day window |
X-RateLimit-Reset | unix timestamp | When the current minute window resets (seconds since epoch) |
X-RateLimit-Tier | string | Your current tier: free or premium |
Retry-After | integer | Seconds until the rate limit resets. Only present on 429 responses. |
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit-Minute: 100
X-RateLimit-Remaining-Minute: 94
X-RateLimit-Limit-Day: 1200
X-RateLimit-Remaining-Day: 1047
X-RateLimit-Reset: 1713700800
X-RateLimit-Tier: free
Example 429 Response
When you exceed a rate limit, the response includes a Retry-After header:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit-Minute: 100
X-RateLimit-Remaining-Minute: 0
X-RateLimit-Limit-Day: 1200
X-RateLimit-Remaining-Day: 1100
X-RateLimit-Reset: 1713700860
X-RateLimit-Tier: free
Retry-After: 42
{
"detail": "Rate limit exceeded for minute window(s). Upgrade to Premium for increased limits (2,000/min, 10,000/hour, 100,000/day) at https://swarms.world/platform/account for just $100/month."
}
Limits by Tier
| Free | Premium |
|---|
| Per minute | 100 | 2,000 |
| Per hour | 50 | 10,000 |
| Per day | 1,200 | 100,000 |
| Tokens per agent | 200,000 | 2,000,000 |
Code Examples
Python
TypeScript
Rust
Go
cURL
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("SWARMS_API_KEY")
BASE_URL = "https://api.swarms.world"
headers = {
"x-api-key": API_KEY,
"Content-Type": "application/json",
}
response = requests.post(
f"{BASE_URL}/v1/agent/completions",
headers=headers,
json={
"agent_config": {
"agent_name": "test",
"model_name": "gpt-4o",
"system_prompt": "You are helpful.",
"max_loops": 1,
},
"task": "Say hello.",
},
)
# Read rate limit headers
remaining = int(response.headers["X-RateLimit-Remaining-Minute"])
limit = int(response.headers["X-RateLimit-Limit-Minute"])
tier = response.headers["X-RateLimit-Tier"]
print(f"Tier: {tier} | {remaining}/{limit} requests remaining this minute")
import "dotenv/config";
const API_KEY = process.env.SWARMS_API_KEY;
const BASE_URL = "https://api.swarms.world";
if (!API_KEY) {
throw new Error("SWARMS_API_KEY is not set");
}
const response = await fetch(`${BASE_URL}/v1/agent/completions`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_config: {
agent_name: "test",
model_name: "gpt-4o",
system_prompt: "You are helpful.",
max_loops: 1,
},
task: "Say hello.",
}),
});
// Read rate limit headers
const remaining = parseInt(response.headers.get("X-RateLimit-Remaining-Minute") ?? "0");
const limit = parseInt(response.headers.get("X-RateLimit-Limit-Minute") ?? "0");
const tier = response.headers.get("X-RateLimit-Tier");
console.log(`Tier: ${tier} | ${remaining}/${limit} requests remaining this minute`);
use std::env;
use reqwest::blocking::Client;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = env::var("SWARMS_API_KEY")
.expect("SWARMS_API_KEY environment variable is required");
let client = Client::new();
let response = client
.post("https://api.swarms.world/v1/agent/completions")
.header("x-api-key", &api_key)
.header("Content-Type", "application/json")
.body(r#"{
"agent_config": {
"agent_name": "test",
"model_name": "gpt-4o",
"system_prompt": "You are helpful.",
"max_loops": 1
},
"task": "Say hello."
}"#)
.send()?;
// Read rate limit headers
let remaining = response.headers()
.get("X-RateLimit-Remaining-Minute")
.and_then(|v| v.to_str().ok())
.unwrap_or("0");
let limit = response.headers()
.get("X-RateLimit-Limit-Minute")
.and_then(|v| v.to_str().ok())
.unwrap_or("0");
let tier = response.headers()
.get("X-RateLimit-Tier")
.and_then(|v| v.to_str().ok())
.unwrap_or("unknown");
println!("Tier: {} | {}/{} requests remaining this minute", tier, remaining, limit);
Ok(())
}
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
)
func main() {
apiKey := os.Getenv("SWARMS_API_KEY")
if apiKey == "" {
log.Fatal("SWARMS_API_KEY environment variable is required")
}
body := strings.NewReader(`{
"agent_config": {
"agent_name": "test",
"model_name": "gpt-4o",
"system_prompt": "You are helpful.",
"max_loops": 1
},
"task": "Say hello."
}`)
req, err := http.NewRequest("POST", "https://api.swarms.world/v1/agent/completions", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("x-api-key", apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// Read rate limit headers
remaining := resp.Header.Get("X-RateLimit-Remaining-Minute")
limit := resp.Header.Get("X-RateLimit-Limit-Minute")
tier := resp.Header.Get("X-RateLimit-Tier")
fmt.Printf("Tier: %s | %s/%s requests remaining this minute\n", tier, remaining, limit)
}
# The -i flag prints response headers
curl -i -X POST https://api.swarms.world/v1/agent/completions \
-H "x-api-key: $SWARMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_config": {
"agent_name": "test",
"model_name": "gpt-4o",
"system_prompt": "You are helpful.",
"max_loops": 1
},
"task": "Say hello."
}'
Retry Logic
Use the Retry-After header to implement automatic retry on 429 responses:
Python
TypeScript
Rust
Go
import time
import requests
def call_with_retry(url, headers, payload, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retrying in {retry_after}s...")
time.sleep(retry_after)
continue
return response
raise Exception("Max retries exceeded")
async function callWithRetry(
url: string,
headers: Record<string, string>,
payload: object,
maxRetries = 3
): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(payload),
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "60");
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
}
throw new Error("Max retries exceeded");
}
use reqwest::blocking::{Client, Response};
use std::thread;
use std::time::Duration;
fn call_with_retry(
client: &Client,
url: &str,
api_key: &str,
body: &str,
max_retries: u32,
) -> Result<Response, Box<dyn std::error::Error>> {
for _ in 0..max_retries {
let response = client
.post(url)
.header("x-api-key", api_key)
.header("Content-Type", "application/json")
.body(body.to_string())
.send()?;
if response.status().as_u16() == 429 {
let retry_after: u64 = response.headers()
.get("Retry-After")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse().ok())
.unwrap_or(60);
println!("Rate limited. Retrying in {}s...", retry_after);
thread::sleep(Duration::from_secs(retry_after));
continue;
}
return Ok(response);
}
Err("Max retries exceeded".into())
}
package main
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
func callWithRetry(url, apiKey, body string, maxRetries int) (*http.Response, error) {
for attempt := 0; attempt < maxRetries; attempt++ {
req, err := http.NewRequest("POST", url, strings.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("x-api-key", apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode == 429 {
retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
if retryAfter == 0 {
retryAfter = 60
}
fmt.Printf("Rate limited. Retrying in %ds...\n", retryAfter)
resp.Body.Close()
time.Sleep(time.Duration(retryAfter) * time.Second)
continue
}
return resp, nil
}
return nil, fmt.Errorf("max retries exceeded")
}
Best Practices
- Check
Remaining-Minute before sending requests — if it’s low, slow down or queue requests.
- Use
Retry-After on 429s — don’t guess the wait time, the header tells you exactly how long.
- Log your tier — use
X-RateLimit-Tier to confirm your account is on the expected plan.
- Build dashboards — track
Remaining-Day over time to understand your usage patterns and plan upgrades.