Overview
The Owem API implements rate limiting to ensure stability and service quality for all customers.
Limits are applied per API Key and per endpoint.
Default Limits
| Type | Limit | Period |
|---|
| Global | 1000 req | 1 minute |
| Per Endpoint | 100 req | 1 minute |
| PIX Transfers | 60 req | 1 minute |
| QR Code Generation | 120 req | 1 minute |
Limits may vary according to your plan. Contact support to increase limits.
The API returns informative headers in each response:
| Header | Description | Example |
|---|
X-RateLimit-Limit | Total limit for the period | 1000 |
X-RateLimit-Remaining | Remaining requests | 847 |
X-RateLimit-Reset | Reset timestamp (epoch) | 1766610300 |
Response Example
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1766610300
Content-Type: application/json
{"success": true, ...}
When Limit is Exceeded
When exceeding the rate limit, you’ll receive:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": 429,
"success": false,
"message": "Too many requests",
"code": "RATE_LIMIT_EXCEEDED"
}
| Header | Description |
|---|
Retry-After | Seconds to wait before retry |
Implementing Traffic Control
Exponential Backoff
Implement retry with exponential backoff for 429 errors:
async function callWithRetry(fn, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn()
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000 // 1s, 2s, 4s...
console.log(`Rate limited. Retry in ${delay}ms`)
await sleep(delay)
continue
}
throw error
}
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
Token Bucket (Client Side)
Control your request rate on your side:
class RateLimiter {
constructor(maxTokens, refillRate) {
this.maxTokens = maxTokens;
this.tokens = maxTokens;
this.refillRate = refillRate; // tokens per second
this.lastRefill = Date.now();
}
async acquire() {
this.refill();
if (this.tokens < 1) {
const waitTime = (1 - this.tokens) / this.refillRate * 1000;
await sleep(waitTime);
this.refill();
}
this.tokens -= 1;
return true;
}
refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}
// Usage: 100 req/min = ~1.67 req/s
const limiter = new RateLimiter(10, 1.67);
async function callOwemAPI(endpoint) {
await limiter.acquire();
return fetch(`https://api.owem.com.br${endpoint}`, { ... });
}
Request Queue
For high volume, use a queue:
import PQueue from "p-queue"
// 10 simultaneous requests, 100ms between each
const queue = new PQueue({ concurrency: 10, interval: 100, intervalCap: 1 })
async function queuedRequest(endpoint, options) {
return queue.add(() => fetch(`https://api.owem.com.br${endpoint}`, options))
}
// Usage
const results = await Promise.all([
queuedRequest("/v4/i/ledger?page=1"),
queuedRequest("/v4/i/ledger?page=2"),
queuedRequest("/v4/i/ledger?page=3"),
// ... hundreds of requests will be queued
])
Best Practices
Monitor Headers
Track X-RateLimit-Remaining to anticipate throttling.
Cache Queries
Cache frequent queries (balance, ledger) to reduce requests.
Batch When Possible
Use optimized pagination instead of multiple small requests.
Webhooks > Polling
Use webhooks for events instead of constant polling.
Complete Example
class OwemClient {
constructor(apiKey, apiSecret) {
this.baseUrl = "https://api.owem.com.br"
this.token = Buffer.from(`${apiKey}:${apiSecret}`).toString("base64")
this.limiter = new RateLimiter(100, 1.67)
}
async request(method, endpoint, body = null) {
await this.limiter.acquire()
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method,
headers: {
Authorization: `Basic ${this.token}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : null,
})
// Update limiter with real data
const remaining = parseInt(response.headers.get("X-RateLimit-Remaining"))
const reset = parseInt(response.headers.get("X-RateLimit-Reset"))
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After")) || 60
console.warn(`Rate limited. Waiting ${retryAfter}s`)
await sleep(retryAfter * 1000)
return this.request(method, endpoint, body)
}
return response.json()
}
// Convenience methods
getBalance(accountId) {
return this.request("GET", `/v4/i/bank-accounts/${accountId}/balance`)
}
createQrCode(data) {
return this.request("POST", "/v4/i/pix/in/dynamic-qrcode", data)
}
}
Support
If you need higher limits for your use case, get in touch: