---
title: Rate Limits and Usage | Tabstack
description: How Tabstack's per-minute request rate limits work, what the 429 response carries, and where to see your usage and quota.
---

Two different things get confused here, so keep them apart. **Rate limits** are requests per minute: exceed them and you get a `429`. **Usage** is dollars and credits you have consumed: a billing concern you read on a different surface, with nothing to do with the `429`. If you are being rate-limited, you do not need to look at your spend.

---

## Rate limits

Rate limits are **per account**. Not per key, not per endpoint. Every key on the account draws from the same per-minute budget, and every endpoint counts against it.

| Plan    | Requests per minute |
| ------- | ------------------- |
| Trial   | 10                  |
| Starter | 15                  |
| Team    | 25                  |
| Pro     | 100                 |

**Per minute is the only request ceiling.** There is no daily or hourly request cap. If you stay under your per-minute limit you can keep calling all day, and the only other ceiling is your credit balance, which is a spend question rather than a rate question. See [Pricing](/pricing/index.md) for per-endpoint credit costs and monthly credit allowances.

Higher limits than the table are available. Contact support if your workload needs one.

### Read the headers, never hit the wall

Every response carries rate limit headers, not just `429`s. This is the part to build around: read your remaining budget off any successful call and slow down before you run out.

| Header                  | Meaning                                                |
| ----------------------- | ------------------------------------------------------ |
| `X-RateLimit-Limit`     | The ceiling for the current window                     |
| `X-RateLimit-Remaining` | Requests left in the current window                    |
| `X-RateLimit-Reset`     | When the window resets, as a Unix timestamp in seconds |

A successful response looks like this:

```
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1717459200
Content-Type: application/json
```

### When you exceed the limit

You get a `429` with the standard error shape, a single `error` field. See the [Error Reference](/production/error-reference/index.md) for the full shape. The `429` also carries a `Retry-After` header telling you how many seconds to wait:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json


{ "error": "rate limit exceeded" }
```

`Retry-After` is the length of the rate limit window, so it is always `60`. It is not a countdown to the moment your budget frees up. If you need to know when the current window actually resets, read `X-RateLimit-Reset` instead: waiting the full 60 seconds is always safe, but often longer than you need.

The SDKs retry `429` automatically with exponential backoff, honoring `Retry-After`, so most developers using an SDK never handle this by hand. See [Production Reliability](/guides/production-reliability/index.md) for the retry and backoff behavior.

### Handling example

The better pattern is proactive: read `X-RateLimit-Remaining` from a normal response and back off before you reach zero. The reactive fallback is to catch the `429` and read `Retry-After`.

- [TypeScript](#tab-panel-266)
- [Python](#tab-panel-267)

```
import Tabstack, { RateLimitError } from "@tabstack/sdk";


const client = new Tabstack();


// Proactive: back off early using the headers on a successful call.
const { data, response } = await client.extract
  .json({
    url: "https://example.com",
    json_schema: { type: "object", properties: { title: { type: "string" } } },
  })
  .withResponse();


const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
if (remaining < 5) {
  console.warn(`Low on budget: ${remaining} requests left this window`);
  // slow down: reduce concurrency or pause before you hit zero
}


// Reactive fallback: catch the 429 and honor Retry-After.
try {
  await client.extract.json({
    url: "https://example.com",
    json_schema: { type: "object", properties: { title: { type: "string" } } },
  });
} catch (err) {
  if (err instanceof RateLimitError) {
    const retryAfter = err.headers?.get("Retry-After");
    console.warn(`Rate limited. Retry after ${retryAfter}s`);
  } else {
    throw err;
  }
}
```

```
from tabstack import Tabstack, RateLimitError


client = Tabstack()


# Proactive: back off early using the headers on a successful call.
response = client.extract.with_raw_response.json(
    url="https://example.com",
    json_schema={"type": "object", "properties": {"title": {"type": "string"}}},
)


remaining = int(response.headers.get("X-RateLimit-Remaining"))
if remaining < 5:
    print(f"Low on budget: {remaining} requests left this window")
    # slow down: reduce concurrency or pause before you hit zero


data = response.parse()


# Reactive fallback: catch the 429 and honor Retry-After.
try:
    client.extract.json(
        url="https://example.com",
        json_schema={"type": "object", "properties": {"title": {"type": "string"}}},
    )
except RateLimitError as err:
    retry_after = err.response.headers.get("Retry-After")
    print(f"Rate limited. Retry after {retry_after}s")
```

### Sizing a scheduled job

For a batch or cron job, the per-minute limit sets your floor on wall-clock time and your credit balance sets your ceiling on total volume. Size against both.

Take 10,000 URLs through Extract (JSON) on Team:

- **Time:** 25 requests per minute means at least 400 minutes, close to seven hours. Run it as a paced queue rather than a burst, and keep concurrency low enough that you stay under 25 in any 60-second span.
- **Credits:** Extract (JSON) is 50 credits per call, so 10,000 URLs is 500,000 credits, exactly the monthly Team allowance. Anything past that bills as overage.

The two ceilings are independent: raising your rate limit makes the job finish sooner but costs the same, and buying more credits lets you process more URLs but does not let you go faster. See [Pricing](/pricing/index.md) for per-endpoint credit costs.

---

## Usage and quota

Different surface, different units. Usage is what you have spent, measured in dollars and credits, and you read it in the console, not from a response header.

There is **no rate-limit visibility in the console**. The console shows usage, not your per-minute request budget. Your account’s rate limit is listed on your billing page, and your remaining budget for the current window is in the response headers above.

What the console shows depends on your plan:

- **Trial:** credits used against your 10,000 free credits.
- **Starter, Team, and Pro:** credits used against credits included (100k on Starter, 500k on Team, 3M on Pro). If you go over your allocation, the console also shows the dollar amount you are over.

For the rate limit that applies to your plan, see your billing page. For spend and quota, go to the [console](https://console.tabstack.ai). For per-endpoint credit costs and plan allowances, see [Pricing](/pricing/index.md). For a rate limit increase, contact support.
