Pular para o conteúdo principal

Visão Geral

A API Owem implementa rate limiting para garantir estabilidade e qualidade do serviço para todos os clientes.
Os limites são aplicados por API Key e por endpoint.

Limites Padrão

TipoLimitePeríodo
Global1000 req1 minuto
Por Endpoint100 req1 minuto
Transferências PIX60 req1 minuto
Geração de QR Code120 req1 minuto
Limites podem variar conforme seu plano. Contate suporte para aumentar limites.

Headers de Rate Limit

A API retorna headers informativos em cada resposta:
HeaderDescriçãoExemplo
X-RateLimit-LimitLimite total do período1000
X-RateLimit-RemainingRequisições restantes847
X-RateLimit-ResetTimestamp de reset (epoch)1766610300

Exemplo de Resposta

HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1766610300
Content-Type: application/json

{"success": true, ...}

Quando Exceder o Limite

Ao exceder o rate limit, você receberá:
{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": 429,
  "success": false,
  "message": "Too many requests",
  "code": "RATE_LIMIT_EXCEEDED"
}

Headers Adicionais em 429

HeaderDescrição
Retry-AfterSegundos para aguardar antes de retry

Implementando Controle de Tráfego

Exponential Backoff

Implemente retry com backoff exponencial para erros 429:
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 (Lado do Cliente)

Controle a taxa de requisições do seu lado:
class RateLimiter {
  constructor(maxTokens, refillRate) {
    this.maxTokens = maxTokens;
    this.tokens = maxTokens;
    this.refillRate = refillRate; // tokens por segundo
    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;
  }
}

// Uso: 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}`, { ... });
}

Queue de Requisições

Para alto volume, use uma fila:
import PQueue from "p-queue"

// 10 requisições simultâneas, 100ms entre cada
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))
}

// Uso
const results = await Promise.all([
  queuedRequest("/v4/i/ledger?page=1"),
  queuedRequest("/v4/i/ledger?page=2"),
  queuedRequest("/v4/i/ledger?page=3"),
  // ... centenas de requisições serão enfileiradas
])

Boas Práticas

Monitore os Headers

Acompanhe X-RateLimit-Remaining para antecipar throttling.

Cache de Consultas

Cache consultas frequentes (saldo, ledger) para reduzir requisições.

Batch quando Possível

Use paginação otimizada ao invés de múltiplas requisições pequenas.

Webhooks > Polling

Use webhooks para eventos ao invés de polling constante.

Exemplo Completo

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,
    })

    // Atualiza limiter com dados reais
    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()
  }

  // Métodos de conveniência
  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)
  }
}

Suporte

Se você precisa de limites maiores para seu caso de uso, entre em contato: