Error Codes
All API errors follow the same format. Here is how to handle each one.
Error Response Format
Every error response contains an error object with a machine-readable code and a human-readable message:
{
"error": {
"code": "insufficient_credits",
"message": "Not enough credits. Required: 20, balance: 5."
}
}Error Reference
| HTTP | Error Code | Meaning | What To Do |
|---|---|---|---|
| 400 | bad_request | Invalid request body or missing required fields. | Check the message for which field failed validation. Fix the request and retry. |
| 401 | unauthorized | Missing or invalid API key. | Verify your Authorization: Bearer masko_... header is correct and the key has not been revoked. |
| 402 | insufficient_credits | Not enough credits for this operation. Response includes required and balance. | Top up credits at app.masko.ai/billing or reduce the request scope. |
| 403 | forbidden | You do not have access to this resource. | Verify the resource belongs to your account. Check that your API key has the necessary permissions. |
| 404 | not_found | The requested resource does not exist. | Check the resource ID in the URL. Use the list endpoints to find valid IDs. |
| 429 | rate_limited | Too many requests. Response includes Retry-After header. | Wait for the number of seconds in the Retry-After header, then retry. |
| 500 | internal_error | Something went wrong on our side. | Retry after a short delay. If persistent, contact support at paul@masko.ai. |
Handling Insufficient Credits
The 402 response includes required and balance fields so you can show users exactly how many credits they need:
// HTTP 402
{
"error": {
"code": "insufficient_credits",
"message": "Not enough credits. Required: 140, balance: 50.",
"required": 140,
"balance": 50
}
}Rate Limit Recovery
When you hit a 429, the response includes a Retry-After header with the number of seconds to wait. Use exponential backoff for robustness:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(url, options);
if (res.status !== 429) {
return res;
}
if (attempt === maxRetries) {
throw new Error('Rate limited after max retries');
}
// Use Retry-After header, or exponential backoff
const retryAfter = res.headers.get('Retry-After');
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay / 1000}s...`);
await new Promise((r) => setTimeout(r, delay));
}
}
// Usage:
const res = await fetchWithRetry(
'https://api.masko.ai/v1/collections/COLLECTION_ID/generate',
{
method: 'POST',
headers: {
'Authorization': 'Bearer masko_YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'image',
name: 'Hello',
image_prompt: 'waving hello',
}),
}
);Tip
Parse the error.code field programmatically to decide how to handle each error. Use error.message for logging and debugging - it may change between versions, but the code will stay stable.