Skip to main content

Error Handling

Learn how to handle errors gracefully in your LosCenotes API integration.


Error Response Format

All API errors follow a consistent structure:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields are invalid",
"status": 400,
"details": {
"field": "email",
"constraint": "Invalid email format"
},
"requestId": "req_1234567890abcdef",
"timestamp": "2024-01-15T10:30:00Z"
}
}

HTTP Status Codes

The LosCenotes API uses standard HTTP status codes:

Status CodeMeaningDescription
200OKRequest successful
201CreatedResource created successfully
400Bad RequestInvalid request parameters
401UnauthorizedInvalid or missing API key
403ForbiddenInsufficient permissions
404Not FoundResource not found
409ConflictResource already exists
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error
503Service UnavailableService temporarily unavailable

Common Error Codes

Authentication Errors

INVALID_API_KEY

{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid",
"status": 401
}
}

AUTHENTICATION_REQUIRED

{
"success": false,
"error": {
"code": "AUTHENTICATION_REQUIRED",
"message": "Authentication is required for this endpoint",
"status": 401
}
}

Validation Errors

VALIDATION_ERROR

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields are invalid",
"status": 400,
"details": [
{
"field": "email",
"constraint": "Invalid email format"
}
]
}
}

Resource Errors

RESOURCE_NOT_FOUND

{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested resource was not found",
"status": 404,
"details": {
"resource": "cenote",
"id": "cenote-not-found"
}
}
}

Business Logic Errors

CENOTE_NOT_AVAILABLE

{
"success": false,
"error": {
"code": "CENOTE_NOT_AVAILABLE",
"message": "The cenote is not available for the selected date",
"status": 422,
"details": {
"cenoteId": "cenote-uuid",
"date": "2025-01-15",
"reason": "maintenance"
}
}
}

INSUFFICIENT_CAPACITY

{
"success": false,
"error": {
"code": "INSUFFICIENT_CAPACITY",
"message": "Not enough capacity for the requested reservation",
"status": 422,
"details": {
"requested": 10,
"available": 5
}
}
}

Rate Limiting Errors

RATE_LIMIT_EXCEEDED

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later",
"status": 429,
"details": {
"limit": 1000,
"window": "1 hour",
"resetTime": "2025-01-15T11:00:00Z"
}
}
}

Error Handling Best Practices

1. Always Check the Success Field

const response = await fetch('https://service-gateway.loscenotes.com/partner/cenotes', {
headers: { 'X-API-Key': apiKey }
});

const data = await response.json();

if (!data.success) {
console.error('API Error:', data.error.code, data.error.message);

// Handle specific error codes
switch (data.error.code) {
case 'RESOURCE_NOT_FOUND':
// Handle not found
break;
case 'RATE_LIMIT_EXCEEDED':
// Implement backoff strategy
break;
default:
// Handle generic error
break;
}
return;
}

// Success - use data.data
console.log(data.data);

2. Implement Exponential Backoff

async function apiCallWithRetry(apiCall, maxRetries = 3, baseDelay = 1000) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await apiCall();
const data = await response.json();

if (data.success) {
return data;
}

if (data.error.status === 429 && attempt < maxRetries - 1) {
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}

throw data.error;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
throw new Error('Max retries exceeded');
}

// Usage
const data = await apiCallWithRetry(() =>
fetch('https://service-gateway.loscenotes.com/partner/cenotes', {
headers: { 'X-API-Key': apiKey }
})
);

3. Validate Input Before API Calls

function validateReservationData(data) {
const errors = [];

if (!data.cenoteId) {
errors.push('Cenote ID is required');
}

if (!data.date || new Date(data.date) < new Date()) {
errors.push('Valid future date is required');
}

if (!data.ageBreakdown || !data.ageBreakdown.adult) {
errors.push('At least one adult is required');
}

return errors;
}

// Usage
const validationErrors = validateReservationData(reservationData);
if (validationErrors.length > 0) {
console.error('Validation errors:', validationErrors);
return;
}

// Proceed with API call
const response = await createReservation(reservationData);

4. Handle Network Errors

try {
const response = await fetch('https://service-gateway.loscenotes.com/partner/cenotes', {
headers: { 'X-API-Key': apiKey }
});

if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}

const data = await response.json();
// Process data...

} catch (error) {
if (error.name === 'TypeError') {
console.error('Network error - check your internet connection');
} else if (error.message.includes('timeout')) {
console.error('Request timeout - try again later');
} else {
console.error('Error:', error.message);
}
}

5. Log Errors for Debugging

function logApiError(error, context) {
const errorLog = {
timestamp: new Date().toISOString(),
requestId: error.requestId,
endpoint: context.endpoint,
method: context.method,
errorCode: error.code || 'UNKNOWN_ERROR',
errorMessage: error.message || 'Unknown error occurred',
details: error.details
};

console.error('LosCenotes API Error:', JSON.stringify(errorLog, null, 2));

// Send to your logging service
// yourLogger.error(errorLog);
}

// Usage
try {
const response = await makeApiCall();
} catch (error) {
logApiError(error, {
endpoint: '/partner/cenotes',
method: 'GET'
});
}

Pricing Specific Errors

CodeDescription
error.cenote.not_foundCenote ID not found
error.tour.not_foundTour ID not found
error.pricing.invalid_item_typeItem type must be "cenote" or "tour"
error.transport.pickup_required_when_includedPickup info required when transport is included

Payment Errors

CodeDescription
card_declinedCard was declined
insufficient_fundsInsufficient funds
expired_cardCard has expired
invalid_cvcInvalid security code
processing_errorPayment processing error

Next Steps