mapier docs

Rate limits

Per-endpoint request caps, and how to behave when you hit one.

Each endpoint has its own budget, measured over a rolling 60-second window.

EndpointLimit
POST /v1/commands/*60 per minute
GET /v1/response_stream30 per minute
GET /v1/attachments/*120 per minute
POST /v1/handle-check30 per minute

The stream limit counts connections, not events. Holding one stream open costs one request; a reconnect loop that fires every second will exhaust it.

Handle check is capped low on purpose — the underlying read is single-flight per Mac, so concurrency there produces read_in_flight rather than throughput.

Exceeding a limit

HTTP/1.1 429 Too Many Requests
retry-after: 12
{ "error": "rate_limited", "retryAfter": 12 }

retryAfter is in seconds and is always at least 1. The Retry-After header carries the same value.

async function withRetry(fn: () => Promise<Response>): Promise<Response> {
  for (let attempt = 0; ; attempt++) {
    const res = await fn();
    if (res.status !== 429 || attempt >= 5) return res;

    const wait = Number(res.headers.get("retry-after") ?? 1);
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

Unconfirmed

Limits are currently counted per client IP address and per server process, not per account. That has two consequences worth designing around: several clients behind one NAT share a budget, and the effective ceiling can vary as requests land on different processes. Per-account limiting is the intended design, so do not build anything that depends on the current behaviour.

Staying under the limit

  • Hold one stream open rather than polling. The stream is the intended way to learn about outcomes, and it costs one connection.
  • Batch nothing, pace everything. There is no bulk endpoint; 60 commands a minute is one per second sustained.
  • Cache handle checks. Reachability changes rarely.
  • Use conditional attachment requests. An If-None-Match hit returns 304 and is far cheaper than re-downloading.

If a limiter fails internally the request is allowed through rather than rejected, so an occasional burst above the stated cap is possible. Do not rely on it.

On this page