Rate Limits
URAPIS enforces rate limits to ensure fair usage across all consumers and protect upstream servers from excessive load.
How Rate Limits Work
Each API provider on URAPIS configures rate limits for their subscription plans. When a consumer exceeds the rate limit, the URAPIS Gateway intercepts the request before it reaches the provider’s server and returns a 429 Too Many Requests response. Because the request is blocked at the gateway level, the consumer’s wallet balance is not charged.
However, rate limits can also be enforced by the upstream provider’s own infrastructure, independently of URAPIS. These two types of 429 errors have different implications for your wallet balance.
Two Sources of 429 Errors
| Aspect | URAPIS Gateway | Upstream Provider |
|---|---|---|
| Where the limit is enforced | URAPIS Gateway, before the request reaches the provider | The provider’s own server, after the request passes through URAPIS |
| Wallet charged? | No — the request never left the gateway | Yes — the request reached the provider’s infrastructure |
| Response body format | Structured JSON: {"error":true,"code":"RATE_LIMITED","message":"..."} | Provider-dependent (JSON, HTML, or plain text) |
| Can you control it? | Yes — slow down your request rate | No — the provider defines their own internal limits |
Identifying the Source of a 429
The most reliable way to distinguish between the two sources is by inspecting the response body.
URAPIS Gateway 429
If the response body contains "code": "RATE_LIMITED", the error came from the URAPIS Gateway and your wallet was not charged:
{"error": true, "code": "RATE_LIMITED", "message": "API quota or rate limit exceeded"}
Upstream Provider 429
If the response body does not contain "code": "RATE_LIMITED" (or the body is not JSON at all), the error came from the upstream provider and your wallet was charged.
Why Upstream 429s Still Deduct Balance
URAPIS performs wallet debiting atomically before forwarding a request to the upstream server. If the request successfully reaches the upstream server (i.e., it passed the URAPIS rate limiter), the transaction is counted. A 429 response from the upstream does not trigger a refund because, from URAPIS’s perspective, the request was successfully delivered.
Balance refunds only occur when:
- A network error or timeout prevents the request from reaching the upstream server
- The upstream server returns a 5xx (500–599) error, indicating an internal failure
Client-Side Handling
Exponential Backoff
Exponential backoff is the recommended strategy for handling both types of 429 errors. It prevents you from sending requests too quickly and reduces wasted balance from upstream 429s.
async function fetchWithBackoff(url, options = {}, maxRetries = 5) {
const baseDelay = 1000; // 1 second
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const body = await response.clone().json().catch(() => ({}));
if (body.code === "RATE_LIMITED") {
console.warn("Rate limited by URAPIS Gateway — balance safe, retrying...");
} else {
console.warn("Rate limited by upstream provider — balance charged, retrying...");
}
if (attempt === maxRetries) {
throw new Error(`Max retries (${maxRetries}) exceeded`);
}
const delay = baseDelay * Math.pow(2, attempt);
const jitter = delay * 0.1 * Math.random();
await new Promise(resolve => setTimeout(resolve, delay + jitter));
}
}
Rate Limit Awareness (Optional)
Some upstream providers include rate limit headers in their responses. If your provider supports these, you can adjust your request rate proactively:
async function fetchWithLimitAwareness(url, options = {}) {
const response = await fetch(url, options);
const remaining = response.headers.get("X-RateLimit-Remaining");
const reset = response.headers.get("X-RateLimit-Reset");
if (remaining !== null && parseInt(remaining, 10) < 5) {
const waitTime = reset
? Math.max(0, parseInt(reset, 10) * 1000 - Date.now())
: 1000;
console.warn(`Rate limit low (remaining: ${remaining}), pausing ${waitTime}ms`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
return response;
}
Note: URAPIS Gateway does not include
X-RateLimit-*headers in its 429 responses. These headers are only available if the upstream provider explicitly includes them.
FAQ
How do I know if my balance was charged?
Check the response body of the 429 error:
- If it contains
"code": "RATE_LIMITED"→ gateway 429 → balance not charged - If it does not contain
"code": "RATE_LIMITED"→ upstream 429 → balance was charged
Why was I charged even though my request failed?
From URAPIS’s perspective, your request did not fail — it successfully reached the provider’s server. Billing is based on whether the request reached the upstream, not the final HTTP status code.
What should I do if I keep getting upstream 429s?
- Check the provider’s API documentation for their official rate limits
- Reduce your request rate — use exponential backoff or a client-side rate limiter
- Contact the provider if you need a higher limit
- Cache responses for data that doesn’t change frequently to reduce total request volume
Best Practices
- Always implement exponential backoff in your client code to avoid wasting balance from rapid retries
- Inspect the response body of 429 errors to distinguish between gateway and upstream limits before deciding how to respond
- Monitor your logs — frequent upstream 429s indicate your request rate needs adjustment
- Use response caching for infrequently changing data (reference data, configuration)
- Review the provider’s API documentation to understand their specific rate limit thresholds