> ## Documentation Index
> Fetch the complete documentation index at: https://docs.barker.money/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate Limits

> Anti-abuse limits on the Partner API. There is no usage billing.

## TL;DR

| Environment    | Limit                            | Key prefix |
| -------------- | -------------------------------- | ---------- |
| **Sandbox**    | 100 requests / minute / API key  | `bk_test_` |
| **Production** | 1000 requests / minute / API key | `bk_live_` |

These are **anti-abuse caps**, not usage tiers. We do not bill per call. Revenue is split on-chain via your `BarkerEngine` contract. Need a higher cap for a legitimate workload? Email <a href="mailto:partners@barker.money">[partners@barker.money](mailto:partners@barker.money)</a> with your use case.

## Behavior on overflow

When you exceed the per-minute cap, the next request returns:

```http theme={null}
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{
  "success": false,
  "code": "rate_limited",
  "message": "Rate limit exceeded. Retry after 30s."
}
```

The `Retry-After` header is the number of seconds until the window resets. Wait that long, then resume.

## Recommended client behavior

### Backoff with jitter

Don't retry the same second the window resets — every client doing that creates a thundering herd. Add 0–2 seconds of random jitter on top of `Retry-After`:

```js theme={null}
async function fetchWithBackoff(url, opts) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(url, opts);
    if (res.status !== 429) return res;
    const retryAfter = Number(res.headers.get("retry-after") || "30");
    const jitter = Math.random() * 2;
    await new Promise(r => setTimeout(r, (retryAfter + jitter) * 1000));
  }
  throw new Error("Exhausted retries");
}
```

The official SDKs (`@barker/sdk-js`, `barker-sdk`) implement this automatically.

### Cache aggressively

Most Partner API responses are cacheable for 30–60 seconds without stale data concerns:

| Endpoint                                              | Cache hint                                  |
| ----------------------------------------------------- | ------------------------------------------- |
| `GET /api/partner/products`                           | 60s — product list rarely changes           |
| `GET /api/partner/products/{slug}`                    | 60s — same                                  |
| `GET /api/partner/products/{slug}/apy-history`        | 5 min — daily granularity                   |
| `GET /api/partner/products/{slug}/fee-stats`          | 5 min — same                                |
| `GET /api/partner/products/{slug}/yield-calc`         | No cache — projection depends on user input |
| `GET /api/partner/products/{slug}/position?address=…` | 30s — user balance may have just moved      |
| `GET /api/partner/products/{slug}/health`             | 60s — operational health                    |

A 60s edge cache reduces realistic dashboard refreshing from \~50 req/min to \~1 req/min per concurrent user.

## Things that do **not** count against your limit

* Webhook deliveries we send to your endpoint (those flow `Barker → you`, no API call from you)
* Embed iframe requests from end-user browsers (they hit `app.barker.money`, not the API)
* Hits to `https://api.barker.money/health` (unauthenticated liveness probe)

## What if I get rate-limited and I'm not abusing?

Open `https://portal.barker.money/usage` to see your call distribution by endpoint and timestamp. If a single client process is hammering one endpoint, that's almost always the source. Common causes:

* Polling a single product every second instead of subscribing to webhooks for `apy_change` / `tvl_alert`
* Forgetting to cache `GET /products` at the edge
* Looping over thousands of users to call `/position` per user — instead, ask us about the bulk-position endpoint (waitlist)

Still stuck? Email <a href="mailto:partners@barker.money">[partners@barker.money](mailto:partners@barker.money)</a> with the API key prefix and a 5-minute window — we can pull our access logs and pinpoint it.
