ReeplHELP

Errors & Rate Limits

What each Reepl API status code means, how scope and subscription failures are reported, and how many requests you can make.

Status codes

CodeMeaningWhat to do
200 / 201Success—
400The request was malformed or failed validationRead the message; it names the field
401The key is missing, malformed, disabled, or revokedCheck the header, then check the key still exists in Settings
403Authenticated, but not allowedEither a missing scope or an inactive subscription — see below
404No such resource, or it belongs to another workspaceCheck the id and the active workspace
429Rate limitedBack off and retry after Retry-After
500Something broke on our sideRetry with backoff; if it persists, contact support

The two kinds of 403

A 403 means one of two quite different things, and the body tells you which.

A missing scope. The key is valid but was not granted what this endpoint needs. The response names the required scope. Open Settings → Developers, click Manage on that key, and edit its permissions to add the scope. The key itself does not change, so nothing using it needs updating. See API keys & scopes.

No active subscription. Every endpoint except GET /external/me requires a live paid plan. When a subscription lapses, calls come back with requiresUpgrade: true. Nothing about the key is wrong; the account needs a plan.

Rate limits

RequestsLimit
GET requests300 per minute
Writes (POST, PUT, PATCH, DELETE)60 per minute
Webhook management endpoints100 per minute

Exceeding a limit returns 429 with a Retry-After header giving the seconds to wait.

Treat these as current operating guidance rather than a contractual guarantee, and build in backoff regardless — a retry loop that ignores Retry-After will keep hitting the wall.

Retrying well

Retry reads after 429 and transient 5xx responses. A write may have succeeded before a timeout or error reached your client: read the resource or check its webhook result before repeating a create, publish, or schedule request. Do not retry 400, 401, 403 or 404 — those fail the same way every time, and hammering them just burns your limit.

Use exponential backoff with jitter, so a fleet of workers that all fail at once does not all retry at once.

// Use for reads, not for blindly retrying publishing or creation.
async function readWithRetry(fn, attempts = 5) {
  for (let i = 0; i < attempts; i++) {
    const res = await fn();
    if (res.status !== 429 && res.status < 500) return res;

    const header = res.headers.get("Retry-After");
    const retryAfter = header === null ? NaN : Number(header);
    const backoff = Number.isFinite(retryAfter) && retryAfter >= 0
      ? retryAfter * 1000
      : 2 ** i * 1000 + Math.random() * 1000;
    await new Promise((r) => setTimeout(r, backoff));
  }
  throw new Error("Giving up after repeated failures");
}

Pagination

Pagination varies by endpoint. Follow the parameters and cursor or hasMore fields in that endpoint's API reference. Do not infer a cursor from a resource ID or assume every list uses the same response shape.