Skip to main content

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.

Every error includes an error code. Use it to choose recovery.

Getting 400?

Fix your request body. See validation errors.

Getting 402?

Check your balance or subscription. See billing errors or billing guide.

Getting 429?

Implement backoff. See retry strategy or rate limits.

Quick reference

Start with the HTTP family, then apply the recovery rule. Retry only when a card says the error is retryable.

400 request validation

Retry: no. Fix the body, query, or path. Covers invalid_input, invalid_json, invalid_id, invalid_tweet_url, invalid_tweet_id, invalid_username, invalid_user_id, invalid_tool_type, invalid_format, invalid_params, missing_query, missing_ids, missing_params, too_many_ids, webhook_inactive, and unsupported_field.

401 authentication

Retry: no. Check x-api-key, regenerate revoked keys, or re-authenticate the connected X account. Covers unauthenticated and x_auth_failure.

402 billing and credits

Retry: no. Subscribe, reactivate billing, update payment, or top up credits. Covers no_subscription, subscription_inactive, no_addon, payment_failed, no_credits, and insufficient_credits.

403 permissions and account health

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, monitor_limit_reached, dm_not_permitted, account_needs_reauth, and account_restricted.

404 missing resource

Retry: no. Verify the resource ID, connected account, username, tweet ID, media, article, draft, or cached style. Covers not_found, account_not_found, user_not_found, tweet_not_found, no_media, article_not_found, draft_not_found, style_not_found, and no_cached_style.

409 duplicate monitor

Retry: no. Use the existing monitor or update it. Covers monitor_already_exists.

422 write validation

Retry: no. Fix the account capability, target, content, DM permissions, or media URL before sending again. Covers x_account_feature_required, x_account_suspended, x_account_protected, x_duplicate_action, x_dm_not_allowed, x_target_not_found, x_content_too_long, x_rejected, and media_download_failed.

202 pending confirmation

Retry: no. Store writeActionId, poll Get Write Action Status, and do not retry-send while status is pending_confirmation. Covers x_write_unconfirmed.

429 rate limit or cooldown

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.

500, 502, and 503 transient failures

Retry: yes for internal_error, x_api_rate_limited, x_api_unavailable, x_api_unauthorized, x_write_failed, and x_transient_error. Stop after 3 attempts. For x_write_ambiguous, verify the result manually before sending anything again.

Error codes (detailed)

Response format:
{ "error": "error_code", "message": "Human-readable description" }
Some errors include additional context fields (e.g. limit, retryAfter).
Request body, query, or path validation failed. Fix the request shape before retrying.

invalid_input

Request body failed validation. Check required fields, types, and enum values against the endpoint docs.

invalid_json

Request body is not valid JSON. Rebuild the body and send a parseable JSON object.

invalid_id

Path ID is not valid. Use the numeric string returned by the create or list endpoint.

invalid_tweet_url

Tweet URL is malformed. Use the full format https://x.com/user/status/ID.

invalid_tweet_id

Tweet ID is empty or malformed. Extract the final numeric status ID before calling tweet, media, or article endpoints.

invalid_username

Username or user path value is empty or invalid. Send a username without the @ prefix, or send a numeric user ID where supported.

invalid_user_id

User lookup input is invalid or does not resolve. Check the username or numeric user ID before retrying.

invalid_tool_type

Extraction tool type is not recognized. Use one of the 23 valid tool types from Create Extraction.

invalid_format

Export format is unsupported. Use csv, json, md, md-document, pdf, txt, or xlsx.

invalid_params

Export query parameters are invalid. Check the format and type values for that export endpoint.

missing_query

Required search query is missing. Add the q parameter before calling search or community endpoints.

missing_ids

Required multi-ID query is missing. Provide comma-separated numeric IDs in the ids parameter.

missing_params

Required query parameters are missing. Check the endpoint docs; follower checks require both source and target.

too_many_ids

Too many IDs were requested at once. Split requests into groups of 100 IDs or fewer.

webhook_inactive

Webhook is disabled. Reactivate it with Update Webhook before testing.

unsupported_field

Request body contains a field this endpoint does not accept. For tweet posts, send public media URLs in media, not uploaded media IDs.
Missing or invalid credentials. Check your API key or session.

unauthenticated

API key or bearer token is missing or invalid. Send x-api-key or regenerate a revoked key.

x_auth_failure

Connected X account session expired or was invalidated. Re-authenticate the account from the dashboard.
Subscription or payment issue. See Billing & Usage for plan details.

no_subscription

No active plan. Start billing from the dashboard or call Subscribe.

subscription_inactive

Subscription is inactive. Reactivate billing from the dashboard.

no_addon

Legacy monitor add-on state. Check billing status; current plans include unlimited monitor slots.

payment_failed

Payment processing failed. Update the payment method from the dashboard.

no_credits

No credit balance is available. Check balance with Get Account, then top up or subscribe.

insufficient_credits

Balance is below the required operation cost. Check Get Account, then Top Up Credits.
Action not allowed under current plan or limits.

api_key_limit_reached

The account already has 100 active API keys. Revoke an active key before creating another.

monitor_limit_reached

Legacy monitor slot limit. Check billing status; current plans include unlimited monitor slots.

dm_not_permitted

DM history requires a connected account that participates in the conversation. Use a participating account or reconnect it from the dashboard.

account_needs_reauth

Connected X account session needs re-authentication. Reconnect the account from the dashboard, then retry.

account_restricted

Connected X account is locked, suspended, recovering, or temporarily blocked. Resolve account health on x.com or wait before retrying.
The requested resource does not exist, is unavailable to this API key, or is not the expected X object type.

not_found

Generic resource lookup failed. Verify the ID belongs to your account and has not been deleted.

account_not_found

Connected X account was not found for this user. Call List X Accounts and use a valid connected account.

user_not_found

X username or numeric user ID does not resolve. Confirm the username with the user or try a different handle.

tweet_not_found

Tweet ID does not resolve. Check the numeric tweet ID; the tweet may have been deleted.

no_media

Tweet exists but has no downloadable media attachments. Use a tweet that contains media.

article_not_found

Tweet ID is valid but is not an X Article. Ask for an X Article URL or use a normal tweet or thread endpoint.

draft_not_found

Draft ID does not exist. Verify the draft ID or create a new draft.

style_not_found

Writing style ID was not found. Analyze tweets first with Analyze Style.

no_cached_style

No cached writing style exists for username lookup. Analyze tweets first with Analyze Style.
A monitor with the same target already exists. Treat the response as a handoff to the existing monitor instead of retrying creation.

monitor_already_exists

Duplicate account or keyword monitor. List existing monitors, reuse the monitor ID, or update event types with Update Monitor or Update Keyword Monitor.
Write validation failed. Change the account, target, content, DM permission, or media input before retrying.

x_account_feature_required

Account capability is missing. Use an account with the required capability or adjust the request.

x_account_suspended

Connected X account is suspended or restricted. Resolve account status on x.com before sending more writes from it.

x_account_protected

Target account is protected. Request access first or choose a target account that the connected account can interact with.

x_duplicate_action

Operation is already complete. Do not retry unchanged; check the target state before sending anything again.

x_dm_not_allowed

Recipient does not accept DMs from this account. Use a permitted connected account or ask the recipient to allow messages.

x_target_not_found

Tweet or user target does not exist. Verify the ID or username before sending another request.

x_content_too_long

Content exceeds the character limit. Shorten the text or use an account that supports the requested content length.

x_rejected

X rejected the write without a specific reason. Change the request, wait 2-3 minutes after rapid attempts, then retry changed content.

media_download_failed

Public media URL could not be downloaded. Fix the HTTPS URL or pass the file via multipart/form-data. Do not retry the same URL.
The write action was dispatched, but final confirmation is still pending.

x_write_unconfirmed

Action may have completed but could not be confirmed. The response includes status: "pending_confirmation", writeActionId, charged: false, and retryable: false.

Recovery

Store writeActionId, call Get Write Action Status, and do not retry-send while status is pending_confirmation.
Request rate exceeded. See Rate Limits for tier details.

rate_limit_exceeded

Xquik tier or action limit was reached. Wait the Retry-After seconds from the response; JSON also includes retryAfter when available.

login_cooldown

A recent login attempt triggered cooldown. Wait retryAfterMs or the Retry-After header before reconnecting or reauthenticating.

x_rate_limited

X throttled the write. Wait 2-3 minutes, avoid rapid consecutive requests to the same account, then retry with backoff.

x_daily_limit

Connected X account reached its daily posting limit. Wait 24 hours before retrying that account, or use another connected account.
Transient failures. Retry with exponential backoff (max 3 attempts).

internal_error

Server error. Retry with backoff and contact support if it persists.

x_api_rate_limited

Read service rate limited. Retry in a few minutes.

x_api_unavailable

Read service temporarily unavailable. Retry with backoff.

x_api_unauthorized

Read service authentication failed. Retry later and contact support if it persists.

x_write_failed

Write action failed unexpectedly. Retry the request and contact support if it persists.

x_write_ambiguous

Write action completion could not be confirmed. Verify the result manually before sending anything again.

x_transient_error

Write service timeout or temporary failure. Retry with backoff.

Retry with exponential backoff

Retry only on 429 and 5xx responses. Use exponential backoff with random jitter to avoid thundering herds. Max 3 retries. Formula: delay = baseDelay * 2^attempt + random(0, jitter)
#!/bin/bash
# Retry wrapper for cURL requests
retry_request() {
  local url="$1"
  local max_retries=3
  local base_delay=1

  for attempt in $(seq 0 $max_retries); do
    response=$(curl -s -w "\n%{http_code}" "$url" \
      -H "x-api-key: xq_YOUR_KEY_HERE")

    status=$(echo "$response" | tail -1)
    body=$(echo "$response" | head -n -1)

    if [ "$status" -lt 429 ]; then
      echo "$body"
      return 0
    fi

    if [ "$status" -eq 429 ] || [ "$status" -ge 500 ]; then
      if [ "$attempt" -eq "$max_retries" ]; then
        echo "Max retries reached. Last status: $status" >&2
        return 1
      fi

      delay=$(echo "$base_delay * (2 ^ $attempt) + $RANDOM % 1000 / 1000" | bc -l)
      echo "Retry $((attempt + 1)) in ${delay}s (status $status)" >&2
      sleep "$delay"
    else
      echo "$body"
      return 1
    fi
  done
}

retry_request "https://xquik.com/api/v1/events?limit=10"

Rate limit handling

When you exceed the rate limit, Xquik returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait. Detecting rate limits:

HTTP status

429 Too Many Requests means the request is rate limited or waiting on an account cooldown.

Retry-After header

The Retry-After header gives seconds to wait before sending the same request again.
Recovery flow:
1

Detect the 429 status

Check the HTTP response status code before reading the body.
2

Read the Retry-After header

Parse the header value as an integer (seconds).
3

Wait the specified duration

Sleep for the full Retry-After duration before retrying.
4

Retry the request

Send the same request again. If it returns another 429, apply exponential backoff.
async function respectRateLimit(url, options = {}) {
  const response = await fetch(url, options);

  if (response.status === 429) {
    const retryAfter = parseInt(
      response.headers.get("Retry-After") || "1",
      10
    );
    console.log(`Rate limited. Waiting ${retryAfter}s...`);
    await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
    return fetch(url, options); // retry once
  }

  return response;
}

Extracting retryAfter from the response body

Rate limit responses include a retryAfter field in the JSON body (seconds until the limit resets). This is equivalent to the Retry-After header but accessible without parsing headers.
const response = await fetch("https://xquik.com/api/v1/events", {
  headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
});

if (response.status === 429) {
  const body = await response.json();
  const waitSeconds = body.retryAfter ?? 60;
  console.log(`Rate limited. Retry in ${waitSeconds}s (from response body)`);
  await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
}

Best practices

4xx errors (except 429) indicate a problem with your request. Fix the request before retrying. 5xx errors and 429 are transient. Retry with backoff.
Always log the full error response including status code and error body. This makes it easy to diagnose issues in production.
monitor_already_exists is not a failure. The monitor you need already exists. List monitors to find it instead of treating this as an error.
Set a 30-second timeout on all API requests. If a request hangs, retry it rather than waiting indefinitely.
Creating a monitor for the same username returns 409. Deleting a non-existent resource returns 404. Both are safe to retry.

Rate Limits

Detailed rate limit tiers and client-side rate limiting.

API Overview

Base URL, authentication, and API conventions.
Last modified on May 17, 2026