Appearance
Error Codes
FreshThe RSS.app API uses standard HTTP status codes. All error responses include a JSON body with details about what went wrong.
Error Response Format
json
{
"message": "Human-readable error message",
"statusCode": 400,
"errors": [
{
"title": "Specific error title",
"code": "machine_readable_code"
}
]
}The errors array may contain multiple entries when a request has several validation problems.
HTTP Status Codes
| Code | Name | When It Occurs | How to Handle |
|---|---|---|---|
| 200 | OK | Request succeeded | Normal operation |
| 400 | Bad Request | Missing required field, malformed JSON, invalid URL format | Fix the request body — check required fields and field types |
| 401 | Unauthorized | Missing or invalid Authorization header | Verify your API Key and Secret format: Bearer KEY:SECRET |
| 402 | Payment Required | Plan limit reached (feeds, bundles, etc.) | Upgrade your RSS.app plan |
| 403 | Forbidden | Authenticated but not allowed to access this resource | Verify the resource belongs to your account |
| 404 | Not Found | Feed or bundle ID does not exist or belongs to another account | Confirm the ID is correct and was created by your account |
| 409 | Conflict | Resource already exists — e.g., feed for this URL already created | Use the existing resource or delete it before recreating |
| 429 | Too Many Requests | Rate limit exceeded | Back off and retry after the specified delay |
| 5xx | Server Error | RSS.app internal error | Retry with exponential backoff; contact support if persistent |
Handling Errors in Node.js
js
const axios = require('axios');
async function createFeed(url) {
try {
const response = await apiClient.post('/feeds', { url });
return response.data;
} catch (err) {
if (!err.response) {
// Network error — no response received
throw new Error(`Network error: ${err.message}`);
}
const { status, data } = err.response;
switch (status) {
case 400:
throw new Error(`Bad request: ${data.message}`);
case 401:
throw new Error('Authentication failed — check API Key and Secret');
case 402:
throw new Error('Plan limit reached — upgrade required');
case 403:
throw new Error('Access denied to this resource');
case 404:
throw new Error(`Feed not found: ${url}`);
case 409:
throw new Error(`Feed already exists for this URL`);
case 429:
throw new Error('Rate limit exceeded — slow down requests');
default:
throw new Error(`API error ${status}: ${data.message}`);
}
}
}Rate Limiting
The 429 status indicates you've exceeded the API rate limit. The response may include a Retry-After header indicating how many seconds to wait.
Exponential backoff pattern:
js
async function withRetry(fn, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
if (err.response?.status === 429 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt}/${maxRetries})`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw err;
}
}
}
// Usage
const feed = await withRetry(() => apiClient.post('/feeds', { url: 'https://example.com' }));409 Conflict — Duplicate Feed
When you try to create a feed for a URL that already exists in your account, the API returns 409. To handle this gracefully, either check before creating or catch the conflict and retrieve the existing feed:
js
async function getOrCreateFeed(url) {
try {
const response = await apiClient.post('/feeds', { url });
return response.data;
} catch (err) {
if (err.response?.status === 409) {
// Feed already exists — find and return it
const allFeeds = await getAllFeeds();
const existing = allFeeds.find((f) => f.source_url === url);
if (existing) return existing;
}
throw err;
}
}5xx Server Errors
Server errors (500, 502, 503, 504) indicate a problem on the RSS.app side. These are typically transient. Retry with exponential backoff. If errors persist across multiple retries over several minutes, the service may be experiencing an outage.