Error Handling
LinkForty API uses conventional HTTP status codes and returns detailed error information in JSON format.
Error Response Format
All errors follow this structure:
{
"error": "Error Type",
"message": "Human-readable description",
"statusCode": 400,
"validation": {
"field": "Specific field error"
}
}
| Field | Type | Description |
|---|---|---|
error | string | Error type/category |
message | string | Human-readable error message |
statusCode | number | HTTP status code |
validation | object | Field-specific validation errors (optional) |
HTTP Status Codes
2xx Success
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded, no response body |
4xx Client Errors
| Code | Name | When It Happens |
|---|---|---|
| 400 | Bad Request | Invalid request format or validation error |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Resource already exists (e.g., duplicate short code) |
| 422 | Unprocessable Entity | Request valid but semantically incorrect |
| 429 | Too Many Requests | Rate limit exceeded |
5xx Server Errors
| Code | Name | When It Happens |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service unavailable |
| 503 | Service Unavailable | Temporary maintenance or overload |
| 504 | Gateway Timeout | Request timeout |
Common Errors
400 Bad Request
Cause: Invalid request data or validation failure.
Example: Missing required field
{
"error": "Bad Request",
"message": "Validation failed",
"statusCode": 400,
"validation": {
"originalUrl": "Original URL is required",
"templateId": "Template ID must be a valid UUID"
}
}
How to fix:
- Check all required fields are present
- Verify field formats (URLs, UUIDs, numbers)
- Review API documentation for endpoint
401 Unauthorized
Cause: Missing or invalid API key.
Example: No Authorization header
curl https://api.linkforty.com/api/links
# No Authorization header provided
Response:
{
"error": "Unauthorized",
"message": "Missing or invalid API key",
"statusCode": 401
}
How to fix:
curl https://api.linkforty.com/api/links \
-H "Authorization: Bearer lf_live_a1b2c3d4e5f6g7h8"
Example: Invalid API key
{
"error": "Unauthorized",
"message": "Invalid API key",
"statusCode": 401
}
How to fix:
- Verify API key is correct
- Check key hasn't been deleted
- Ensure using correct environment (test vs live)
403 Forbidden
Cause: Authenticated but lacking permissions.
Example: Accessing another organization's resource
{
"error": "Forbidden",
"message": "You do not have access to this resource",
"statusCode": 403
}
How to fix:
- Verify you're in the correct organization
- Check your role (Owner/Admin/Member/Viewer)
- Ensure resource belongs to your organization
404 Not Found
Cause: Resource doesn't exist.
Example: Link not found
curl https://api.linkforty.com/api/links/nonexistent123 \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
Response:
{
"error": "Not Found",
"message": "Link not found",
"statusCode": 404
}
How to fix:
- Verify resource ID is correct
- Check resource hasn't been deleted
- Ensure resource exists in your organization
409 Conflict
Cause: Resource already exists.
Example: Duplicate custom short code
curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "template_123",
"originalUrl": "https://example.com",
"customCode": "summer-sale"
}'
Response:
{
"error": "Conflict",
"message": "Short code 'summer-sale' is already in use",
"statusCode": 409
}
How to fix:
- Use a different custom code
- Let LinkForty generate a random code (omit
customCode) - Check if link was already created
422 Unprocessable Entity
Cause: Request valid but can't be processed.
Example: Invalid URL format
{
"error": "Unprocessable Entity",
"message": "Invalid URL format",
"statusCode": 422,
"validation": {
"originalUrl": "Must be a valid URL starting with http:// or https://"
}
}
How to fix:
- Verify URL format is correct
- Ensure URL is reachable
- Check for typos
429 Too Many Requests
Cause: Rate limit exceeded.
Response:
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 42 seconds.",
"statusCode": 429,
"retryAfter": 42
}
Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642531200
Retry-After: 42
How to fix:
- Wait for
retryAfterseconds - Implement exponential backoff
- Use bulk endpoints
- See Rate Limits for details
500 Internal Server Error
Cause: Unexpected server error.
Response:
{
"error": "Internal Server Error",
"message": "An unexpected error occurred. Please try again.",
"statusCode": 500
}
How to fix:
- Retry the request
- If persistent, contact support
- Check status page: status.linkforty.com
Validation Errors
Validation errors include field-specific details:
{
"error": "Bad Request",
"message": "Validation failed",
"statusCode": 400,
"validation": {
"originalUrl": "Must be a valid URL",
"attributionWindowHours": "Must be between 1 and 2160",
"customCode": "Must be 4-30 characters, alphanumeric only"
}
}
Common validation errors:
| Field | Error | Fix |
|---|---|---|
originalUrl | "Must be a valid URL" | Include http:// or https:// |
templateId | "Must be a valid UUID" | Use proper template ID format |
attributionWindowHours | "Must be between 1 and 2160" | Set value in allowed range |
customCode | "Already in use" | Choose different code |
expiresAt | "Must be a future date" | Set date in future |
Error Handling Best Practices
1. Always Check Status Code
const response = await fetch('https://api.linkforty.com/api/links', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(linkData)
});
if (!response.ok) {
const error = await response.json();
console.error(`Error ${error.statusCode}: ${error.message}`);
throw new Error(error.message);
}
const link = await response.json();
2. Handle Specific Errors
try {
const response = await createLink(linkData);
return response;
} catch (error) {
if (error.statusCode === 409) {
// Duplicate short code - try with different code
return createLink({ ...linkData, customCode: generateRandomCode() });
} else if (error.statusCode === 429) {
// Rate limited - wait and retry
await sleep(error.retryAfter * 1000);
return createLink(linkData);
} else if (error.statusCode === 400) {
// Validation error - show to user
throw new ValidationError(error.validation);
} else if (error.statusCode >= 500) {
// Server error - retry with backoff
return retryWithBackoff(() => createLink(linkData));
} else {
// Other error - propagate
throw error;
}
}
3. Implement Retry Logic
async function retryWithBackoff(fn, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries || error.statusCode < 500) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt), 32000);
console.log(`Retry ${attempt}/${maxRetries} in ${delay}ms`);
await sleep(delay);
}
}
}
4. Log Errors Properly
async function createLink(data) {
try {
const response = await fetch('https://api.linkforty.com/api/links', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
logError({
endpoint: '/api/links',
method: 'POST',
statusCode: error.statusCode,
message: error.message,
requestData: data,
timestamp: new Date().toISOString()
});
throw error;
}
return await response.json();
} catch (error) {
throw error;
}
}
5. User-Friendly Error Messages
function getUse rMessage(error) {
switch (error.statusCode) {
case 400:
return `Invalid input: ${Object.values(error.validation || {}).join(', ')}`;
case 401:
return 'Authentication failed. Please check your API key.';
case 403:
return 'You don\'t have permission to perform this action.';
case 404:
return 'The requested resource was not found.';
case 409:
return 'This short code is already in use. Please choose another.';
case 429:
return `Too many requests. Please wait ${error.retryAfter} seconds.`;
case 500:
case 502:
case 503:
case 504:
return 'Server error. Please try again later.';
default:
return 'An unexpected error occurred. Please try again.';
}
}
SDK Error Handling
Official SDKs provide typed errors:
JavaScript/TypeScript
import { LinkFortySDK, LinkFortyError } from '@linkforty/sdk';
try {
const link = await client.links.create({
templateId: 'template_123',
originalUrl: 'https://example.com'
});
} catch (error) {
if (error instanceof LinkFortyError) {
console.error(`LinkForty Error ${error.statusCode}: ${error.message}`);
if (error.validation) {
console.error('Validation errors:', error.validation);
}
} else {
console.error('Unexpected error:', error);
}
}
Python
from linkforty import LinkForty, LinkFortyError, ValidationError
try:
link = client.links.create(
template_id='template_123',
original_url='https://example.com'
)
except ValidationError as e:
print(f"Validation failed: {e.validation}")
except LinkFortyError as e:
print(f"LinkForty error {e.status_code}: {e.message}")
except Exception as e:
print(f"Unexpected error: {e}")
Debugging Errors
Enable Debug Logging
const client = new LinkFortySDK({
apiKey: API_KEY,
debug: true // Logs all requests/responses
});
Check Request/Response
const response = await fetch('https://api.linkforty.com/api/links', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(linkData)
});
console.log('Status:', response.status);
console.log('Headers:', Object.fromEntries(response.headers));
const body = await response.text();
console.log('Body:', body);
Test with cURL
curl -v -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "template_123",
"originalUrl": "https://example.com"
}'
Production Error Monitoring
1. Centralized Error Tracking
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: 'your-sentry-dsn'
});
async function createLink(data) {
try {
return await client.links.create(data);
} catch (error) {
Sentry.captureException(error, {
tags: {
endpoint: 'create_link',
statusCode: error.statusCode
},
extra: {
requestData: data
}
});
throw error;
}
}
2. Error Metrics
const errorCounts = {
400: 0,
401: 0,
404: 0,
429: 0,
500: 0
};
function trackError(error) {
errorCounts[error.statusCode] = (errorCounts[error.statusCode] || 0) + 1;
// Send to monitoring service
metrics.increment('linkforty.api.error', {
statusCode: error.statusCode,
endpoint: error.endpoint
});
}
3. Alert on Error Spikes
if (errorCounts[500] > 10) {
alert('High rate of 500 errors from LinkForty API!');
}
if (errorCounts[429] > 5) {
alert('Hitting rate limits frequently - consider upgrading plan');
}
Troubleshooting Guide
Error: "Invalid API key"
Check:
- API key starts with
lf_live_orlf_test_ - Key hasn't been deleted from dashboard
- Using correct environment (dev vs prod)
Fix:
- Generate new API key from dashboard
- Update environment variables
Error: "Template not found"
Check:
- Template ID is correct
- Template belongs to your organization
- Template hasn't been deleted
Fix:
- List templates:
GET /api/templates - Create new template if needed
- Use correct template ID
Error: "Short code already in use"
Check:
- Custom code is globally unique
- Code hasn't been used by your org before
Fix:
- Choose different custom code
- Omit
customCodeto auto-generate - Check existing links:
GET /api/links
Next Steps
- 🔑 Authentication - API key management
- ⏱️ Rate Limits - Understand rate limiting
- 🔗 Create Link API - API reference
- 📊 SDK Integration - Use official SDKs
Support
Having trouble with errors?
- 📚 API Documentation
- 💬 Community Forum
- 🐛 Report Bug
- 📧 Email: support@linkforty.com