Using the API
Rate limits and usage
Limits keep one integration from slowing down a business's desks.
The limits#
Every limit is counted over a minute, and every minute starts afresh.
| Counted per | Limit | Counts |
|---|---|---|
| Key, reading | 600 | Every GET the key makes |
| Key, writing | 120 | Every POST, PUT and PATCH the key makes |
| Business | 1,200 | Every request from all of the business's keys together |
| Address, failed sign-in | 30 | Requests from one IP address answered 401 |
A request counts against its key's tier and the business's at once, and the first one to run out refuses it. A refused request still counts — retrying in a tight loop keeps you refused.
For a sense of scale: a lobby screen reading four endpoints every five seconds makes 48 reads a minute. If you are near a limit, you are probably polling for something a webhook would tell you.
Rate limit headers#
Every answer to a key that was recognised carries three headers:
RateLimit-Limit— the limit of the tier nearest to running out;RateLimit-Remaining— how many requests are left on it this minute;RateLimit-Reset— seconds until the minute ends and it starts again.
HTTP/1.1 200 OK
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 41 Over a limit, the answer is 429, with Retry-After in seconds:
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 120
RateLimit-Remaining: 0
RateLimit-Reset: 17
Retry-After: 17 {
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Too many requests for this API key. Wait and try again"
}
} Backing off#
-
On a
429, wait exactlyRetry-Afterseconds, then try again. Waiting less is refused again. -
Watch
RateLimit-Remaining. When it is low, slow down before you are refused, not after. - Several workers sharing one key share its limit; give each job its own key instead.
- Cache what changes slowly. Lines, services and desks change a few times a day; a visit changes a few times an hour. Ask for the first every few minutes, or when a webhook says so.
Node
async function withRetry(send) {
for (let attempt = 1; ; attempt++) {
const res = await send();
if (res.status !== 429 || attempt === 5) return res;
const wait = Number(res.headers.get("Retry-After") ?? "1");
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
}
}
const res = await withRetry(() =>
fetch("https://business.linemeup.in/api/v1/lines", {
headers: { Authorization: `Bearer ${process.env.LINEMEUP_API_KEY}` },
}),
); Python
import os
import time
import requests
def with_retry(send, attempts=5):
for attempt in range(1, attempts + 1):
res = send()
if res.status_code != 429 or attempt == attempts:
return res
time.sleep(int(res.headers.get("Retry-After", "1")))
res = with_retry(lambda: requests.get(
"https://business.linemeup.in/api/v1/lines",
headers={"Authorization": f"Bearer {os.environ['LINEMEUP_API_KEY']}"},
timeout=10,
)) PHP
<?php
function with_retry(callable $send, int $attempts = 5): array
{
for ($attempt = 1; ; $attempt++) {
[$status, $headers, $body] = $send();
if ($status !== 429 || $attempt === $attempts) {
return [$status, $headers, $body];
}
sleep((int) ($headers['retry-after'] ?? 1));
}
} Reading your usage#
The owner sees how each key and each endpoint is being used in Settings → Integrations:
- Per key — requests today and over the last 30 days, how many were errors and how many were refused for rate, the busiest endpoints, average and slowest time, and when it was last used.
- Per webhook endpoint — deliveries that succeeded, failed or were given up, the success rate, the median and 95th-percentile time your receiver took, and the latest failures.
- A chart of the last 30 days, requests and deliveries side by side.
Usage is counted by the business's own day and hour. Hourly figures are kept for 30 days and daily ones for 13 months. Figures can lag by up to half a minute.