Rate Limiting

A BSPAY aplica rate limiting por endpoint e bloqueio anti-abuso por credencial. Excedê-los retorna 429 Too Many Requests.

A BSPAY aplica rate limiting por endpoint e bloqueio anti-abuso por credencial. Excedê-los retorna 429 Too Many Requests.

Limites por endpoint

EndpointLimiteNotas
POST /v2/oauth/token30 reqs/min/IPApós 5 falhas seguidas → IP bloqueado por 5min (IP_BLOCKED_TEMPORARILY)
GET /v2/account/balance120 reqs/min
GET /v2/account/info/profile60 reqs/min
GET /v2/account/limits60 reqs/min
GET /v2/account/fees60 reqs/min
POST /v2/transactions/cashin12 000 reqs/minAlta vazão pra checkout
POST /v2/transactions/cashout6 000 reqs/minExige HMAC
POST /v2/transactions/wallet30 reqs/min
POST /v2/internal_transfers/payment10 reqs/minAnti-enumeração de usernames
POST /v2/conversions/rate120 reqs/minCotação spot
POST /v2/conversions/simulate60 reqs/min
POST /v2/conversions/new30 reqs/minExige HMAC
POST /v2/account/transactions/list300 reqs/min
GET /v2/account/infractions60 reqs/min
POST /v2/account/infractions/reply10 reqs/minMultipart upload

Bloqueio anti-abuso

Independente do rate limit por endpoint, há um bloqueio global por erros consecutivos:

CondiçãoLimiteAção
Erros 4xx por credencial15 erros/minBloqueio temporário da credencial
Falhas de auth por IP5 erros/5minIP_BLOCKED_TEMPORARILY (5min)

A ideia é proteger contra:

  • Brute-force de credenciais (/oauth/token)
  • Enumeração de username ou chaves PIX

Resposta 429

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Try again later.",
    "group": "SECURITY",
    "retryable": true,
    "details": {
      "retry_after_seconds": 12
    }
  }
}

Headers de resposta

Toda resposta (sucesso ou erro) inclui:

HeaderDescrição
X-RateLimit-LimitLimite total da janela (reqs/min)
X-RateLimit-RemainingReqs restantes na janela atual
X-RateLimit-ResetUnix timestamp de quando o contador zera
Retry-AfterSegundos pra esperar (presente apenas em 429)

Boas práticas

1. Cache do access token

Token tem validade de 1h — não regenere a cada request.

Python
Node.js
import time

class TokenCache:
    def __init__(self):
        self.token = None
        self.expires_at = 0

    def get(self):
        if self.token and time.time() < self.expires_at - 60:
            return self.token
        self.token = self._fetch_new()
        self.expires_at = time.time() + 3600
        return self.token

2. Retry com backoff exponencial

Em 429, respeite Retry-After antes de retentar. Para 502/503, use backoff exponencial (1s → 2s → 4s → 8s).

import time, requests

def with_retry(method, url, max_retries=4, **kwargs):
    for attempt in range(max_retries):
        res = requests.request(method, url, **kwargs)

        if res.status_code == 429:
            wait = int(res.headers.get('Retry-After', 2 ** attempt))
            time.sleep(wait)
            continue

        if res.status_code in (502, 503):
            time.sleep(2 ** attempt)
            continue

        return res

    raise Exception("max_retries exceeded")

3. Webhook em vez de polling

Polling em transactions/list desperdiça quota. Configure postback_url e reaja ao webhook cashin.confirmed / cashout.confirmed. Reconcile via list só periodicamente (1×/hora).

4. Nonce + idempotência em rotas com HMAC

X-Nonce deve ser único nos últimos 5 min. Se quiser retry seguro:

  • Mesmo external_id + mesmo body + nonce novo → idempotente (segunda chamada retorna a transação original)
  • Mesmo external_id + body diferente → erro DUPLICATE_EXTERNAL_ID

Erros relacionados

CódigoHTTPAção
RATE_LIMIT_EXCEEDED429Espere Retry-After segundos
IP_BLOCKED_TEMPORARILY403Espere 5 minutos — não retry imediato
IP_BLACKLISTED403Contate suporte — bloqueio permanente
SECURITY_BLOCKED423Conta sob revisão de segurança

Esta página foi útil?