Error Handling
The Relink API uses standardized HTTP status codes and a consistent error response format to indicate the success or failure of API requests. All error responses follow the same structure for easy parsing and handling.
Standardized Error Response Format
All API errors return a standardized JSON response:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable error message",
"details": [
{
"path": "field.path",
"message": "Field specific error message"
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | Always false for error responses |
error.code | string | Machine-readable error code |
error.message | string | Human-readable error description |
error.details | array | Optional detailed validation errors |
timestamp | string | ISO 8601 timestamp of the error |
Authentication Errors (401)
Missing Authorization Header
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Authorization header missing"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Invalid API Key Format
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key format"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Invalid/Inactive API Key
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Permission & Subscription Errors (403)
Subscription Limit Reached
{
"success": false,
"error": {
"code": "SUBSCRIPTION_LIMIT_REACHED",
"message": "Subscription limit reached"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}API Access Not Available for Plan
{
"success": false,
"error": {
"code": "SUBSCRIPTION_LIMIT_REACHED",
"message": "API access is not available for Starter subscription. Please upgrade your plan to use API features."
},
"timestamp": "2024-01-15T10:30:00.000Z"
}User Doesn’t Own Resource
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to access this resource"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Validation Errors (400)
Schema Validation Failed
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"path": "configuration.smartlinkUrl",
"message": "Smartlink URL can only contain latin characters and numbers"
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Invalid URL Format
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"path": "openGraph.imageUrl",
"message": "Invalid URL format"
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Reserved URL Error
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"path": "configuration.smartlinkUrl",
"message": "Smartlink URL cannot be one of the following: dashboard, welcome, pp, tos."
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Missing Required Fields
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"path": "configuration.smartlinkName",
"message": "Smartlink name is required"
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Invalid Smartlink Type
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"path": "configuration.smartlinkType",
"message": "Invalid smartlink type. Must be one of: App, Url, Landing, QRTag, DigiCard, TxtBin"
}
]
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Resource Not Found Errors (404)
Smartlink Not Found
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Smartlink not found"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Resource Doesn’t Belong to User
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Smartlink not found or doesn't belong to user"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Conflict Errors (409)
Duplicate Smartlink URL
{
"success": false,
"error": {
"code": "CONFLICT",
"message": "Smartlink URL already exists"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Rate Limiting Errors (429)
Too Many Requests
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests, please try again later"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Rate Limits by Plan:
- Professional: 1000 requests per hour
- Enterprise: 5000 requests per hour
Server Errors (500)
Internal Server Error
{
"success": false,
"error": {
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal server error"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Service Unavailable
{
"success": false,
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}URL Safety Errors (400)
The API automatically checks all URLs against Google Safe Browsing API for security.
Single Unsafe URL
{
"success": false,
"error": {
"code": "UNSAFE_URL_DETECTED",
"message": "Unsafe URL detected: https://malicious-site.com"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Multiple Unsafe URLs
{
"success": false,
"error": {
"code": "UNSAFE_URL_DETECTED",
"message": "Multiple unsafe URLs detected in configuration"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}Error Code Reference
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_FAILED | Request validation failed |
| 400 | UNSAFE_URL_DETECTED | URL failed security check |
| 401 | UNAUTHORIZED | Authentication failed |
| 403 | FORBIDDEN | Insufficient permissions |
| 403 | SUBSCRIPTION_LIMIT_REACHED | Plan limits exceeded |
| 404 | NOT_FOUND | Resource not found |
| 409 | CONFLICT | Resource already exists |
| 429 | RATE_LIMIT_EXCEEDED | Rate limit exceeded |
| 500 | INTERNAL_SERVER_ERROR | Server error |
| 503 | SERVICE_UNAVAILABLE | Service temporarily down |
Handling Errors in Code
JavaScript Example
async function handleApiRequest(url, options) {
try {
const response = await fetch(url, options);
const data = await response.json();
if (!data.success) {
// Handle different error types
switch (data.error.code) {
case 'UNAUTHORIZED':
console.error('Authentication failed:', data.error.message);
// Redirect to login or refresh API key
break;
case 'VALIDATION_FAILED':
console.error('Validation errors:');
data.error.details?.forEach(detail => {
console.error(`${detail.path}: ${detail.message}`);
});
break;
case 'SUBSCRIPTION_LIMIT_REACHED':
console.error('Subscription limit reached:', data.error.message);
// Show upgrade prompt
break;
case 'RATE_LIMIT_EXCEEDED':
console.error('Rate limit exceeded, retrying in 60 seconds...');
// Implement retry logic
setTimeout(() => handleApiRequest(url, options), 60000);
return;
default:
console.error('API Error:', data.error.message);
}
throw new Error(data.error.message);
}
return data;
} catch (error) {
console.error('Request failed:', error.message);
throw error;
}
}
// Usage
try {
const result = await handleApiRequest('https://relink.is/api/v1/smartlink', {
method: 'POST',
headers: {
'Authorization': 'Bearer rlk_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
configuration: {
smartlinkName: "Test App",
smartlinkType: "App"
}
})
});
console.log('Success:', result.data);
} catch (error) {
// Error already handled in handleApiRequest
}Python Example
import requests
import time
from typing import Dict, Any
class RelinkAPIError(Exception):
def __init__(self, error_data: Dict[str, Any]):
self.code = error_data.get('code')
self.message = error_data.get('message')
self.details = error_data.get('details', [])
super().__init__(self.message)
def handle_api_request(url: str, **kwargs) -> Dict[str, Any]:
try:
response = requests.request(**kwargs, url=url)
data = response.json()
if not data.get('success', False):
error = data.get('error', {})
# Handle specific error types
if error.get('code') == 'RATE_LIMIT_EXCEEDED':
print("Rate limit exceeded, waiting 60 seconds...")
time.sleep(60)
return handle_api_request(url, **kwargs)
elif error.get('code') == 'VALIDATION_FAILED':
print("Validation errors:")
for detail in error.get('details', []):
print(f" {detail.get('path')}: {detail.get('message')}")
raise RelinkAPIError(error)
return data
except requests.RequestException as e:
print(f"Request failed: {e}")
raise
# Usage
try:
result = handle_api_request(
url="https://relink.is/api/v1/smartlink",
method="POST",
headers={
"Authorization": "Bearer rlk_your_api_key_here",
"Content-Type": "application/json"
},
json={
"configuration": {
"smartlinkName": "Test App",
"smartlinkType": "App"
}
}
)
print("Success:", result['data'])
except RelinkAPIError as e:
print(f"API Error [{e.code}]: {e.message}")
if e.details:
for detail in e.details:
print(f" {detail.get('path')}: {detail.get('message')}")Best Practices
Error Handling Strategy
- Always check the
successfield in responses - Implement specific handling for different error codes
- Log error details for debugging and monitoring
- Provide user-friendly messages for validation errors
- Implement retry logic for rate limiting and server errors
Validation Error Handling
- Parse the
detailsarray for field-specific errors - Highlight problematic fields in your UI
- Show specific validation messages to users
- Validate data client-side before API calls when possible
Rate Limiting
- Implement exponential backoff for rate limit errors
- Cache responses when appropriate to reduce API calls
- Monitor your API usage to avoid hitting limits
- Consider upgrading your plan if you consistently hit limits
Security
- Never expose API keys in client-side code
- Rotate API keys regularly
- Monitor for unauthorized access attempts
- Handle authentication errors gracefully without exposing sensitive information