Skip to main content
Every Xquik API error returns a consistent JSON body with an error code. Use these codes to build reliable integrations with automatic 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

CodeHTTPRetryableQuick fix
invalid_input400NoFix request body. Check required fields and types.
invalid_json400NoSend valid JSON in the request body.
invalid_id400NoUse a numeric string for the ID path parameter.
invalid_tweet_url400NoUse full URL format: https://x.com/user/status/ID.
invalid_tweet_id400NoProvide a valid numeric tweet ID.
invalid_username400NoProvide a valid X username without @.
invalid_tool_type400NoUse a valid extraction tool type.
invalid_format400NoUse csv, json, md, md-document, pdf, txt, or xlsx.
invalid_params400NoCheck format and type values.
missing_query400NoInclude the required q parameter.
missing_params400NoCheck endpoint docs for required parameters.
webhook_inactive400NoReactivate the webhook before testing.
unauthenticated401NoCheck x-api-key header. Regenerate if revoked.
account_needs_reauth401NoRe-authenticate X account from the dashboard.
x_auth_failure401NoRe-authenticate X account from the dashboard.
no_subscription402NoSubscribe from the dashboard.
subscription_inactive402NoReactivate subscription from the dashboard.
usage_limit_reached402NoWait for next billing period or enable extra usage.
no_addon402NoAdd a monitor addon from the dashboard.
extra_usage_disabled402NoEnable extra usage from the subscription page.
extra_usage_requires_v2402NoContact support to migrate pricing plan.
manual_subscription402NoExtra usage requires Stripe-managed subscription.
frozen402NoUpdate payment method from the dashboard.
overage_limit_reached402NoWait for current overage invoice to process.
payment_failed402NoUpdate payment method from the dashboard.
no_credits402NoPurchase credits or subscribe.
insufficient_credits402NoPurchase more credits or wait for next billing period.
api_key_limit_reached403NoDelete an existing key before creating a new one.
monitor_limit_reached403NoDelete a monitor or add capacity ($5/month).
not_found404NoVerify the resource ID.
user_not_found404NoCheck the username. Account may be suspended.
tweet_not_found404NoCheck the tweet ID. Tweet may be deleted.
no_media404NoTweet has no media attachments.
article_not_found404NoTweet has no linked article.
draft_not_found404NoVerify the draft ID.
style_not_found404NoAnalyze the user’s style first via POST /api/v1/styles.
no_cached_style404NoAnalyze the user’s style first via POST /api/v1/styles.
monitor_already_exists409NoUse the existing monitor.
shadow_account422NoAccount may be restricted on X.
x_account_feature_required422NoUpgrade to X Premium on the target account.
x_account_suspended422NoCheck account status on X.
x_account_protected422NoRequest to follow the protected account first.
x_duplicate_action422NoAction already completed. No retry needed.
x_target_not_found422NoVerify target ID or username.
x_content_too_long422NoShorten tweet to 280 characters or fewer.
x_rejected422NoCheck request parameters. Contact support if persistent.
x_write_unconfirmed202NoVerify outcome on X before retrying.
rate_limit_exceeded429YesRetry with exponential backoff. Respect Retry-After.
x_api_rate_limited502YesRetry in a few minutes.
x_rate_limited429YesWait a few minutes and retry.
x_daily_limit429NoWait 24 hours or use a different X account.
internal_error500YesRetry with backoff. Contact support if persistent.
x_api_unavailable502YesRetry with backoff.
x_api_unauthorized502YesRetry later. Contact support if persistent.
stream_registration_failed500YesRetry the request.
x_write_failed500YesRetry the request. Contact support if persistent.
x_write_ambiguous500NoVerify the action result manually.
x_transient_error503YesRetry with backoff.
media_download_failed500YesRetry the request.

Error codes (detailed)

Response format:
{ "error": "error_code", "message": "Human-readable description" }
Some errors include additional context fields (e.g. limit, retryAfter).
Request body or parameters failed validation. Fix the request before retrying.
CodeDescriptionRecommended Action
invalid_inputRequest body failed validationFix the request body. Check required fields and types.
invalid_jsonRequest body contains invalid JSONFix the request body to contain valid JSON.
invalid_idPath parameter is not a valid IDFix the path parameter. IDs are numeric strings.
invalid_tweet_urlTweet URL format is invalidUse the full URL format: https://x.com/user/status/ID.
invalid_tweet_idTweet ID is empty or invalidProvide a valid numeric tweet ID in the path.
invalid_usernameX username is empty or invalidProvide a valid X username without the @ prefix.
invalid_tool_typeExtraction tool type not recognizedUse one of the 23 valid tool types. See Create Extraction.
invalid_formatExport format is not supportedUse csv, json, md, md-document, pdf, txt, or xlsx.
invalid_paramsExport query parameters are invalidCheck the format and type values.
missing_queryRequired query parameter is missingInclude the required q parameter.
missing_paramsRequired query parameters are missingCheck the endpoint docs for required parameters.
webhook_inactiveWebhook is disabledReactivate via Update Webhook before testing.
Missing or invalid credentials. Check your API key or session.
CodeDescriptionRecommended Action
unauthenticatedMissing or invalid API keyCheck the x-api-key header. Regenerate the key if revoked.
account_needs_reauthConnected X account needs re-authenticationRe-authenticate the X account from the dashboard.
x_auth_failureX account session expired or invalidRe-authenticate the X account from the dashboard.
Subscription or payment issue. See Billing & Usage for plan details.
CodeDescriptionRecommended Action
no_subscriptionNo active subscriptionSubscribe from the dashboard.
subscription_inactiveSubscription is not activeReactivate from the dashboard.
usage_limit_reachedMonthly usage limit exceededWait for the next billing period or check balance via Get Account.
no_addonNo monitor addon on subscriptionAdd a monitor addon from the dashboard.
extra_usage_disabledExtra usage not enabledEnable extra usage from the subscription page.
extra_usage_requires_v2Extra usage requires updated pricing planContact support to migrate to the current pricing version.
manual_subscriptionExtra usage unavailable for this subscription typeExtra usage is only available for Stripe-managed subscriptions.
frozenExtra usage paused, outstanding payment requiredUpdate payment method from the dashboard.
overage_limit_reachedOverage spending limit reachedWait for the current overage invoice to be processed. See Spending Limits.
payment_failedPayment processing failedUpdate payment method from the dashboard.
no_creditsNo credits availablePurchase credits or subscribe from the dashboard.
insufficient_creditsNot enough credits for this operationCheck balance via Get Account. Purchase more credits or wait for next billing period.
Action not allowed under current plan or limits.
CodeDescriptionRecommended Action
api_key_limit_reachedAPI key limit reached (100 max)Delete an existing key before creating a new one.
monitor_limit_reachedPlan monitor limit exceededDelete an existing monitor or add capacity ($5/month per extra monitor).
The requested resource does not exist or has been deleted.
CodeDescriptionRecommended Action
not_foundResource does not existVerify the resource ID. It may belong to another account or have been deleted.
user_not_foundX user not foundCheck the username. The account may not exist or may be suspended.
tweet_not_foundTweet not foundCheck the tweet ID. The tweet may have been deleted or the ID is invalid.
no_mediaTweet has no downloadable mediaThe tweet does not contain any media attachments.
article_not_foundTweet has no linked articleThe tweet does not contain a linked article.
draft_not_foundDraft does not existVerify the draft ID. It may have been deleted.
style_not_foundNo cached writing styleAnalyze the user’s style first via POST /api/v1/styles.
no_cached_styleNo cached writing style for username lookupAnalyze the user’s style first via POST /api/v1/styles.
Resource already exists or action outcome is unclear.
CodeDescriptionRecommended Action
monitor_already_existsDuplicate monitor for this X accountUse the existing monitor. Call Update Monitor to change event types.
CodeDescriptionRecommended Action
shadow_accountAccount is shadow-bannedThe X account may be restricted. Check account status on X.
x_account_feature_requiredX Premium required for this actionUpgrade to X Premium on the target account.
x_account_suspendedX account suspended or restrictedCheck the account status on X.
x_account_protectedTarget account is privateThe target account has a protected profile. Request to follow them first.
x_duplicate_actionAction already performed (liked, retweeted, etc.)No action needed. The operation was already completed.
x_target_not_foundTweet or user target does not existVerify the target ID or username. The resource may have been deleted.
x_content_too_longTweet exceeds character limitShorten the tweet text to 280 characters or fewer.
x_rejectedX rejected the write with an unknown reasonCheck the request parameters and retry. Contact support if persistent.
The write action may have completed but confirmation could not be obtained. Do not retry without verifying the outcome first.
CodeDescriptionRecommended Action
x_write_unconfirmedAction may have been completed but could not be confirmedCheck the X account timeline or profile to verify before retrying. Do not retry without checking first to avoid duplicates. Not retryable.
Request rate exceeded. See Rate Limits for tier details.
CodeDescriptionRecommended Action
rate_limit_exceededAPI rate limitedRetry with exponential backoff. Respect the Retry-After header. Response includes retryAfter (seconds).
x_api_rate_limitedX data source rate limitedRetry in a few minutes. The data source is experiencing high traffic.
x_rate_limitedX rate-limited the write requestWait a few minutes and retry. The platform is throttling write operations.
x_daily_limitX account reached daily posting limitWait 24 hours before retrying with this account. Use a different X account if available. Not retryable.
Transient failures. Retry with exponential backoff (max 3 attempts).
CodeDescriptionRecommended Action
internal_errorServer errorRetry with backoff. Contact support if persistent.
x_api_unavailableX data source temporarily unavailableRetry with backoff.
x_api_unauthorizedX data source authentication failedRetry later. Contact support if persistent.
stream_registration_failedStream registration failedRetry the request.
x_write_failedWrite action failed unexpectedlyRetry the request. Contact support if persistent.
x_write_ambiguousWrite action completion could not be confirmedVerify the action result manually. The write may have succeeded.
x_transient_errorUpstream timeout or temporary failureRetry with backoff. The platform is experiencing intermittent issues.
media_download_failedMedia download from source failedRetry the request.

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:
SignalValue
HTTP status429
Retry-After headerSeconds until the limit resets (e.g. 60)
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.