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.

StatuscodeWhat it means
400validation_failedA field is missing or invalid. For example, empty text, or both publish_now and scheduled_date set.
400invalid_jsonThe request body is not valid JSON. Check the Content-Type header and the payload.
401unauthorizedMissing or invalid API key. Send Authorization: Bearer ot_your_key.
403subscription_requiredYour plan does not include this. Upgrade or connect an eligible account.
429rate_limitToo Many Requests. You exceeded your per-minute or daily limit. Back off and retry.
500internal_errorSomething 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

A 400, 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

A 429 (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.