Rate Limiting
The API enforces rate limits to ensure fair usage and platform stability. Limits are applied per tenant using a sliding-window algorithm.
Default Limits
| Scope | Limit | Window |
|---|---|---|
| General API | 100 requests | 1 minute |
Authentication (/auth/login, /auth/register) | 10 requests | 1 minute |
Token refresh (/auth/refresh) | 30 requests | 1 minute |
Batch operations (/batch) | 20 requests | 1 minute |
Response Headers
Every response includes rate-limit headers so your application can adjust dynamically:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit | Max requests allowed in the window | 100 |
X-RateLimit-Remaining | Requests remaining in current window | 87 |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets | 1718234567 |
Retry-After | Seconds to wait before retrying (only on 429) | 12 |
X-RateLimit-Policy | Human-readable policy description | 100;w=60 |
429 Error Response
When you exceed the limit, the API returns HTTP 429:
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Try again in 12 seconds.",
"status": 429
}
}
SDK — Automatic Backoff
The SDK handles 429 responses automatically with exponential backoff:
import { Urblock } from "@urblock/sdk";
// Built-in retry on 429/5xx with exponential backoff + jitter (up to 3 retries by default)
const urblock = new Urblock(process.env.URBLOCK_API_KEY!, {
maxRetries: 3, // default — set 0 to disable
});
// You can also handle it manually for bulk operations
async function mintBatch(recipients: string[], tokenId: string) {
for (const to of recipients) {
try {
await urblock.tokens.mint(tokenId, {
to,
amount: "1000000000000000000",
});
} catch (err) {
if (err.status === 429) {
const waitMs = (err.retryAfter || 10) * 1000;
console.log(`Rate limited — waiting ${waitMs}ms`);
await new Promise(r => setTimeout(r, waitMs));
// Retry the same item
await urblock.tokens.mint(tokenId, {
to,
amount: "1000000000000000000",
});
} else {
throw err;
}
}
}
}
curl — Checking Headers
curl -i https://api.urblock.io/tokens \
-H "Authorization: Bearer $TOKEN"
# Response headers:
# X-RateLimit-Limit: 100
# X-RateLimit-Remaining: 99
# X-RateLimit-Reset: 1718234567
Monitoring Rate Limits
Build a simple monitor to log your usage:
const response = await fetch("https://api.urblock.io/tokens", {
headers: { Authorization: `Bearer ${token}` },
});
const remaining = parseInt(response.headers.get("x-ratelimit-remaining") || "0");
const limit = parseInt(response.headers.get("x-ratelimit-limit") || "100");
const usage = ((limit - remaining) / limit * 100).toFixed(1);
console.log(`Rate limit usage: ${usage}% (${remaining}/${limit} remaining)`);
if (remaining < 10) {
console.warn("⚠️ Approaching rate limit — consider throttling requests");
}
Best Practices
- Read
Retry-After— always respect the header instead of using a fixed wait - Use batch endpoints —
/batchprocesses multiple operations in one request - Cache responses —
GET /networksandGET /tokens/:idrarely change - Spread bulk work — distribute requests evenly across the window instead of bursting
- Monitor headers — track
X-RateLimit-Remainingto preemptively throttle - Use webhooks over polling — webhooks don't count toward your rate limit
Batch Operations
A single batch request counts as one request regardless of how many operations it contains. Use Batch Operations to minimize rate-limit consumption.
Next Steps
- Batch Operations — execute multiple operations in one request
- Pagination — efficiently page through large result sets
- Webhooks Overview — replace polling with push events
- Errors — understand error responses and handling