Error handling
Understand error responses and implement robust retry logic for your API integration.
All Core API errors follow a consistent format. This guide explains each error type, what causes it, and how to handle it in your integration.
Error response format
When a request fails, the API returns an HTTP error status code and a JSON response body:
{
"errors": [
{
"message": "external_id must be supplied"
}
]
}Standard structure:
errors— Array of error objectsmessage— Human-readable description of what went wrong
Some errors may include additional fields with context (e.g., which field failed validation).
HTTP status codes
400 Bad Request
Meaning: Your request is malformed or missing required data.
Example:
{
"errors": [{
"message": "external_id must be supplied"
}]
}Common causes:
- Missing required fields
- Invalid data format (e.g., string instead of number)
- Malformed JSON in request body
How to fix:
- Review the error
message— it tells you what's wrong - Check the endpoint reference for required fields
- Validate your JSON syntax
- Ensure correct Content-Type header (
application/json)
Retryable? ❌ No — Fix your request first, then retry.
401 Unauthorized
Meaning: Authentication failed or is missing.
Example:
{
"errors": [{
"message": "Unauthorized"
}]
}Common causes:
- Missing
X-Yotpo-Tokenheader - Invalid or expired access token
- Incorrect Store ID in URL path
How to fix:
- Verify
X-Yotpo-Tokenheader is present - Check token value is correct (no extra spaces)
- Generate a new token: Authentication guide
- Verify Store ID in URL matches your token
Retryable? ❌ No — Fix authentication first.
404 Not Found
Meaning: The requested resource doesn't exist.
Example:
{
"errors": [{
"message": "Product not found"
}]
}Common causes:
- Incorrect resource ID (typo or wrong ID)
- Resource was deleted
- Incorrect endpoint URL
How to fix:
- Verify the ID is correct
- Check the resource exists using a list/search endpoint
- Review the endpoint URL for typos
Retryable? ❌ No — The resource doesn't exist. Check your data.
422 Unprocessable Entity
Meaning: Request is well-formed, but validation failed.
Example:
{
"errors": [{
"message": "Currency must be valid ISO code of 3 characters"
}]
}Common causes:
- Data violates business rules (e.g., invalid currency code)
- Field value outside allowed range
- Dependency not satisfied (e.g., creating a variant without a product)
How to fix:
- Read the error
messagefor specific validation failure - Review guidelines and conventions for data formats
- Check the endpoint documentation for field constraints
- Verify referenced resources exist (e.g., product exists before creating variant)
Retryable? ❌ No — Fix the data first.
429 Too Many Requests
Meaning: You've exceeded the rate limit (5 requests/second per store).
Example:
{
"errors": [{
"message": "Too Many Requests"
}]
}How to fix:
- Wait 1 second
- Retry the request
- Implement rate limit handling: Rate limiting guide
Retryable? ✅ Yes — Wait, then retry. Always retryable.
500 Internal Server Error
Meaning: Something went wrong on Yotpo's side.
Example:
{
"errors": [{
"message": "Internal server error"
}]
}How to handle:
- Wait a few seconds
- Retry with exponential backoff (see below)
- If problem persists, contact Yotpo support
Retryable? ✅ Yes — Transient error, safe to retry.
502 Bad Gateway / 503 Service Unavailable
Meaning: Yotpo's API is temporarily unavailable (maintenance, deployment, or overload).
How to handle:
- Wait 5-10 seconds
- Retry with exponential backoff
- If problem persists, check Yotpo status page
Retryable? ✅ Yes — Temporary issue, retry with backoff.
Retry strategies
Which errors should you retry?
| Status Code | Retry? | Strategy |
|---|---|---|
| 400 | ❌ No | Fix request data |
| 401 | ❌ No | Fix authentication |
| 404 | ❌ No | Resource doesn't exist |
| 422 | ❌ No | Fix validation errors |
| 429 | ✅ Yes | Wait 1 second, then retry |
| 500 | ✅ Yes | Exponential backoff |
| 502 | ✅ Yes | Exponential backoff |
| 503 | ✅ Yes | Exponential backoff |
Exponential backoff
For server errors (500, 502, 503), use exponential backoff to avoid overwhelming the API:
Wait times: 1s → 2s → 4s → 8s → give up
import time
import requests
def make_request_with_retry(url, headers, max_retries=4):
for attempt in range(max_retries):
try:
response = requests.get(url, headers=headers)
# Success
if response.status_code == 200:
return response
# Client errors - don't retry
if 400 <= response.status_code < 500 and response.status_code != 429:
raise Exception(f"Client error: {response.status_code}")
# Rate limit - fixed 1s wait
if response.status_code == 429:
time.sleep(1)
continue
# Server error - exponential backoff
if response.status_code >= 500:
if attempt < max_retries - 1:
wait_time = 2 ** attempt
time.sleep(wait_time)
continue
else:
raise Exception(f"Server error after {max_retries} retries")
except requests.exceptions.RequestException as e:
# Network error - retry with backoff
if attempt < max_retries - 1:
wait_time = 2 ** attempt
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")Idempotency and retries
Safe to retry:
GETrequests — Read operations are always safe to retryPUTrequests — Updates with same payload are idempotentDELETErequests — Deleting same resource twice is safe (2nd returns 404)
Retry with caution:
POSTrequests — May create duplicates if retried
See Idempotency guide for handling POST retries safely.
Best practices
1. Log errors for debugging
Capture full context when errors occur:
import logging
logger = logging.getLogger(__name__)
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
logger.error(
f"API error: {e.response.status_code}",
extra={
"url": url,
"status_code": e.response.status_code,
"response_body": e.response.text,
"request_data": data
}
)
raise2. Handle errors gracefully
Don't fail completely on transient errors:
def sync_products():
try:
products = fetch_products()
return products
except RateLimitError:
logger.warning("Rate limited, using cached data")
return load_from_cache()
except ServerError:
logger.error("Server error, will retry later")
return None3. Validate before sending
Catch errors before making API calls:
def create_product(product_data):
# Validate required fields
if not product_data.get("external_id"):
raise ValueError("external_id is required")
if not product_data.get("name"):
raise ValueError("name is required")
# Validate data formats
if len(product_data.get("currency", "")) != 3:
raise ValueError("currency must be 3-character ISO code")
# Make API call
return api_client.post("/products", data=product_data)4. Set request timeouts
Prevent hanging requests:
try:
response = requests.post(
url,
json=data,
headers=headers,
timeout=30 # 30 second timeout
)
except requests.exceptions.Timeout:
logger.error("Request timed out")
# Retry or handle accordingly5. Monitor error rates
Track error patterns to identify integration issues:
from collections import Counter
error_counter = Counter()
def track_error(status_code):
error_counter[status_code] += 1
# Alert if error rate is high
if error_counter[status_code] > 100:
alert("High error rate for status code: " + str(status_code))Common scenarios
Handling validation errors (422)
def create_order(order_data):
try:
response = api_client.post("/orders", data=order_data)
return response.json()
except ValidationError as e:
# Extract specific validation errors
errors = e.response.json().get("errors", [])
for error in errors:
logger.warning(f"Validation error: {error['message']}")
# Fix data and retry if possible
if "currency" in errors[0]["message"]:
order_data["currency"] = "USD" # Fix
return create_order(order_data) # Retry
raise # Can't fix automaticallyRecovering from authentication errors (401)
class APIClient:
def __init__(self, store_id, secret):
self.store_id = store_id
self.secret = secret
self.token = None
def authenticate(self):
response = requests.post(
f"https://api.yotpo.com/core/v3/stores/{self.store_id}/access_tokens",
json={"secret": self.secret}
)
self.token = response.json()["access_token"]
def make_request(self, method, endpoint, **kwargs):
if not self.token:
self.authenticate()
headers = {"X-Yotpo-Token": self.token}
response = requests.request(method, endpoint, headers=headers, **kwargs)
# Re-authenticate if token expired
if response.status_code == 401:
self.authenticate()
headers = {"X-Yotpo-Token": self.token}
response = requests.request(method, endpoint, headers=headers, **kwargs)
return responseHandling server errors with retry queue
from queue import Queue
import time
retry_queue = Queue()
def process_with_retry():
while True:
# Get next item from queue
if retry_queue.empty():
time.sleep(1)
continue
item = retry_queue.get()
try:
response = make_request(item["url"], item["data"])
if response.status_code >= 500:
# Server error - requeue with backoff
item["retry_count"] = item.get("retry_count", 0) + 1
if item["retry_count"] < 4:
time.sleep(2 ** item["retry_count"])
retry_queue.put(item)
else:
logger.error(f"Max retries exceeded for {item['url']}")
except Exception as e:
logger.error(f"Unhandled error: {e}")Troubleshooting
Error message is generic
Problem: Error says "Bad Request" but doesn't specify what's wrong.
Solution:
- Check your JSON syntax (use a validator)
- Review endpoint documentation for required fields
- Compare your payload to working examples
- Enable debug logging to see full request/response
Getting 422 for valid data
Problem: Your data looks correct but validation fails.
Solution:
- Check data types (string vs. number, etc.)
- Verify ISO codes (currency, country, language)
- Check field length limits (see endpoint docs)
- Ensure referenced IDs exist (products, customers)
Random 500 errors
Problem: Same request sometimes works, sometimes returns 500.
Solution:
- Implement retry with exponential backoff
- Check if errors correlate with high traffic times
- If persistent, contact Yotpo support with request IDs
Summary
Key takeaways:
- All errors follow
{"errors": [{"message": "..."}]}format - Client errors (4xx) — Fix your request, don't retry
- Server errors (5xx) — Retry with exponential backoff
- 429 rate limit — Wait 1 second, then retry
- Validate data before sending to catch errors early
Related
- Authentication guide — Fix 401 errors
- Rate limiting — Handle 429 errors
- Idempotency — Safe retry strategies for POST
- Guidelines and conventions — Data formats and validation rules
Updated about 4 hours ago