Skip to main content

Error Handling

The Flow API uses a consistent error response format to help you handle errors gracefully in your application.

Error Response Format

All errors follow this structure:

{
"error": {
"type": "error_type",
"message": "Human-readable error message",
"code": "ERROR_CODE",
"details": [
{
"field": "field_name",
"message": "Field-specific error message"
}
]
},
"meta": {
"request_id": "req_abc123",
"timestamp": 1703123456000
}
}

Error Types

authentication_error (401)

Returned when authentication fails or is missing.

{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "INVALID_API_KEY"
}
}

Common causes:

  • Missing Authorization header
  • Invalid API key format
  • Revoked or expired API key

authorization_error (403)

Returned when the authenticated user doesn't have permission to perform the action.

{
"error": {
"type": "authorization_error",
"message": "You don't have permission to access this resource",
"code": "FORBIDDEN"
}
}

not_found (404)

Returned when a requested resource doesn't exist.

{
"error": {
"type": "not_found",
"message": "Post not found",
"code": "RESOURCE_NOT_FOUND"
}
}

validation_error (400)

Returned when request validation fails.

{
"error": {
"type": "validation_error",
"message": "Validation failed",
"code": "VALIDATION_ERROR",
"details": [
{
"field": "content",
"message": "Content is required"
},
{
"field": "channelId",
"message": "Channel ID must be a valid UUID"
}
]
}
}

rate_limit_error (429)

Returned when the rate limit is exceeded.

{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": [
{
"message": "Retry after 45 seconds"
}
]
},
"meta": {
"request_id": "req_abc123",
"timestamp": 1703123456000
}
}

Response headers:

  • X-RateLimit-Limit: Maximum requests per window (100)
  • X-RateLimit-Remaining: Remaining requests in current window
  • X-RateLimit-Reset: Unix timestamp when the limit resets

server_error (500)

Returned when an internal server error occurs.

{
"error": {
"type": "server_error",
"message": "Internal server error",
"code": "INTERNAL_ERROR"
}
}

Handling Errors

TypeScript (with SDK)

import { Flow, FlowError } from '@flowdev/sdk';

const flow = new Flow('flow_sk_live_...');

try {
const post = await flow.posts.create({
channelId: 'channel_123',
content: 'Hello, world!',
});
} catch (error) {
if (error instanceof FlowError) {
switch (error.type) {
case 'validation_error':
console.error('Validation failed:', error.details);
break;
case 'rate_limit_error':
console.error('Rate limit exceeded, retry after:', error.retryAfter);
// Wait and retry
await new Promise(resolve => setTimeout(resolve, error.retryAfter * 1000));
break;
case 'authentication_error':
console.error('Invalid API key');
break;
default:
console.error('API error:', error.message);
}
}
}

JavaScript (fetch)

async function createPost(apiKey, data) {
const response = await fetch('https://api.flowsocial.app/v1/posts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});

if (!response.ok) {
const error = await response.json();

if (response.status === 429) {
// Rate limit exceeded
const retryAfter = parseInt(response.headers.get('X-RateLimit-Reset')) - Date.now() / 1000;
console.error('Rate limit exceeded, retry after:', retryAfter, 'seconds');
return;
}

if (response.status === 400) {
// Validation error
console.error('Validation errors:', error.error.details);
return;
}

console.error('API error:', error.error.message);
return;
}

return await response.json();
}

Python

import requests
import time

def create_post(api_key, data):
response = requests.post(
'https://api.flowsocial.app/v1/posts',
headers={
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json',
},
json=data,
)

if response.status_code == 429:
# Rate limit exceeded
retry_after = int(response.headers.get('X-RateLimit-Reset', 0)) - int(time.time())
print(f'Rate limit exceeded, retry after {retry_after} seconds')
return None

if response.status_code == 400:
# Validation error
error = response.json()
print('Validation errors:', error['error']['details'])
return None

if not response.ok:
error = response.json()
print('API error:', error['error']['message'])
return None

return response.json()

Retry Logic

Exponential Backoff

For rate limit errors and server errors, implement exponential backoff:

async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error instanceof FlowError && error.type === 'rate_limit_error') {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}

Best Practices

  1. Always check response status before processing data
  2. Handle rate limits gracefully with exponential backoff
  3. Log request IDs for debugging (included in error responses)
  4. Validate input before sending requests to avoid validation errors
  5. Implement retry logic for transient errors (5xx, rate limits)
  6. Don't retry on client errors (4xx) except rate limits

Request IDs

Every API response (including errors) includes a request_id in the meta field. Use this when contacting support:

Request ID: req_abc123def456

This helps us quickly locate and debug issues.