Rate limits and quotas
Limits apply to your developer account as a whole: all of its keys and applications together. Test and live mode have separate per-second limits, so testing never slows production.
Per second: burst and sustained rate
Each plan gives a burst (requests you can send at once) and a sustained rate (requests per second the burst refills at). For example, the Developer plan allows bursts of 60 and 30 requests per second sustained.
Every authenticated response tells you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Your burst size. |
X-RateLimit-Remaining |
Requests left in the current burst. |
X-RateLimit-Reset |
Unix time (seconds) when the burst is full again. |
Over the limit, the API answers 429 rate_limited with Retry-After: <seconds>.
Per month: included requests (live mode)
Each plan includes a number of billable requests per calendar month, in Thailand time. The count resets at 00:00 on the 1st.
| Header | Meaning |
|---|---|
X-Quota-Limit |
Requests included this month. |
X-Quota-Remaining |
Included requests left. |
X-Quota-Reset |
Unix time when the count resets. |
On the Free plan, requests stop at the included amount: you get 429 monthly_quota_exceeded, with Retry-After set to the seconds until the 1st. On paid plans, extra requests keep working and are billed at the plan's per-1,000 price on the next monthly invoice.
Billable means a live request that reached an account and that DOOLAE served:
| Counts | Doesn't count |
|---|---|
| 2xx responses | 5xx (DOOLAE's errors) |
| 4xx responses, which still use capacity | 429 (rate limited) |
| 401 (no valid key) | |
| Anything in test mode |
Other limits
| Limit | Value | Error |
|---|---|---|
LINE sends per landlord account (/v1/notifications, /v1/messages) |
60 per minute | 429 sending_limit |
| Test-mode requests per developer account | 20,000 per day | 429 sandbox_limit |
| Sandbox size | 20 properties, 500 rooms | 429 sandbox_limit |
| Request body | 1 MB | 413 request_too_large |
| Page size | 100 | 400 invalid_parameter |
| Requests with invalid keys from one IP address | 30 per minute | 429 too_many_failed_attempts |
When DOOLAE as a whole is very busy, a request can wait up to 10 seconds for its turn and then get 503 service_unavailable with Retry-After.
Backing off
- On
429and503, wait forRetry-Afterseconds, then retry. - For anything else retryable (network errors,
500), use exponential backoff with jitter: 1 s, 2 s, 4 s, 8 s, … up to a minute. - Retry writes with the same
Idempotency-Key, so a retry can't do the work twice (see idempotency). - Prefer webhooks to polling. If you poll,
GET /v1/events?created_after=…is the cheapest way to catch up.
async function call(url, init = {}, attempt = 0) {
const res = await fetch(url, init);
if ((res.status === 429 || res.status >= 500) && attempt < 5) {
const wait = Number(res.headers.get("Retry-After")) || Math.min(60, 2 ** attempt) * (0.5 + Math.random());
await new Promise((r) => setTimeout(r, wait * 1000));
return call(url, init, attempt + 1);
}
return res;
}