> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
> Use this file to discover all available pages before exploring further.

# X API error handling for tweet & follower workflows

> Recover Xquik requests, cursors, writes, monitors, webhooks, and dependencies. Follow retry safety, restore account access, and handle reply restrictions.

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

Choose recovery by `error` code.

## Quick reference

Start with HTTP status. Retry only when stated.

<CardGroup cols={2}>
  <Card title="400 request validation" icon="circle-alert">
    Retry: no. Fix the body, query, or path. Examples include
    `invalid_json`, `invalid_id`, `invalid_tweet_url`, `invalid_tweet_id`.
  </Card>

  <Card title="401 authentication" icon="key-round">
    Retry: no. Check `x-api-key`, regenerate revoked keys, or re-authenticate
    the connected X account. Covers `unauthenticated` and `x_auth_failure`.
  </Card>

  <Card title="402 billing and credits" icon="credit-card">
    Retry: no. Read `payment_options`, then get explicit user confirmation.
    Covers `no_subscription`, `subscription_inactive`, `payment_failed`,
    `no_credits`, and `insufficient_credits`.
  </Card>

  <Card title="403 permissions and account health" icon="shield-alert">
    Retry: no. Delete an extra key, check billing status, use a participating
    DM account, re-authenticate the X account, or resolve account health on
    x.com. Covers `api_key_limit_reached`, `dm_not_permitted`,
    `account_needs_reauth`, and `account_restricted`. For `x_account_protected`,
    choose a public timeline.
  </Card>

  <Card title="404 missing resource" icon="search-x">
    Retry: no. Verify the resource ID & connected account. Codes include
    `account_not_found`, `user_not_found`, `tweet_not_found`, `no_media`,
    and other missing resources.
  </Card>

  <Card title="409 or 410 cursor state" icon="refresh-cw">
    Busy cursor: follow `Retry-After` and retry once. Gone cursor: restart
    cursorless and deduplicate IDs.
  </Card>

  <Card title="422 write validation" icon="message-circle-warning">
    Retry: no, except `x_automation_refused`. Fix the account capability,
    target, content, DM permissions, or media URL before sending again.
    Covers `x_dm_not_allowed`, `x_reply_not_allowed`, `x_target_not_found`, `x_content_too_long`,
    and the other [422 codes](#common-error-codes).
  </Card>

  <Card title="202 active write" icon="clock">
    Retry: no. Store the action and poll `statusUrl` while `terminal` is
    `false`. Follow `Retry-After`, `pollAfterMs`, and `nextAction`.
  </Card>

  <Card title="429 rate limit or cooldown" icon="timer">
    Retry: mixed. Retry `rate_limit_exceeded` and `x_rate_limited` after
    `Retry-After` or exponential backoff. Wait out `login_cooldown` via
    `retryAfterMs`. Do not retry `x_daily_limit` on the same X account for 24
    hours.
  </Card>

  <Card title="500, 502, and 503 transient failures" icon="rotate-ccw">
    Retry: yes for `internal_error`, `x_api_rate_limited`,
    `x_api_unavailable` and `x_api_unauthorized`. For writes, retry only when
    `safeToRetry` is `true`, using a new `Idempotency-Key`.
    Retry `pagination_stalled` once after 30 seconds. Keep returned results.
  </Card>
</CardGroup>

## Common error codes

The OpenAPI
[`Error`](https://docs.xquik.com/openapi.yaml) schema lists every public code.

**Default response.**

```json theme={null}
{ "error": "error_code", "message": "Human-readable description" }
```

Send `xquik-api-contract: 2026-04-29` for a structured `error` object. Some
responses also include `message`, `reason`, `retryAfter`, or `retryAfterMs`.

<AccordionGroup>
  <Accordion title="Validation errors (400)">
    Invalid body, query, or path.

    <CardGroup cols={2}>
      <Card title="invalid_input" icon="circle-alert">
        Invalid body. Check the endpoint's required fields, types & enum values.
      </Card>

      <Card title="invalid_json" icon="file-code">
        Invalid JSON body. Send a parseable JSON object.
      </Card>

      <Card title="invalid_id" icon="hash">
        Invalid path ID. Use the create or list endpoint's numeric string.
      </Card>

      <Card title="invalid_tweet_url" icon="link">
        Tweet URL is malformed. Use `https://x.com/user/status/ID`.
      </Card>

      <Card title="invalid_tweet_id" icon="message-circle">
        Send a post ID or post URL, such as `x.com/nasa/status/20`.
      </Card>

      <Card title="invalid_username" icon="users">
        Username is empty or invalid. Send a username without the `@` prefix.
      </Card>

      <Card title="invalid_user_id" icon="users">
        Send a user ID, `@username` or profile URL, such as `x.com/nasa`.
      </Card>

      <Card title="invalid_message_id" icon="hash">
        Send a DM ID from [DM history](/api-reference/x/dm-history).
      </Card>

      <Card title="account_required" icon="users">
        Add your connected X account, such as `?account=@nasa`.
      </Card>

      <Card title="invalid_tool_type" icon="database">
        Unknown extraction tool type. Use one of the 23 types from
        [Create Extraction](/api-reference/extractions/create).
      </Card>

      <Card title="invalid_format" icon="file-text">
        Export format is unsupported. Use `csv`, `json`, `md`, `md-document`,
        `pdf`, `txt`, or `xlsx`.
      </Card>

      <Card title="invalid_params" icon="circle-alert">
        Invalid export query. Check the `format` and `type` values.
      </Card>

      <Card title="missing_query" icon="search">
        Add the `q` parameter to search & community calls.
      </Card>

      <Card title="missing_ids" icon="list">
        Provide comma-separated numeric IDs in `ids`.
      </Card>

      <Card title="missing_params" icon="circle-alert">
        Required query parameters are missing. Follower checks need both source
        & target.
      </Card>

      <Card title="too_many_ids" icon="list">
        Too many IDs. Split them into groups of 100 or fewer.
      </Card>

      <Card title="invalid_media_url" icon="link">
        Send each `media` entry as an HTTP or HTTPS URL.
      </Card>

      <Card title="unsupported_media_combination" icon="image">
        Send 1 MP4 alone, or only images.
      </Card>

      <Card title="unsupported_field" icon="circle-x">
        The body has a field this endpoint rejects. Posts take public media URLs
        in `media`, not uploaded media IDs.
      </Card>

      <Card title="missing_idempotency_key" icon="key-round">
        The X write has no `Idempotency-Key` header. Send 1 unique key per
        intended write, and reuse it only to retry that write.
      </Card>

      <Card title="invalid_idempotency_key" icon="key-round">
        `Idempotency-Key` doesn't match the endpoint's format. Send a random UUID
        v4, which every endpoint accepts.
      </Card>

      <Card title="invalid_coverage_cursor" icon="circle-x">
        Cursor is malformed. Restart without it and deduplicate stored IDs.
      </Card>

      <Card title="invalid_community_topic" icon="circle-x">
        Unknown Community topic. Use a name or ID from
        [Community topics](/api-reference/x/community-topics).
      </Card>

      <Card title="invalid_cursor" icon="circle-x">
        X can't read the cursor, or it expired. Start again without `cursor`,
        then use `next_cursor`. It costs nothing.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Authentication errors (401)">
    Missing or invalid credentials. Check your API key or session.

    <CardGroup cols={2}>
      <Card title="unauthenticated" icon="key-round">
        API key or bearer token is missing or invalid. Send `x-api-key` or
        regenerate a revoked key.
      </Card>

      <Card title="x_auth_failure" icon="refresh-cw">
        The X account's session ended. Re-authenticate it from the
        [dashboard](https://xquik.com/dashboard).
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Anonymous paid-read authentication (401)">
    Non-MPP paid reads return `401` with `WWW-Authenticate: Bearer` and a guest wallet action. This is not a Payment challenge. Authenticate or get confirmation before calling the action.
  </Accordion>

  <Accordion title="Billing & credit errors (402)">
    A `402` creates no checkout. Account and OAuth responses advertise account billing actions. Guest responses advertise only `POST /api/v1/guest-wallets/topups`. Direct MPP responses include a Payment challenge and guest option. Get confirmation before calling any action. A write's `402` adds `top_up_url` & `dashboard`.

    <CardGroup cols={2}>
      <Card title="no_subscription" icon="badge-x">
        No plan. Check credits, then [top up](/api-reference/credits/topup) or [subscribe](/api-reference/account/subscription-checkout).
      </Card>

      <Card title="subscription_inactive" icon="badge-alert">
        Plan inactive. Remaining credits work. Top up or reactivate on the [billing page](https://dashboard.xquik.com/en/account?tab=subscription).
      </Card>

      <Card title="payment_failed" icon="credit-card">
        Payment failed. Update the payment method in the
        [dashboard](https://xquik.com/subscription).
      </Card>

      <Card title="no_credits" icon="coins">
        No credits left. Check [Get Account](/api-reference/account/get), then
        use [Top Up Credits](/api-reference/credits/topup) after confirmation.
      </Card>

      <Card title="insufficient_credits" icon="wallet-cards">
        Balance is below the operation's cost. Account callers check
        [Get Account](/api-reference/account/get), guest callers
        [Guest Wallet Status](/api-reference/guest-wallets/status). Use only the
        payment action advertised for that credential.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Permission errors (403)">
    Plan, limits, or account visibility prevent this action.

    <CardGroup cols={2}>
      <Card title="api_key_limit_reached" icon="key-round">
        The account has 100 active API keys. Revoke one before creating another.
      </Card>

      <Card title="dm_not_permitted" icon="message-circle">
        DM history requires a participating connected account. Choose one or reconnect it from the
        [dashboard](https://xquik.com/dashboard).
      </Card>

      <Card title="account_needs_reauth" icon="refresh-cw">
        The X account needs re-authentication. Reconnect it from the
        [dashboard](https://xquik.com/dashboard), then retry.
      </Card>

      <Card title="account_restricted" icon="shield-alert">
        Connected X account is locked, suspended, recovering, or temporarily
        blocked. Resolve account health on x.com or wait before retrying.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Not found errors (404)">
    The requested resource does not exist, is unavailable to this API key, or
    is not the expected X object type.

    <CardGroup cols={2}>
      <Card title="not_found" icon="search-x">
        Lookup failed. Verify the ID belongs to your account
        and has not been deleted.
      </Card>

      <Card title="account_not_found" icon="users">
        No such connected X account. Call
        [List X Accounts](/api-reference/x-accounts/list) and use one it lists.
      </Card>

      <Card title="user_not_found" icon="users">
        X shows no profile for the username or user ID. `reason` says why:
        `not_found` or `suspended`. Confirm the handle, or choose another
        account when X suspended it.
      </Card>

      <Card title="tweet_not_found" icon="message-circle">
        The tweet ID doesn't resolve. Check it. The author may have deleted the
        tweet.
      </Card>

      <Card title="no_media" icon="image">
        The tweet has no downloadable media. Use a tweet with media.
      </Card>

      <Card title="article_not_found" icon="file-text">
        Valid Tweet ID, but no X Article. Request an Article URL or use tweet or thread endpoints.
      </Card>

      <Card title="draft_not_found" icon="file-text">
        Draft missing. Verify its ID or create one.
      </Card>

      <Card title="style_not_found" icon="pen-line">
        Style ID missing. Analyze tweets with
        [Analyze Style](/api-reference/styles/analyze).
      </Card>

      <Card title="no_cached_style" icon="pen-line">
        No cached style for this username. Analyze tweets with [Analyze Style](/api-reference/styles/analyze).
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Conflict and cursor errors (409/410)">
    Reuse duplicate monitors. Recover cursors by status and error code.

    <CardGroup cols={2}>
      <Card title="monitor_already_exists" icon="copy-check">
        Duplicate account or keyword monitor. List existing monitors, reuse the
        monitor ID, or update event types with
        [Update Monitor](/api-reference/monitors/update) or
        [Update Keyword Monitor](/api-reference/monitors/update-keyword).
      </Card>

      <Card title="coverage_cursor_unavailable" icon="timer">
        Follow the exact `Retry-After` seconds. Retry the same cursor once.
        Repeated busy responses never authorize a fresh extraction.
      </Card>

      <Card title="coverage_cursor_gone" icon="refresh-cw">
        No `Retry-After`. Restart cursorless and deduplicate IDs.
        Keep received results. Restarting creates a separate extraction.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Media errors (413/415)">
    Xquik stops before any X call and charges nothing.

    <CardGroup cols={2}>
      <Card title="unsupported_media_type" icon="file-image">
        Xquik can't read the file or its `Content-Type`. Send a valid, supported file.
      </Card>

      <Card title="media_too_large" icon="image">
        The file is too big, even after Xquik shrinks images. Send a smaller file.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Validation errors (422)">
    Write validation failed. Change the account, target, content, DM
    permission, or media input before retrying.

    <CardGroup cols={2}>
      <Card title="x_account_feature_required" icon="lock-keyhole">
        Missing account capability. Change the account or request.
        Creating a community returns it, uncharged, when X blocks the account.
      </Card>

      <Card title="x_account_suspended" icon="shield-alert">
        X account suspended or restricted. Resolve its status on x.com before further writes.
      </Card>

      <Card title="x_account_protected" icon="lock">
        Target account is protected. Timeline reads return 403, including continuations. Use a public account.
        Profiles remain readable. Xquik collects and charges no results.
        Writes return 422.
      </Card>

      <Card title="x_duplicate_action" icon="copy-check">
        Operation is complete. Check the target before retrying. Never repeat an unchanged request.
      </Card>

      <Card title="x_dm_not_allowed" icon="message-circle">
        Recipient does not accept DMs from this account. Use a permitted
        connected account or ask the recipient to allow messages.
      </Card>

      <Card title="x_dm_not_deleted" icon="message-circle">
        X still shows the DM. Xquik charges nothing. Retry with a new `Idempotency-Key`.
      </Card>

      <Card title="x_target_not_found" icon="search-x">
        Target is missing or invisible to the connected X account.
        Verify the ID and that account's access before sending another request.
        Writes also return it for a username X doesn't know.
      </Card>

      <Card title="x_reply_not_allowed" icon="message-circle-warning">
        This account cannot reply to the post. Choose a post it can reply to.
      </Card>

      <Card title="x_reply_target_unavailable" icon="message-circle-warning">
        The post you're replying to is unavailable on X, so Xquik sent nothing.
        Check the post on x.com or reply to another post.
      </Card>

      <Card title="x_content_too_long" icon="message-circle-warning">
        Content exceeds the character limit. Shorten it or use an account that
        allows longer posts.
      </Card>

      <Card title="x_rejected" icon="circle-x">
        X refused the write and said why. Xquik charges nothing. Change the request.
        Retry only when the durable action marks it safe.
      </Card>

      <Card title="x_automation_refused" icon="bot">
        X said the write might be automated & refused it. Xquik charges
        nothing. Send the next request now with a new `Idempotency-Key`.
        Waiting does not help. If X keeps refusing, check the account on x.com.
        Vary your text or pace.
      </Card>

      <Card title="x_media_rejected" icon="image">
        X couldn't read the uploaded file. Upload a valid JPG, PNG, GIF, WebP,
        or MP4. Do not retry the same file.
      </Card>

      <Card title="x_image_invalid" icon="image">
        X refused the image's size. Send one from 200x100 to 8192x8192 pixels.
      </Card>

      <Card title="media_download_failed" icon="image">
        Xquik could not download the public media URL. Fix the HTTPS URL or pass the
        file via multipart/form-data. Do not retry the same URL.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Active write lifecycle (202)">
    The durable write remains active. Its status is `accepted`, `dispatching`,
    or `pending_confirmation`. Follow
    [write lifecycle recovery](#write-lifecycle-recovery).

    <CardGroup cols={2}>
      <Card title="x_post_not_sent" icon="send">
        X never published the post. Xquik charges nothing. Post again with a new `Idempotency-Key`.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Rate limit errors (429)">
    Request rate exceeded. See [Rate Limits](/guides/rate-limits) for tier details.

    <CardGroup cols={2}>
      <Card title="rate_limit_exceeded" icon="timer">
        You reached an Xquik tier or action limit. Wait the `Retry-After` seconds
        from the response. JSON also includes `retryAfter` when available.
      </Card>

      <Card title="login_cooldown" icon="clock">
        A recent login attempt triggered cooldown. Wait `retryAfterMs` or the
        `Retry-After` header before reconnecting or reauthenticating.
      </Card>

      <Card title="x_rate_limited" icon="gauge">
        X throttled the write. Follow `Retry-After`, then retry with backoff.
      </Card>

      <Card title="x_daily_limit" icon="calendar-x">
        Connected X account reached its daily posting limit. Wait 24 hours
        before retrying that account, or use another connected account.
      </Card>
    </CardGroup>
  </Accordion>

  <Accordion title="Server & service errors (500/502/503)">
    Transient failures. Retry with exponential backoff, up to 3 attempts.

    <CardGroup cols={2}>
      <Card title="internal_error" icon="server-crash">
        Server error. Retry with backoff and
        [contact support](mailto:support@xquik.com) if it continues.
      </Card>

      <Card title="x_api_rate_limited" icon="timer-reset">
        Read service rate limited. Retry in a few minutes.
      </Card>

      <Card title="x_api_unavailable" icon="cloud-off">
        Read service temporarily unavailable or busy. Respect `Retry-After`
        when present, otherwise retry with backoff.
      </Card>

      <Card title="pagination_stalled" icon="pause">
        Pagination stopped after repeated empty pages. Retry the same cursor once after 30 seconds.
        A second stall ends the list. Keep returned rows. Deduplicate by ID.
      </Card>

      <Card title="x_api_unauthorized" icon="key-round">
        Read service authentication failed. Retry later and
        [contact support](mailto:support@xquik.com) if it continues.
      </Card>

      <Card title="x_write_failed" icon="circle-x">
        Xquik could not explain why the write failed. Follow `safeToRetry` &
        `nextAction`. Contact support if it stays unsafe to retry.
      </Card>

      <Card title="x_write_ambiguous" icon="activity">
        X may have applied the write. X gave 408, 500, 502, 503 or 504 after the
        send, or no confirmation. Poll the action & check the result before
        sending again.
      </Card>

      <Card title="x_transient_error" icon="rotate-ccw">
        A temporary failure, such as X's 408, 500, 502, 503 or 504 before the
        send. Retry only when `safeToRetry` is `true`.
      </Card>
    </CardGroup>
  </Accordion>
</AccordionGroup>

## Write lifecycle recovery

Writes can return durable actions. Check their lifecycle fields first.

<Steps>
  <Step title="Store the action">
    Store `id`, `status`, `request.hash`, `account`, `target`, `billing`, and
    `statusUrl`.
  </Step>

  <Step title="Poll the write action">
    Poll `statusUrl` while `terminal` is `false`. Respect `Retry-After` and
    `pollAfterMs`.
  </Step>

  <Step title="Store the outcome">
    Store `result` and settled `billing` after `terminal` becomes `true`.
  </Step>

  <Step title="Follow retry safety">
    Retry only when `safeToRetry` is `true`, using a new `Idempotency-Key`.
    Verify the result when `nextAction.type` is `verify_result`.
  </Step>
</Steps>

## Retry with exponential backoff

For reads, retry `pagination_stalled` once. Otherwise retry `429` and `5xx`. Use `Retry-After`, then
exponential backoff with jitter. For writes, follow the durable action's
`safeToRetry` and `nextAction` fields instead.

**Formula.** `delay = baseDelay * 2^attempt + random(0, jitter)`

```ts theme={null}
function retryDelayMs(response: Response, attempt: number): number {
  const retryAfter = response.headers.get("Retry-After");

  if (retryAfter) {
    return Number.parseInt(retryAfter, 10) * 1000;
  }

  return 1000 * 2 ** attempt + Math.floor(Math.random() * 1000);
}

async function shouldRetry(response: Response, attempt: number): Promise<boolean> {
  const body = await response
    .clone()
    .json()
    .catch(() => null);
  if (body?.error === "pagination_stalled") return attempt === 0;
  return response.status === 429 || response.status >= 500;
}
```

## Rate limit handling

<CardGroup cols={2}>
  <Card title="HTTP status" icon="gauge">
    `429 Too Many Requests` signals a rate limit or account cooldown.
  </Card>

  <Card title="Retry-After header" icon="timer">
    `Retry-After` gives the seconds to wait before resending.
  </Card>
</CardGroup>

Without `Retry-After`, read `retryAfter` or `retryAfterMs` from the JSON body.
After another 429, back off and stop after 3 attempts.

## Best practices

Log status & error codes without credentials or private payloads.
Set request timeouts & poll long-running jobs.
Deleting missing resources returns `404`. Repeating deletion is safe.

<CardGroup cols={2}>
  <Card title="Rate limits" icon="gauge" href="/guides/rate-limits">
    Rate limit tiers and client-side limits.
  </Card>

  <Card title="API overview" icon="book" href="/api-reference/overview">
    Base URL, authentication, and conventions.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.