Rate Limits
Asterwise applies three independent rate limits per request:
- IP burst limit — protects against unauthenticated abuse from a single IP
- Tier monthly quota — the call budget for your plan, resets monthly
- Tier burst limit — caps the rate of authenticated requests within a single minute
All three limits emit the canonical error envelope with a specific error code and an HTTP Retry-After header. Standard HTTP retry middleware (urllib3 Retry, axios-retry, fetch-retry, Cloudflare, AWS WAF) will automatically honor the header without custom code.
Plan quotas
| Plan | Monthly calls | Price | Tier burst limit (per minute) |
|---|---|---|---|
| Sandbox | 500 | Free | 10 |
| Builder | 15,000 | $39 | 60 |
| Launch | 60,000 | $99 | 200 |
| Scale | 300,000 | $249 | 500 |
Calls that fail with a 4xx status code are not counted against your monthly quota. Calls that return 5xx are not counted either — only successful 2xx responses consume quota.
IP burst limit (unauthenticated)
Every IP is limited to 60 requests per minute across all endpoints. This applies before authentication, so requests without an API key still count.
When triggered, the response is:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-Request-Id: <uuid>
{
"success": false,
"error": "ip_rate_limit_exceeded",
"message": "Too many requests from this IP address. Try again in 60 seconds.",
"details": [],
"retry_after": 60,
"doc_url": "https://docs.asterwise.com/reference/errors/ip_rate_limit_exceeded",
"request_id": "<uuid>",
"timestamp": "<ISO 8601 UTC>"
}
Tier monthly quota
When you exceed your monthly quota, the response is:
HTTP/1.1 429 Too Many Requests
X-Request-Id: <uuid>
{
"success": false,
"error": "monthly_usage_limit_exceeded",
"message": "You have reached your monthly call limit. Upgrade at asterwise.com/dashboard to get more calls.",
"details": [],
"retry_after": null,
"doc_url": "https://docs.asterwise.com/reference/errors/monthly_usage_limit_exceeded",
"request_id": "<uuid>",
"timestamp": "<ISO 8601 UTC>"
}
retry_after is null for monthly limits because the wait time depends on the calendar — your quota resets on the 1st of each month UTC.
Tier burst limit (authenticated)
Authenticated requests are also rate-limited per minute by tier:
| Tier | Burst limit (per minute) |
|---|---|
| Sandbox | 10 |
| Builder | 60 |
| Launch | 200 |
| Scale | 500 |
When exceeded:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-Request-Id: <uuid>
{
"success": false,
"error": "burst_limit_exceeded",
"message": "Too many requests in the last minute. Try again shortly.",
"details": [],
"retry_after": 60,
"doc_url": "https://docs.asterwise.com/reference/errors/burst_limit_exceeded",
"request_id": "<uuid>",
"timestamp": "<ISO 8601 UTC>"
}
Response headers
Every rate-limit response carries:
| Header | Value | When set |
|---|---|---|
Retry-After | Integer seconds | On every retry-bearing rate-limit response. Conforms to RFC 7231 §7.1.3. |
X-Request-Id | UUID | Always. Use for support tracing. |
X-RateLimit-Limit | Integer | Your monthly call budget. |
X-RateLimit-Plan | String | Your plan name (sandbox, builder, launch, scale). |
Subscription expiry
Distinct from rate limits but on the same code path: if your subscription expires, requests return subscription_expired with a 402 status code. The message includes the upgrade URL. No Retry-After — renew your subscription at the dashboard.
Best practices
Honor Retry-After
Most HTTP clients respect Retry-After automatically:
- Python
- TypeScript
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
respect_retry_after_header=True, # default in urllib3 1.26+
)
session.mount("https://", HTTPAdapter(max_retries=retry_strategy))
response = session.get(
"https://api.asterwise.com/v1/astro/ayanamsha",
headers={"Authorization": "Bearer YOUR_KEY"},
)
import axios from "axios";
import axiosRetry from "axios-retry";
const client = axios.create({
baseURL: "https://api.asterwise.com",
headers: { Authorization: "Bearer YOUR_KEY" },
});
axiosRetry(client, {
retries: 3,
retryCondition: (error) => error.response?.status === 429,
retryDelay: (retryCount, error) => {
const retryAfter = error.response?.headers["retry-after"];
return retryAfter ? parseInt(retryAfter, 10) * 1000 : 1000;
},
});
const response = await client.get("/v1/astro/ayanamsha");
Distribute calls across the minute
For high-throughput applications, smooth your request rate. If you have a daily batch of 1,000 charts to compute on the Builder plan (60/min burst limit), distribute them across the day rather than firing them all at once.
Use idempotency for retry safety
GET requests are idempotent by definition. POST endpoints are also idempotent on Asterwise — sending the same payload twice produces the same chart, no duplicate side effects. Safe to retry POSTs without idempotency keys.
Monitor X-RateLimit-Limit proactively
Track your monthly consumption against X-RateLimit-Limit so you can upgrade before you hit the quota wall. The dashboard at asterwise.com/dashboard shows real-time usage.
What happens during a Redis outage
Asterwise uses Redis to track rate-limit counters. If Redis is temporarily unreachable, rate limiting fails open — your requests succeed even if you would have been rate-limited. This is intentional: availability outweighs perfect quota enforcement during an infrastructure incident. Monthly quota tracking resumes when Redis recovers.
You will not be charged for requests served during a Redis outage unless they would have counted normally.