DocsBookingAPIErrors & Rate Limiting

Errors and rate limiting

All errors return a JSON body with a machine-readable error code and a human-readable message. Use the error code in your error-handling logic — the message may change, the code will not.

Error response format

Error response 4xx / 5xx
{
  "error":     "invalid_credentials",
  "message":   "The API key or secret is incorrect.",
  "requestId": "req_01HZ9VBKX4EFGH2J..."
}
💡
Always include requestId when contacting MoovLogic support. It uniquely identifies the failed request in our logs.

HTTP status codes

StatusMeaning
200 OK Request succeeded.
201 Created Booking was created successfully.
400 Bad Request Validation error. Check the errors array in the response.
401 Unauthorized Token is missing, expired, or invalid.
403 Forbidden Token is valid but you do not have permission for this operation.
404 Not Found The requested booking tagName or service type ID does not exist.
422 Unprocessable Entity Business rule violation — for example, an invalid status transition or a pickup time in the past.
429 Too Many Requests Rate limit exceeded. See Rate limits below.
500 Internal Server Error Unexpected server error. Retry with exponential back-off. If it persists, contact support with the requestId.

Error codes reference

Error codeHTTP statusDescription
invalid_credentials 401 The API key or secret is incorrect.
token_expired 401 The JWT has expired. Re-authenticate to get a new token.
token_missing 401 The Authorization header is absent from the request.
insufficient_permission 403 Your service account does not have access to this resource or operation.
booking_not_found 404 No booking exists with this tagName, or it belongs to a different account.
service_type_not_found 404 The serviceTypeId does not exist or is not active.
no_pricing_available 422 No active tariff is configured for this route and service type combination.
pickup_in_past 422 The pickupDateTimeUtc is in the past.
invalid_status_transition 422 The requested status change is not a valid transition from the current status.
booking_not_cancellable 422 The booking cannot be cancelled at its current status (e.g. already completed or cancelled).
validation_error 400 One or more fields failed validation. See the errors array for field-level detail.
rate_limit_exceeded 429 You have exceeded the request rate limit for this endpoint.

Validation errors

When error is validation_error, the response includes an errors array with field-level detail:

Validation error response 400 Bad Request
{
  "error":   "validation_error",
  "message": "One or more fields failed validation.",
  "errors": [
    {
      "field":   "pickupDateTimeUtc",
      "message": "Pickup time must be in the future."
    },
    {
      "field":   "passengerEmail",
      "message": "Invalid email address format."
    }
  ],
  "requestId": "req_01HZ9VBKX4E..."
}

Rate limits

PolicyLimitApplies to
AuthPolicy 10 requests / minute POST /api/v1/auth/token
ApiPolicy 60 requests / minute All other endpoints

Rate limit information is returned in the response headers on every request:

Rate limit headers
X-RateLimit-Limit:     60
X-RateLimit-Remaining: 47
X-RateLimit-Reset:     1714521600
HeaderDescription
X-RateLimit-Limit The maximum requests allowed in the current window.
X-RateLimit-Remaining The number of requests remaining in the current window.
X-RateLimit-Reset Unix timestamp indicating when the rate limit window resets.
💡
Check X-RateLimit-Remaining and pause when it approaches 0. X-RateLimit-Reset tells you exactly when the window resets so you can resume immediately rather than guessing.

Retry strategy

⚠️
Never auto-retry 400, 401, 403, or 422 responses — these indicate problems with your request that won't resolve on their own. Fix the underlying issue before retrying.

For 429 and 5xx responses, retry with exponential back-off:

Exponential back-off
import time, requests

def api_request_with_retry(method, url, **kwargs):
    max_attempts = 5
    base_delay   = 1.0

    for attempt in range(max_attempts):
        response = requests.request(method, url, **kwargs)
        if response.status_code not in (429, 500, 502, 503, 504):
            return response
        if attempt < max_attempts - 1:
            wait = base_delay * (2 ** attempt)
            time.sleep(wait)

    raise Exception(f"Request failed after {max_attempts} attempts")
async function apiRequestWithRetry(url, options, maxAttempts = 5) {
    const retryable = new Set([429, 500, 502, 503, 504]);

    for (let attempt = 0; attempt < maxAttempts; attempt++) {
        const res = await fetch(url, options);
        if (!retryable.has(res.status)) return res;
        if (attempt < maxAttempts - 1) {
            await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
        }
    }
    throw new Error(`Request failed after ${maxAttempts} attempts`);
}

Getting help

If a request fails unexpectedly and retrying does not resolve it, contact api-support@moovlogic.com with:

  • The requestId from the error response
  • The endpoint and HTTP method
  • The timestamp of the failed request