API Documentation
Learn how to connect and integrate with our API.
Authentication
All the API endpoints require an API key sent by the Bearer Authentication method.
--url 'https://fomoboost.com/api/{endpoint}' \
--header 'Authorization: Bearer {api_key}' \
Rate limiting
Requests are limited independently per IP address, account, API key, and endpoint. All limits must have available quota for a request to succeed.
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (seconds until the API key window resets), X-RateLimit-Policy, and X-RateLimit-Endpoint. On 429 responses, Retry-After (seconds) and X-RateLimit-Dimension indicate which limit was exceeded.
Safe retry rules
Integrations, plugins, and API clients must implement resilient retry behaviour. Never retry immediately in a tight loop.
- Retry only
429and transient5xxresponses — never retry4xxvalidation or auth errors. - On
429, wait at least theRetry-Afterheader value (in seconds) before the next attempt. - Use exponential backoff with jitter: start at 1s, double each attempt, cap at 60s, add random jitter (±20%).
- Use a circuit breaker: after 5 consecutive failures, stop requests for 30–120s, then allow a single probe.
- Respect
X-RateLimit-Remainingand throttle proactively when remaining quota is low.
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 42
X-RateLimit-Dimension: api_key
{
"errors": [
{
"title": "You reached the limit of requests in one minute, please wait.",
"status": "429",
"detail": "Rate limit exceeded for dimension \"api_key\" on endpoint \"campaigns:GET\". Retry after 42 seconds." }
],
"meta": {
"rate_limit": {
"dimension": "api_key",
"endpoint": "campaigns:GET",
"limit": 60,
"retry_after": 42
}
}
}
class FomoApiClient {
constructor({ baseUrl, apiKey, maxRetries = 5 }) {
this.baseUrl = baseUrl.replace(/\/$/, '');
this.apiKey = apiKey;
this.maxRetries = maxRetries;
this.consecutiveFailures = 0;
this.circuitOpenUntil = 0;
}
async request(path, options = {}) {
if(Date.now() < this.circuitOpenUntil) {
throw new Error('Circuit breaker open — too many recent failures');
}
let attempt = 0;
while(true) {
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Accept': 'application/json',
...(options.headers || {}),
},
});
if(response.ok) {
this.consecutiveFailures = 0;
return response.json();
}
const retryable = response.status === 429 || response.status >= 500;
if(!retryable || attempt >= this.maxRetries) {
this.consecutiveFailures++;
if(this.consecutiveFailures >= 5) {
this.circuitOpenUntil = Date.now() + 60000;
}
throw new Error(`API error ${response.status}`);
}
const retryAfterHeader = parseInt(response.headers.get('Retry-After') || '0', 10);
const backoffMs = retryAfterHeader > 0
? retryAfterHeader * 1000
: Math.min(60000, 1000 * Math.pow(2, attempt));
const jitter = backoffMs * (0.8 + Math.random() * 0.4);
await new Promise(resolve => setTimeout(resolve, jitter));
attempt++;
}
}
}
Errors
Our API uses conventional HTTP status codes to indicate the success or failure of a request.
{
"errors": [
{
"title": "You do not have access to the API.",
"status": 401
}
]
}