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

# Rate Limits

> Per-minute request limits and monthly video quotas.

## Two kinds of limits

Pull/story applies two independent limits to story creation and polling:

1. **Rate limit** — how many requests per minute you can make
2. **Minute quota** — how much generated video you can create per month

## Rate limits

| Endpoint type                                 | Limit                     |
| --------------------------------------------- | ------------------------- |
| `POST /api/v1/stories` (creation)             | 10 / minute               |
| `GET /api/v1/stories/:id` (polling)           | 120 / minute              |
| Auth failures and other protected API traffic | best-effort IP throttling |

When exceeded you receive:

```json theme={null}
HTTP 429 Too Many Requests

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Retry after 18 seconds.",
    "retry_after": 18
  }
}
```

The `Retry-After` HTTP header is also set.

## Minute quota

| Plan | Video minutes per month | Price      |
| ---- | ----------------------- | ---------- |
| Free | 10 min                  | `$0`       |
| Pro  | 120 min                 | `$19 / mo` |

Each generated pull story consumes minutes equal to its video duration. A short brief is usually \~1 minute; a large brief is 3-5 minutes.

When you run out of minutes:

```json theme={null}
HTTP 402 Payment Required

{
  "error": {
    "code": "insufficient_minutes",
    "message": "Monthly video quota exceeded.",
    "minutes_remaining": 0,
    "minutes_required": 3,
    "upgrade_url": "https://pullstory.com/dashboard"
  }
}
```

## Handling limits in your code

<CodeGroup>
  ```ts Node theme={null}
  async function createStory(payload) {
    for (let attempt = 0; attempt < 3; attempt++) {
      const res = await fetch("https://pullstory.com/api/v1/stories", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.PULLSTORY_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });

      if (res.status === 429) {
        const retryAfter = Number(res.headers.get("retry-after") ?? 10);
        await new Promise((r) => setTimeout(r, retryAfter * 1000));
        continue;
      }

      return res.json();
    }
  }
  ```

  ```python Python theme={null}
  import time
  import requests

  def create_story(payload):
      for attempt in range(3):
          r = requests.post(
              "https://pullstory.com/api/v1/stories",
              headers={"Authorization": f"Bearer {API_KEY}"},
              json=payload,
          )
          if r.status_code == 429:
              time.sleep(int(r.headers.get("Retry-After", 10)))
              continue
          return r.json()
  ```
</CodeGroup>

## Need more?

If 120 minutes isn't enough for your team, [email us](mailto:moshikarim@proton.me) — we'll get you on a custom plan.
