Error envelope

All errors follow the same JSON structure:

{
  "error": "Human-readable message",
  "code": "MACHINE_READABLE_CODE"
}

The error field is a plain English string suitable for logging or displaying to users. The code field is a stable machine-readable identifier for programmatic error handling.

HTTP status codes

HTTPMeaning
400Bad Request - malformed JSON or invalid parameter
401Unauthorized - missing or invalid API key
403Forbidden - key is valid but the account lacks the required plan
404Not Found - resource does not exist or belongs to another account
422Unprocessable Entity - valid JSON but business rule violation
429Too Many Requests - rate limit exceeded
500Internal Server Error - unexpected server-side failure

Error codes

HTTPcodeDescription
401MISSING_API_KEYNo API key was provided in the request
401INVALID_API_KEYKey not found in the database, or has been revoked
403PLAN_REQUIREDAccount is not on the Agency plan
404NOT_FOUNDResource not found or does not belong to the authenticated account
422VALIDATION_ERRORRequest body failed schema validation
422LIMIT_REACHEDPlan limit reached (e.g. maximum brands exceeded)
422LIMIT_EXCEEDEDResource-level limit reached (e.g. maximum 5 avatars per brand)
422QUOTA_EXCEEDEDInsufficient credits to launch the requested campaign
409CONFLICTA resource with the same unique identifier already exists
429RATE_LIMITED60 requests per minute per key has been exceeded
500INTERNAL_ERRORUnexpected server-side error

Rate limiting details

The rate limit is 60 requests per minute per API key, enforced via a Redis sliding window counter. When the limit is exceeded:

  • HTTP status: 429
  • Response header: Retry-After: 60
  • Body code: RATE_LIMITED

The limiter fails open - if Redis is temporarily unavailable, all requests are allowed through rather than being blocked.

Handling errors

const res = await fetch("https://mentova.ai/api/v1/brands", {
  headers: { "X-API-Key": "mtv_live_your_key" },
});

if (!res.ok) {
  const { error, code } = await res.json();

  if (res.status === 429) {
    const retryAfter = res.headers.get("Retry-After");
    console.error(`Rate limited. Retry after ${retryAfter}s`);
  } else if (code === "QUOTA_EXCEEDED") {
    console.error("Not enough credits:", error);
  } else {
    console.error(`API error [${code}]:`, error);
  }
}