Skip to content

IP allowlist and rate limits

Two controls sit in front of every call you make, in this order: the IP allowlist decides whether the call is looked at, and the rate limiter decides whether it is served now. A call refused by the allowlist never spends your rate budget.

Your client carries a list of addresses it may call from. The list is set by the bank when the client is created and changed through your relationship manager; you cannot edit it through the API.

  • Entries are plain addresses (203.0.113.10) or CIDR blocks (203.0.113.0/24, 2001:db8::/32). IPv4 and IPv6 are both accepted.
  • An empty list means no restriction. The bank will normally insist on a non-empty list for production.
  • The list is checked twice: once at the token endpoint, and again on every subsequent call. Issuing a token from an allowed address does not let the token be used from a disallowed one, and narrowing the list takes effect on the next request, not at token expiry.
  • The address compared is the one the bank’s edge sees the connection coming from. Headers you add (X-Forwarded-For, X-Real-IP) are never trusted. If you call through a NAT gateway or an egress proxy, the allowlist must contain that address.
  • An entry that cannot be parsed is ignored, and an allowlist that exists but matches nothing denies. Make sure every entry you give the bank is a valid address or block.

A refused call is answered with HTTP 403 and:

{
"code": "IP_NOT_ALLOWED",
"message": "Access denied from this IP address"
}

At the token endpoint the same refusal comes back in the native error shape with errorCode IP_NOT_ALLOWED; see Authentication.

Each client has a requests-per-minute budget, stored on the client record and shown on the portal’s API access page. The bank sets it when the client is created (600 per minute if nothing else is specified) and can change it on request; a change applies to your very next call.

How it is counted:

  • A sliding 60-second window, not a calendar minute: at any instant, the number of calls you made in the preceding 60 seconds.
  • One window per client, covering every endpoint together, across all of the bank’s API nodes. A cheap call is refused the moment an expensive polling loop has spent the budget.
  • Only authenticated calls count. A call refused by the allowlist, or with a bad token, is not counted.
  • A refused call is not counted either, so hammering a 429 does not push the reset further out.

Every authenticated response carries three headers, whether or not you are close to the limit:

Header Meaning
X-RateLimit-Limit Your per-minute budget
X-RateLimit-Remaining Calls left in the current sliding window, before this call was counted
X-RateLimit-Reset Unix time in seconds, 60 seconds from now. The window is fully clear by then if you make no further calls

Because the window slides, Remaining recovers gradually as old calls age out rather than jumping back to Limit at Reset.

Exhausting the budget returns HTTP 429 with:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1775054460
Content-Type: application/json
{"code":"RATE_LIMIT_EXCEEDED","message":"Too many requests. Retry after 60 seconds."}

Retry-After is always 60. In practice capacity returns sooner as the window slides, but 60 seconds is the guarantee.

A 429 is produced before your request reaches any business logic. No money moves on a 429, so retrying with the same reference is always safe; see Idempotency.

The token endpoint is unauthenticated, so the per-client window above does not apply to it. It is protected instead by a per-source-address limit at the bank’s edge that refuses short bursts. You should never meet it: fetch one token, cache it, and refresh shortly before expires_in elapses. A client that fetches a token per request will be refused at the edge long before it is refused by the per-minute window.

  • Read X-RateLimit-Remaining on every response and throttle yourself when it gets low, rather than waiting for a 429.
  • On a 429, wait at least Retry-After seconds, then retry with exponential backoff and jitter. Do not retry in a tight loop; refused calls are logged and a sustained burst looks like an incident to the bank.
  • Treat a 429 as a signal from your own code, not the user’s. Queue the work and drain it at a steady rate.
  • Replace polling with webhooks wherever one exists. Balance polling and check-transaction loops are the usual way partners exhaust the window; an inflow or transaction webhook costs you nothing. See Events and payloads.
  • Batch jobs (end-of-day statements, bulk funding) should run at a fixed rate below the limit, not as fast as the network allows.
  • If you have a legitimate sustained need above your budget, ask your relationship manager for a higher limit before go-live rather than after the first 429 in production.

The API access page shows the client’s IP allowlist, its per-minute and per-day limits, and the time and address of its last use.