<!-- https://cutedyno.com/docs/guides/rate-limits -->

# Rate limits

How much you can send, what the headers tell you, and how to back off correctly.

Requests are counted per API key in a rolling one-minute window. The default allowance is **1000 requests per hour**, which the limiter spends as **16 per minute** so one burst cannot exhaust an hour's budget.

Every response tells you where you stand, so you never have to guess:

| Header | Meaning |
| --- | --- |
| `RateLimit-Limit` | Requests allowed in the current window. |
| `RateLimit-Remaining` | Requests left in it. |
| `RateLimit-Reset` | Seconds until the window resets. |
| `RateLimit-Policy` | The policy in force, for example `16;w=60`. |
| `Retry-After` | On a `429` only. Seconds to wait. |

## Handling a 429

A `429` carries code `rate_limit_exceeded` and a `Retry-After` header. Wait that long, then retry — do not retry immediately, and do not invent your own delay when the API has told you the right one.

```typescript
import { CuteDynoError } from '@cutedyno/node';

try {
  await cutedyno.posts.create(input);
} catch (error) {
  if (error instanceof CuteDynoError && error.isRateLimit) {
    await sleep((error.retryAfter ?? 60) * 1000);
    // …then retry, with the same idempotency key.
  }
}
```

The Node SDK does this for you: `429` and `5xx` are retried twice by default, honouring `Retry-After`, with exponential backoff and jitter when the header is absent. Raise or lower it with `maxRetries`.

Use the same idempotency key across those retries, or a retry can create a second post. See [idempotency](/docs/guides/idempotency).

## Staying under the limit

**Use webhooks instead of polling.** This is the big one. Polling ten posts every five seconds is 120 requests a minute against a 16 per minute limit; a `post.published` webhook is zero. [Webhooks](/docs/webhooks) is the fix for almost every rate limit problem we see.

**Batch accounts into one post.** One [`POST /v1/posts`](/docs/api/create-post) with five `accountIds` is one request, publishes to five accounts, and gives you one status to track. Five separate posts are five requests and five things to reconcile.

**Watch the headers rather than counting.** Track `RateLimit-Remaining` and slow down as it approaches zero. The SDK will hand it to you on every response:

```typescript
const cutedyno = new CuteDyno({
  onRateLimit: ({ remaining, reset }) => {
    if (remaining < 3) metrics.gauge('cutedyno.throttle_risk', reset);
  },
});
```

**Serialise your workers.** Ten workers each retrying in lockstep will hammer the same key. One queue with limited concurrency uses the same budget and finishes sooner.

## Platform limits are separate

CuteDyno's limit governs calls to CuteDyno. The platforms have their own, and those apply to publishing, not to your API calls:

- TikTok caps direct posts per user per day.
- Instagram caps content publishing per account per rolling 24 hours.
- YouTube spends daily quota units per upload.

When a platform rejects a post for its own quota, the post fails with a platform error rather than a CuteDyno `429`, and the message names the platform. Retrying immediately will not help; the platform quota resets on its own schedule. The per-platform pages under [Platforms](/docs/platforms) list the current caps.

## Asking for more

If your workload genuinely needs a higher ceiling — a large migration, or a platform onboarding thousands of profiles at once — [get in touch](/contact) with your key prefix and the shape of the traffic. Limits are per key, so a bulk-import key can be raised without touching your production one.
