Skip to main content

Handling errors, rate limits, and retry logic

Handle Mobaro API 400–503 responses, stay within per-key rate limits, retry safely with backoff, and receive webhooks reliably.

Written by Logan Bowlby

Overview

Reliable integrations handle failures in two directions: your system calling the Mobaro API, and Mobaro sending webhooks to your endpoint. This article covers the HTTP responses the Mobaro API returns, the rate limits behind them and how to react to each, then the webhook-receiver side at the end.

The Mobaro API returns a consistent set of status codes across endpoints. Your integration should detect them and react: fix the request or key, or retry safely.

At a glance

Who can do this

Anyone calling the Mobaro API with an API key. Writes need a key without Read-only. Creating keys needs Organization › Administrate.

Where

API keys: Configuration › API › API Keys

Works on

Public API

Availability

Enabled per organization by Mobaro — ask your CSM


Mobaro API response codes

Code

What it means

What to do

400 Bad Request

The request failed validation, for example a Limit outside the allowed range or an unsupported OrderBy value. The response body names the field and the problem.

Fix the request. Do not retry unchanged.

401 Unauthorized

Missing, invalid or deleted API key in the X-Api-Key header.

Check the header and the key. Do not retry until fixed.

403 Forbidden

A Read-only key was used for a write (POST, PUT or DELETE).

Use a key without Read-only for writes. Do not retry.

404 Not Found

The record or route doesn't exist.

Check IDs and routes. Do not blind-retry.

429 Too Many Requests

You hit a rate limit. See Mobaro API rate limits.

Wait, then retry with exponential backoff and jitter.

500 Internal Server Error

Something went wrong on Mobaro's side while handling the request.

Retry a few times with backoff. For writes, check whether the change was applied first. If it persists, contact Mobaro Support.

503 Service Unavailable

The Mobaro API is temporarily switched off on Mobaro's side.

Retry later with backoff and jitter; give up after a few attempts and alert your team.


Mobaro API rate limits

Rate limits are counted per API key, with one exception for User writes:

What

Limit

Counted

All API requests

10 requests per second

Per API key

Location group writes (create, update, move, relations, delete)

1 request per 15 seconds

Per API key

When you go over a limit, one extra request is held briefly and then processed; any further requests get 429 straight away.

⚠️ Heads-up: The 429 response doesn't include a Retry-After header, so use your own backoff. Because the User write limit is shared, you can get a 429 on User writes even when your own volume is low; back off and retry.


Handle each API error

400 Bad Request

  • Read the response body: it lists each invalid field with a message, for example Allowed values are: ... for an unsupported OrderBy.

  • Common causes: Limit above the maximum (128 on most list endpoints, 20 on Results), a malformed date, or a missing required field. See Mobaro API parameter reference.

401 Unauthorized

  • Send the key in the X-Api-Key header on every request.

  • Confirm the key still exists under Configuration › API › API Keys. A deleted key no longer works. See Creating and Managing API Keys.

  • Only retry after fixing the key. Blind retries waste time and count toward your rate limit.

403 Forbidden

  • The key is marked Read-only, and read-only keys can't create, update or delete data.

  • Use a separate key without Read-only for integrations that write, and keep read-only keys for reporting.

404 Not Found

  • Re-check the resource path and the ID (for example /users/{id}, /timesheets/{id}).

  • If you're looking up by filters, list first, then get by ID.

429 Too Many Requests

  • Back off and retry with exponential backoff (for example 1 s, 2 s, 4 s, 8 s) and jitter.

  • Page through large datasets with Limit and Offset instead of firing many requests in parallel.

  • Give separate integrations separate keys, so one busy job doesn't slow another. This doesn't apply to User writes, which share one limit.

503 Service Unavailable

  • Treat it as temporary. Retry with exponential backoff and jitter.

  • Stop after a capped number of attempts (for example 3–6) and alert your team.


Safe API retry pattern

This pseudocode stops on errors you must fix and backs off on 429 and 503:

maxAttempts = 5
delay = 1s

for attempt in 1..maxAttempts:
resp = call_api()
if resp.status in [200..299]: return resp
if resp.status in [400, 401, 403, 404]:
abort (fix the request, key or IDs; do not auto-retry)
if resp.status in [429, 503]:
sleep(delay + random_jitter())
delay *= 2
continue
raise error with context (for 500, see below)
raise error "max retries exceeded"

Retry helper code samples

Python (requests)

import time, random, requests

API = "https://app.mobaro.com/api/customers/users"
HEADERS = {"X-Api-Key": "YOUR_SECRET_TOKEN"}

def get_with_retry(url, headers, attempts=5, base=1.0):
delay = base
for i in range(attempts):
r = requests.get(url, headers=headers)
if 200 <= r.status_code < 300:
return r
if r.status_code in (400, 401, 403, 404):
raise RuntimeError(f"Non-retryable {r.status_code}: {r.text}")
if r.status_code in (429, 503):
time.sleep(delay + random.uniform(0, 0.5))
delay *= 2
continue
r.raise_for_status()
raise TimeoutError("Max retries exceeded")

resp = get_with_retry(API, HEADERS)
print(resp.json())

Node.js (built-in fetch)

async function getWithRetry(url, opts = {}, attempts = 5, base = 1000) {
let delay = base;
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, opts);
if (res.ok) return res;
if ([400, 401, 403, 404].includes(res.status)) {
throw new Error(`Non-retryable ${res.status}: ${await res.text()}`);
}
if (res.status === 429 || res.status === 503) {
await new Promise(r => setTimeout(r, delay + Math.random() * 250));
delay *= 2;
continue;
}
throw new Error(`${res.status} ${await res.text()}`);
}
throw new Error("Max retries exceeded");
}

const res = await getWithRetry(
"https://app.mobaro.com/api/customers/users",
{ headers: { "X-Api-Key": "YOUR_SECRET_TOKEN" } }
);
console.log(await res.json());

Receive Mobaro webhooks reliably

Everything above is about your system calling the Mobaro API. When Mobaro sends webhooks to your endpoint, the roles reverse and you're the server:

  • Return a 2xx within 5 seconds. Acknowledge the delivery quickly and do heavier processing afterwards. A slower response, or any non-2xx status, counts as a failed delivery.

  • Be idempotent. The same notification can arrive more than once. The payload is { event, resource, timestamp, data }, and there is no separate object ID field: use resource, event, data.id and timestamp together as your key.

  • Expect out-of-order arrival. A failed notification waits for its retry while newer ones go through, so compare data.updated with what you already have.

  • Let Mobaro retry. Failed deliveries are retried automatically with increasing delays, up to once every 24 hours, with no attempt limit. You don't need your own catch-up polling for short outages.

ℹ️ Note: For webhook setup and the payload, see Using webhooks in Mobaro. For the notification list and retrying a notification straight away, see Monitoring webhook deliveries and retrying failed events.


Best practices

  • Pace bulk jobs below the limits rather than relying on retries: for example, no more than about 8 requests per second per key, one location group write every 15 seconds, and User writes one at a time. For how keys and limits work, see Understanding API access scopes and limitations.

  • Log the endpoint, status code, attempt number and delay used.

  • Alert on repeated 401s or 403s (key or configuration problem) and spikes of 429 or 503 (load or scheduling issue).

  • Record the last successful page or ID so your job can resume after a failure.

  • Make creates and updates idempotent (for example, look up by external ID before creating) so retries are safe.


Frequently asked questions

Why do I get 401 Unauthorized when my API key is correct?

Mobaro didn't accept the X-Api-Key header: it's missing, incomplete, or the key was deleted. A common cause is pasting the Prefix from the API Keys list; the full key is shown only once, when it's created. If you don't have it, create a new key.

GET works, but POST, PUT or DELETE fails. Why?

A 403 means the key is Read-only; use a key without it for writes. Otherwise, check the operation is listed in the Mobaro API documentation: not every backend action is in the API. For example, you can't add a comment to an Assignment through the API.

Does a 401 or 403 mean the key's user or Role lacks permissions?

No. API keys aren't linked to a user, Role or User Group, so there are no permissions to adjust. A 401 means the key itself wasn't accepted; a 403 means a Read-only key tried to write. See Understanding API access scopes and limitations.

Did this answer your question?