# Rate limits and daily quotas

> Ur Next Door API daily quotas per plan and action, the 60 requests a minute limit, rate limit headers, UTC midnight resets, and size limits.

Source: https://docs.urnextdoor.com/limits/

Two limits apply to API use: a **daily quota** for each kind of create, and a **burst limit** on requests a minute. Both are per member: all of your keys share them, so five keys do not give you five times the quota.

## Daily quotas

Each successful create or upload uses one unit of its action's quota for the current UTC day:

| Action | Free | Pro | Business |
| --- | ---: | ---: | ---: |
| `post` | 20 | 60 | 200 |
| `question` | 20 | 60 | 200 |
| `poll` | 10 | 30 | 100 |
| `mcq` | 30 | 90 | 300 |
| `community` | 2 | 5 | 20 |
| `service` | 5 | 15 | 50 |
| `job` | 5 | 15 | 50 |
| `media` | 50 | 150 | 500 |

- The columns are plans. Your plan is shown by `GET /api/v1/me` and `GET /api/v1/usage`; you can change it in **Settings > Billing**.
- `community` is small on purpose: communities are long lived.
- Quotas count **only API requests**. What you do on the website or in the app never uses them.
- A **scheduled** create counts on the day you schedule it, not the day it goes live. Cancelling it later does not give the unit back.
- **Reads and schedule management are not counted:** `GET /me`, `GET /usage`, the lookups, `GET /scheduled`, reschedule, publish now and cancel.

## Failed requests never count

Only a successful answer (`2xx`) uses quota. A `422` because of a typo, a `403`, a `409` or even a `500` gives the unit back at once. Fix the problem and try again.

## When the day resets

Counters reset at **00:00 UTC** every day (05:30 in India). `X-RateLimit-Reset` and `resets_at` give the exact moment.

## Check your usage

**cURL**

```bash
curl "https://urnextdoor.com/api/v1/usage" \
  -H "Authorization: Bearer $UND_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/usage", {
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.get(
    "https://urnextdoor.com/api/v1/usage",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/usage');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

Sample response: `200 OK`

```json
{
  "ok": true,
  "plan": "free",
  "day": "2026-10-15",
  "resets_at": "2026-10-16T00:00:00Z",
  "quotas": [
    {
      "action": "post",
      "limit": 20,
      "used": 3,
      "remaining": 17
    },
    {
      "action": "question",
      "limit": 20,
      "used": 0,
      "remaining": 20
    },
    {
      "action": "media",
      "limit": 50,
      "used": 2,
      "remaining": 48
    }
  ],
  "request_id": "9f8e7d6c5b4a3921"
}
```

Every quota-counted response also carries three headers:

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` | Today's limit for this action |
| `X-RateLimit-Remaining` | Units left today for this action |
| `X-RateLimit-Reset` | Unix seconds of the next UTC midnight |

## When the quota runs out

The request answers `429` with the code `quota_exceeded` and a `Retry-After` header (seconds until the reset):

```json
{
  "ok": false,
  "error": "You have used today's 20 posts for the API. The limit resets at 00:00 UTC.",
  "code": "quota_exceeded",
  "request_id": "a1b2c3d4e5f60718"
}
```

Do not retry before the reset; schedule the rest for tomorrow instead, or upgrade your plan.

## The burst limit: 60 requests a minute

Across every endpoint, counted or not, a member may make **60 requests a minute**. Beyond that, requests answer `429` with the code `rate_limited` and a `Retry-After` header. Wait that many seconds and continue.

Separately, repeated **failed key checks** from one network address are limited, so a script stuck in a loop with a wrong key is slowed down. Fix the key first.

A simple, polite client:

```js
async function call(url, options, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(url, options);
    if (res.status !== 429 && res.status < 500) return res;
    const body = await res.clone().json().catch(() => ({}));
    if (body.code === "quota_exceeded") return res;
    const wait = Number(res.headers.get("Retry-After") || 2 ** i);
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
  return fetch(url, options);
}
```

Reuse the same `Idempotency-Key` in `options` across the attempts, so a retried create never posts twice.

## Size limits

| Limit | Value |
| --- | --- |
| JSON request body | 256 KB |
| JSON request body for `POST /media` | 12 MB |
| Image in | 8 MB, PNG, JPEG, GIF or WebP |
| Image stored | 2 MB or less after conversion to WebP |
| `gallery` | 12 images |
| Poll and MCQ `options` | 2 to 5 |
| Service `packages` | 3 |
| Service `faqs` | 20 |
| Active API keys per member | 5 |
| `scheduled_at` | 5 minutes to 180 days ahead |

Over a size limit, the request answers `413 payload_too_large`.

## Frequently asked questions

### Do all my keys share one quota?

Yes. Quotas and the burst limit are per member, across all of your keys.

### Does posting on the website use my API quota?

No. Only requests made with an API key count.

### Does a failed request use quota?

No. Anything that is not a 2xx answer gives the unit back immediately.

### How do I get higher limits?

Higher plans have higher quotas, as the table shows. Upgrade in Settings > Billing on the website.

## Next steps

- [Errors](https://docs.urnextdoor.com/errors.md).
- [Idempotency](https://docs.urnextdoor.com/idempotency.md) for safe retries.
- [Schedule content](https://docs.urnextdoor.com/guides/schedule-content.md).
