UGCDocumentation

Error Object

API error response structure and error codes

Updated July 14, 2026

There is no single universal error envelope - the exact shape depends on what rejected the request. See for the full breakdown; this page covers the two concrete shapes you'll actually parse.

Shape 1: framework-level errors (the majority)

Auth failures, validation failures, and most thrown errors return this shape:

json
{
  "statusCode": 400,
  "message": "amount must be a positive number",
  "error": "Bad Request"
}

message can be a single string or an array of strings (one per failed validation rule). There is no machine-readable code field in this shape - branch on the HTTP status code, not on the message text, which is meant for humans and can change.

FieldTypeDescription
statusCodenumberHTTP status code, repeated in the body
messagestring \string[]Human-readable description of what went wrong
errorstringShort name for the status code (e.g. "Bad Request", "Unauthorized")

Shape 2: inline payment-processing errors

A handful of endpoints that process a card or crypto charge synchronously catch failures and return this instead of throwing:

json
{
  "success": false,
  "error": "The card was declined by the issuing bank"
}

Here error is a plain string, not an object - there's no error.code/error.message to destructure. Check success only on the specific endpoints documented as using this shape; everywhere else, check the HTTP status.

HTTP Status Codes

Status CodeMeaningUsage
400Bad RequestInvalid request parameters or body
401UnauthorizedInvalid, missing, or revoked credential
403ForbiddenCredential doesn't have permission for this resource or action
404Not FoundResource doesn't exist, or doesn't belong to your account
409ConflictResource state conflict (e.g. already actioned, duplicate idempotency key)
422Unprocessable EntityRequest was well-formed but failed validation
429Too Many RequestsRate limit exceeded - see Retry-After header
500Internal Server ErrorUnexpected server error - safe to retry
502 / 503Bad Gateway / Service UnavailableUpstream or platform temporarily unavailable - retry with backoff

For guidance on what typically causes each status and how to respond, see Reading an Error by Status Code.

Error Handling Example

JavaScript/Node.js
const res = await fetch(`https://api.ugc.africa/automation/actions/campaigns/${campaignId}/fund`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(fundData)
});

const result = await res.json();

if (!res.ok || result.success === false) {
  throw new Error(result.message ?? result.error ?? 'request failed');
}

return result;

Rate Limit Details

A 429 response carries no retry details in the body - back off using the Retry-After header:

json
{
  "statusCode": 429,
  "message": "ThrottlerException: Too Many Requests",
  "error": "Too Many Requests"
}
  • General API: 100 requests per minute per API key
  • Retry with exponential backoff once Retry-After has elapsed

Best Practices

1. Check the HTTP status, not a `success` field - most endpoints have no success field at all

2. Branch on status code, not on `message` text - message strings are for humans and can change

3. Implement retry logic - retry with exponential backoff for 429s and 5xx errors, never for 4xx

4. Log the full response body - useful for debugging even without a machine-readable code

5. Use idempotency keys - prevent duplicate side effects when you retry a create/action call

Did this solve your problem?