Errors
Every OpenTweet REST API error returns JSON with the same two fields: a human-readable error message you can log or show, and a stable code you can branch on. The HTTP status tells you whether to fix the request or retry it.
Error shape
Errors are always JSON, never HTML or a bare string:
json
{
"error": "human-readable message you can log or show",
"code": "machine_code"
}Status codes
Branch on the HTTP status first, then read code for the specific cause.
| Status | code | What it means |
|---|---|---|
| 400 | validation_failed | A field is missing or invalid. For example, empty text, or both publish_now and scheduled_date set. |
| 400 | invalid_json | The request body is not valid JSON. Check the Content-Type header and the payload. |
| 401 | unauthorized | Missing or invalid API key. Send Authorization: Bearer ot_your_key. |
| 403 | subscription_required | Your plan does not include this. Upgrade or connect an eligible account. |
| 429 | rate_limit | Too Many Requests. You exceeded your per-minute or daily limit. Back off and retry. |
| 500 | internal_error | Something broke on our side. Retry with backoff. |
When to retry
The rule is simple. A permanent client error will fail the same way every time, so retrying wastes calls against your rate limit. Only transient errors are worth a second attempt.
Do not retry permanent 4xx errors
A400, 401, or 403 means the request itself is wrong. Fix the payload, the key, or the plan. Retrying will not help.Retry 429 and 5xx with backoff
A429 (rate limit) or a 500 (internal error) is transient. Wait, then retry with exponential backoff so you do not hammer the limit. See rate limits for the per-plan numbers.Handling errors in Node
Check the status, parse the JSON, and split permanent failures from retryable ones:
javascript
async function post(text) {
const res = await fetch("https://opentweet.io/api/v1/posts", {
method: "POST",
headers: {
"Authorization": "Bearer ot_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({ text, publish_now: true }),
});
if (!res.ok) {
const { error, code } = await res.json();
// Permanent client errors: fix the request, do not retry.
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
throw new Error(`Fix the request (${code}): ${error}`);
}
// 429 and 5xx are transient: retry with exponential backoff.
throw new RetryableError(`${res.status} ${code}: ${error}`);
}
return res.json();
}Related
- • Rate limits: per-plan requests per minute, daily post limits, and the 429 response.
- • REST API reference: every endpoint, field, and request shape.
- • Authentication: API keys, the Bearer header, and verifying with GET /me.