Skip to main content

Error Handling

Complete guide to error responses, codes, and retry strategies for the Affinity AI Bid API.

Error Response Format

All errors follow a consistent structure:

{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"field": "specific.field.path",
"reason": "detailed_reason"
},
"request_id": "uuid-v4-string"
}
}

Error Codes

Client Errors (4xx)

CodeHTTP StatusDescriptionRetry
INVALID_REQUEST400Malformed OpenRTB requestNo
MISSING_REQUIRED_FIELD400Required field missingNo
INVALID_FIELD_VALUE400Field value invalidNo
UNSUPPORTED_FORMAT400Requested format not supportedNo
RATE_LIMIT_EXCEEDED429Too many requestsYes (after delay)

Server Errors (5xx)

CodeHTTP StatusDescriptionRetry
INTERNAL_ERROR500Server errorYes
BACKEND_ERROR502Backend bidder errorYes
SERVICE_UNAVAILABLE503Service temporarily downYes
GATEWAY_TIMEOUT504Backend timeoutYes

Error Examples

Invalid Request (400)

Malformed OpenRTB request:

{
"error": {
"code": "INVALID_REQUEST",
"message": "Invalid OpenRTB request format",
"details": {
"field": "imp[0].native",
"reason": "missing required field 'request'"
},
"request_id": "req-abc123"
}
}

Resolution: Fix the request structure and retry.

Missing Required Field (400)

Required field not provided:

{
"error": {
"code": "MISSING_REQUIRED_FIELD",
"message": "Required field missing",
"details": {
"field": "id",
"reason": "BidRequest must include 'id' field"
},
"request_id": "req-def456"
}
}

Resolution: Add the missing field and retry.

Invalid Field Value (400)

Field value doesn't meet validation rules:

{
"error": {
"code": "INVALID_FIELD_VALUE",
"message": "Field value invalid",
"details": {
"field": "imp[0].native.request",
"reason": "width must be a positive integer, got -300"
},
"request_id": "req-ghi789"
}
}

Resolution: Correct the field value and retry.

Unsupported Format (400)

Requested creative format not supported:

{
"error": {
"code": "UNSUPPORTED_FORMAT",
"message": "Requested format not supported",
"details": {
"field": "imp[0].ext.aura.adcpFormats[0].id",
"reason": "format 'custom_format_xyz' is not supported"
},
"request_id": "req-jkl012"
}
}

Resolution: Use a supported format from the AdCP formats list.

Rate Limit Exceeded (429)

Too many requests from publisher or IP:

{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"details": {
"limit": 1000,
"window": "1 second",
"retry_after": 30
},
"request_id": "req-mno345"
}
}

Response Headers:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Retry-After: 30

Resolution: Wait for the specified retry_after seconds before retrying.

Internal Server Error (500)

Server-side processing error:

{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error processing bid request",
"request_id": "req-pqr678"
}
}

Resolution: Retry with exponential backoff.

Backend Error (502)

Backend bidder returned an error:

{
"error": {
"code": "BACKEND_ERROR",
"message": "Backend bidder error",
"details": {
"backend": "affilizz-search-product-ad",
"reason": "backend returned 500"
},
"request_id": "req-stu901"
}
}

Resolution: Retry with exponential backoff.

Service Unavailable (503)

Service temporarily unavailable:

{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Backend bidders temporarily unavailable",
"retry_after": 30,
"request_id": "req-vwx234"
}
}

Resolution: Wait for retry_after seconds and retry.

Gateway Timeout (504)

Backend timeout exceeded:

{
"error": {
"code": "GATEWAY_TIMEOUT",
"message": "Backend timeout exceeded",
"details": {
"timeout_ms": 500
},
"request_id": "req-yz567"
}
}

Resolution: Retry with exponential backoff.

Retry Logic

Retryable Errors (5xx)

For server errors (500, 502, 503, 504), implement exponential backoff:

Attempt 1: Immediate
Attempt 2: Wait 1 second
Attempt 3: Wait 2 seconds
Attempt 4: Wait 4 seconds
Max: 3 retries

Algorithm:

async function retryRequest(request, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await sendRequest(request)
return response
} catch (error) {
if (attempt === maxRetries) throw error

// Check if error is retryable (5xx)
if (error.status >= 500 && error.status < 600) {
const delay = Math.pow(2, attempt) * 1000 // 1s, 2s, 4s
await sleep(delay)
continue
}

// Non-retryable error
throw error
}
}
}

Non-Retryable Errors (4xx)

For client errors (400, 429), do not retry without fixing the request:

  • 400 errors: Fix the request structure/values
  • 429 errors: Wait for Retry-After header value

Rate Limit Handling

Respect rate limit headers:

function handleRateLimit(response) {
const retryAfter = response.headers['Retry-After'];
const resetTime = response.headers['X-RateLimit-Reset'];

if (retryAfter) {
// Wait for specified seconds
await sleep(parseInt(retryAfter) * 1000);
} else if (resetTime) {
// Wait until reset time
const now = Date.now() / 1000;
const waitTime = Math.max(0, resetTime - now);
await sleep(waitTime * 1000);
}
}

Rate Limiting

Limits

ScopeLimitWindowResponse
Global10,000 req/sec1 second503 Service Unavailable
Per Publisher1,000 req/sec1 second429 Too Many Requests
Per IP100 req/sec1 second429 Too Many Requests

Rate Limit Headers

All responses include rate limit information:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1704067200

When rate limited:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Retry-After: 30

Best Practices

1. Implement Exponential Backoff

Always use exponential backoff for 5xx errors to avoid overwhelming the service during incidents.

2. Respect Rate Limits

Monitor rate limit headers and implement client-side throttling to stay within limits.

3. Log Request IDs

Always log the request_id from error responses for troubleshooting and support requests.

4. Handle No Bid Gracefully

A 204 No Content response is not an error - handle it gracefully without displaying errors to users.

5. Set Appropriate Timeouts

Set client timeouts slightly higher than the service timeout (500ms) to allow for network latency:

const timeout = 600 // 600ms client timeout

6. Monitor Error Rates

Track error rates by code to identify integration issues or service degradation:

  • Alert on sustained 4xx rates (integration issues)
  • Alert on 5xx rates above baseline (service issues)

Troubleshooting

Common Issues

Issue: Consistent 400 errors

  • Cause: Invalid request format
  • Solution: Validate request against OpenRTB 2.6 spec
  • Tool: Use test mode (test: 1) to validate requests

Issue: Intermittent 503 errors

  • Cause: Backend temporarily unavailable
  • Solution: Implement retry logic with exponential backoff
  • Expected: Occasional 503s are normal during deployments

Issue: 429 rate limit errors

  • Cause: Exceeding request rate limits
  • Solution: Implement client-side throttling and respect Retry-After headers

Issue: 504 timeout errors

  • Cause: Backend taking too long to respond
  • Solution: Retry with exponential backoff
  • Note: May indicate backend performance issues

Support

If you encounter persistent errors:

  1. Check the API Status Page for known issues
  2. Review error details and request_id from responses
  3. Contact support with:
    • Request ID
    • Timestamp
    • Full error response
    • Sample request (sanitized)

Next Steps