# Unified Rate Limits

Apideck maps every downstream API's rate-limit signal into three standardized response headers,
`x-downstream-ratelimit-limit`, `x-downstream-ratelimit-remaining`, and `x-downstream-ratelimit-reset`,
so you can handle throttling the same way regardless of which connector you're calling. Not every header
is present on every response: Apideck only includes what the downstream API actually reports.

## Understanding API Rate Limits

Rate limits cap how many requests a client can make within a time window, and every API provider reports
that limit differently. Some use a response header, some use an error body, some report nothing at all.
That inconsistency is what Apideck's unified headers exist to remove: instead of writing per-connector logic
to parse each downstream API's own format, you always read the same three fixed header names, only including
the ones the downstream API actually reported.

## Apideck's Unified Rate Limit Headers

The three standardized headers are:

```
x-downstream-ratelimit-limit       # max requests allowed in the current window
x-downstream-ratelimit-remaining   # requests left before the downstream API throttles you
x-downstream-ratelimit-reset       # epoch timestamp when the window resets (omitted if unknown)
```

Apideck populates these from whatever rate-limit signal the downstream API returns on that call. See
[How It Works](#how-it-works) below for a worked example, and
[Variability in API Responses](#variability-in-api-responses) for what happens when a downstream API
reports less than all three values.

### How It Works

Apideck reads whatever rate-limit format a downstream API returns and translates it into the three
standardized headers above. Here's the Salesforce case end to end:

Salesforce returns a single header:

```
sforce-limit-info: api-usage=18/15000
```

Apideck maps this to:

```
x-downstream-ratelimit-limit: 15000
x-downstream-ratelimit-remaining: 14982
```

Salesforce's header doesn't include a reset time, so `x-downstream-ratelimit-reset` is omitted from the
response entirely rather than sent as an empty or zero value.

### Retry-After Header and Rate Limit Reset

When a downstream API returns a `retry-after` header (seconds until the client should retry), Apideck
converts it into an absolute `x-downstream-ratelimit-reset` epoch timestamp by adding it to the current
time, so your client doesn't have to track elapsed time itself.

For example, if a downstream API returns:

```
Retry-After: 30
```

Apideck computes the reset time as:

```
x-downstream-ratelimit-reset: 1718200800
```

That epoch timestamp is the moment the rate limit resets, so you can schedule your next request against it
directly instead of polling.

### Variability in API Responses

Not every downstream API reports full rate-limit data. Some provide partial information (e.g. a limit but
no reset time, as with Salesforce above), and some provide none at all. Apideck only ever sends the headers
it can populate from what the downstream API actually returned; it does not fabricate values for missing
fields.

If you find a downstream API that returns rate-limit details Apideck isn't mapping to these headers yet,
reach out to Apideck support with the connector name and the raw header/response you're seeing.

## Automatic Retry Handling in TypeScript SDK

The `@apideck/unify` TypeScript SDK automatically retries any Unify API call that comes back with a
`retry-after` header, without any configuration on your part. This automatic-retry behavior is
TypeScript-only today; other language SDKs don't yet retry automatically and must handle `retry-after`
manually using the headers described above.

### SDK Example

Here's an example of how to use the TypeScript SDK with automatic retry handling:

```typescript
import { Apideck } from '@apideck/unify'
import * as errors from '@apideck/unify/models/errors'

const apideck = new Apideck({
  consumerId: '<insert-consumer-id-here>',
  appId: '<insert-application-id-here>',
  apiKey: '<insert-api-key-here>'
})

async function run() {
  try {
    const result = await apideck.accounting.taxRates.list({
      serviceId: 'salesforce',
      filter: {
        assets: true,
        equity: true,
        expenses: true,
        liabilities: true,
        revenue: true
      },
      passThrough: {
        search: 'San Francisco'
      },
      fields: 'id,updated_at'
    })

    for await (const page of result) {
      console.log(page)
    }
  } catch (error) {}
}

run()
```

## Benefits of Unified Rate Limit Headers

Because the header names and format are identical across every connector, you write one rate-limit handler
instead of one per downstream API:

1. **One code path, not one per connector**: `x-downstream-ratelimit-*` means the same thing whether the
   call went to Salesforce, QuickBooks, or any other connector, with no per-connector parsing branch.
2. **Retry logic works off real data**: `x-downstream-ratelimit-reset` gives you an exact epoch timestamp to
   retry against, instead of guessing a backoff interval.
3. **Fewer unnecessary throttles**: reading `x-downstream-ratelimit-remaining` before you burst requests
   lets you slow down before the downstream API rejects a call, rather than after.

## Best Practices for Using Unified Rate Limit Headers

1. **Read `x-downstream-ratelimit-remaining` on every response**, not just after you hit a 429. It tells
   you how much headroom is left before the downstream API throttles you.
2. **Schedule retries from `x-downstream-ratelimit-reset`** (an epoch timestamp) instead of a fixed sleep.
   See [Retry-After Header and Rate Limit Reset](#retry-after-header-and-rate-limit-reset).
3. **Don't assume all three headers are present.** Per [Variability in API Responses](#variability-in-api-responses),
   a downstream API that doesn't report a reset time will simply omit that header, so check for its presence
   before reading it.
4. **On the TypeScript SDK, you get `retry-after` handling for free** (see
   [Automatic Retry Handling](#automatic-retry-handling-in-typescript-sdk)); on other language SDKs, implement
   the backoff yourself using these headers.

## Conclusion

Apideck's unified rate-limit headers let you write throttling and retry logic once, against
`x-downstream-ratelimit-limit`, `x-downstream-ratelimit-remaining`, and `x-downstream-ratelimit-reset`,
instead of maintaining a separate parser for every downstream API's own rate-limit format.
