> ## 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.

# Tweet classification API with your own AI questions

> Classify X posts with your own AI questions: topics, labels, scores, or yes/no checks, with probabilities and totals. Costs 2 credits per analyzed post.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-classify-tweets-200">
      ```json theme={null}
      {
        "results": [
          {
            "tweet": {
              "id": "1893456789012345678",
              "text": "The new headphones sound great. The app keeps crashing.",
              "retweetCount": 4,
              "replyCount": 2,
              "likeCount": 31
            },
            "analysis": {
              "status": "succeeded",
              "answers": [
                {
                  "questionId": "sentiment",
                  "questionVersion": "sentiment:2",
                  "type": "choice",
                  "value": "mixed",
                  "confidence": 0.88
                }
              ],
              "questions": [
                {
                  "id": "sentiment",
                  "version": "sentiment:2"
                }
              ],
              "contextBytes": 612,
              "contextAvailability": {
                "quote": "not_applicable",
                "reply": "not_applicable",
                "author": "available",
                "media": "not_supplied",
                "article": "not_supplied"
              }
            },
            "answers": {
              "sentiment": "mixed",
              "intensity": 1,
              "sarcasm": 0.03
            },
            "sourceDomains": [],
            "cashtags": []
          }
        ],
        "unanalyzed": [
          {
            "id": "1893456789012345679",
            "status": "skipped",
            "reason": "post_unavailable"
          }
        ],
        "analysisSummary": {
          "schemaVersion": 1,
          "rows": {
            "analyzed": 1,
            "failed": 0,
            "skipped": 0
          },
          "engagement": 37,
          "questions": {
            "sentiment": {
              "type": "choice",
              "counts": {
                "positive": 0,
                "negative": 0,
                "mixed": 1,
                "neutral": 0,
                "unclear": 0
              },
              "shares": {
                "positive": 0,
                "negative": 0,
                "mixed": 1,
                "neutral": 0,
                "unclear": 0
              }
            }
          },
          "targets": []
        },
        "has_next_page": false,
        "next_cursor": ""
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-classify-tweets-400">
      ```json theme={null}
      {
        "error": "invalid_input",
        "message": "Invalid input. Check the request body."
      }
      ```
    </Tab>

    <Tab title="401" id="response-x-classify-tweets-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required."
      }
      ```
    </Tab>

    <Tab title="402" id="response-x-classify-tweets-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="403" id="response-x-classify-tweets-403">
      ```json theme={null}
      {
        "error": "x_account_protected",
        "message": "Account is protected. Choose a public account."
      }
      ```
    </Tab>

    <Tab title="424" id="response-x-classify-tweets-424">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-classify-tweets-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>

    <Tab title="502" id="response-x-classify-tweets-502">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-classify-tweets-503">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "Xquik is busy right now. Retry shortly."
      }
      ```
    </Tab>
  </Tabs>
</Panel>

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

<Callout icon="coins" color="#5c3327">
  **2 credits per analyzed post** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit · Supports [guest paid reads](/guides/guest-wallets)
</Callout>

Classify posts reads each post with AI and answers 1 to 8 questions you write.
The endpoint is `POST /api/v1/x/analysis/classify`.

| Question type | Answers | Use it for |
| - | - | - |
| `choice` | 1 category of 2 to 255 you name | Topics, intents, labels |
| `score` | 1 level of 2 or more, lowest first | Urgency, quality, fit |
| `probability` | From `0` to `1`, the chance the answer is yes | Yes/no checks, such as a refund request |

Without `analysis.questions`, it answers the
[sentiment questions](/api-reference/x/sentiment-analysis).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/x/analysis/classify \
    -H "x-api-key: xq_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "(\"can anyone recommend\" OR \"need help with\") (laptop OR phone) lang:en",
      "limit": 50,
      "analysis": {
        "questions": [
          {
            "id": "request",
            "type": "choice",
            "version": "1",
            "instructions": "What does the author ask for?",
            "categories": {
              "support": "Help with a problem.",
              "recommendation": "Advice on choosing a product.",
              "other": "Neither request."
            }
          },
          {
            "id": "urgency",
            "type": "score",
            "version": "1",
            "instructions": "How soon does the author need an answer?",
            "levels": ["No rush.", "Within days.", "Right now."]
          }
        ]
      }
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/x/analysis/classify", {
    method: "POST",
    headers: {
      "x-api-key": "xq_your_api_key_here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: '("can anyone recommend" OR "need help with") (laptop OR phone) lang:en',
      limit: 50,
      analysis: {
        questions: [
          {
            id: "request",
            type: "choice",
            version: "1",
            instructions: "What does the author ask for?",
            categories: {
              support: "Help with a problem.",
              recommendation: "Advice on choosing a product.",
              other: "Neither request.",
            },
          },
          {
            id: "urgency",
            type: "score",
            version: "1",
            instructions: "How soon does the author need an answer?",
            levels: ["No rush.", "Within days.", "Right now."],
          },
        ],
      },
    }),
  });
  const data = await response.json();
  if (!response.ok) throw new Error(`${data.error}: ${data.message}`);

  const leads = data.results
    .filter((result) => result.answers.request === "recommendation")
    .map((result) => ({ tweet_id: result.tweet.id, urgency: result.answers.urgency }));
  const requests = data.analysisSummary.questions.request.counts;

  process.stdout.write(`${JSON.stringify({ leads, requests })}\n`);
  ```

  ```python Python theme={null}
  import json
  import requests

  response = requests.post(
      "https://xquik.com/api/v1/x/analysis/classify",
      headers={"x-api-key": "xq_your_api_key_here"},
      json={
          "query": '("can anyone recommend" OR "need help with") (laptop OR phone) lang:en',
          "limit": 50,
          "analysis": {
              "questions": [
                  {
                      "id": "request",
                      "type": "choice",
                      "version": "1",
                      "instructions": "What does the author ask for?",
                      "categories": {
                          "support": "Help with a problem.",
                          "recommendation": "Advice on choosing a product.",
                          "other": "Neither request.",
                      },
                  },
                  {
                      "id": "urgency",
                      "type": "score",
                      "version": "1",
                      "instructions": "How soon does the author need an answer?",
                      "levels": ["No rush.", "Within days.", "Right now."],
                  },
              ],
          },
      },
      timeout=120,
  )
  data = response.json()
  if not response.ok:
      raise RuntimeError(f"{data['error']}: {data['message']}")

  leads = [
      {"tweet_id": result["tweet"]["id"], "urgency": result["answers"]["urgency"]}
      for result in data["results"]
      if result["answers"]["request"] == "recommendation"
  ]
  requests_by_type = data["analysisSummary"]["questions"]["request"]["counts"]
  print(json.dumps({"leads": leads, "requests": requests_by_type}))
  ```
</CodeGroup>

The snippets list posts that ask for a recommendation, with their urgency, and
print the count per request type. They do not print the full response.

## Write a question

Each question needs an `id`, a `type`, a `version`, and `instructions`. Write
the instructions as you would brief a careful reader.

* A `choice` adds `categories`: each name with a short description, or `null`.
  Add a category such as `other` or `unclear` for posts that fit none.
* A `score` adds `levels`: 2 or more descriptions, lowest first. The answer is
  the level's index, from `0`, and can have decimals, such as `1.4`.
* A `probability` can add `criteria`, with what counts as `yes` and as `no`.

Write question IDs and category names in lower case, with digits or
underscores, such as `feature_request`. A name with spaces or capitals returns
`400 invalid_input` with the name to fix.

```json theme={null}
{
  "id": "refund",
  "type": "probability",
  "version": "1",
  "instructions": "Does the author ask for their money back?",
  "criteria": { "yes": "Asks for a refund.", "no": "Asks for no refund." }
}
```

## Classify topics

A topic classifier is 1 `choice` question whose categories are your topics.
Add `other` for posts outside them. `analysisSummary.questions.<id>.shares`
holds each topic's share of posts.

```json theme={null}
{
  "id": "topic",
  "type": "choice",
  "version": "1",
  "instructions": "Which product area is the post about?",
  "categories": {
    "battery": "Charging or battery life.",
    "camera": "Photos or video.",
    "display": "The screen.",
    "other": "Another area or none."
  }
}
```

## Version your questions

`version` is yours. Change it whenever you change a question's instructions,
categories, or levels. Each answer carries `questionVersion`, so stored rows
show which wording produced them.

## Use a preset

Set `analysis.preset` instead of `questions` for a ready set, at the same
price.

| Preset | Answers | Needs targets |
| - | - | - |
| `sentiment` | `sentiment`, `intensity`, `sarcasm` | No |
| `brand` | `relevance`, `sentiment`, `experience` | Yes |
| `news` | `format`, `attribution`, `relevance` | Yes |
| `market` | `stance`, `content`, `conviction`, `relevance` | Yes |
| `complaints` | `complaint`, `issue`, `urgency` | Yes |
| `competitors` | `event`, `firsthand`, `commercial_relevance` | Yes |
| `purchase_intent` | `intent`, `fit`, `readiness` | Yes |
| `product_feedback` | `feedback`, `firsthand_use`, `impact` | Yes |

## Pick the posts to analyze

Send `tweetIds`, `texts`, or a search, never more than 1. Sending none, or more
than 1, returns `400 invalid_input`.

| Source | Send | Posts analyzed |
| - | - | - |
| Post IDs | `tweetIds`, up to 100 post IDs or post URLs | Each post once |
| Your own texts | `texts`, up to 100 strings | Each text. Reads nothing from X |
| A search | 1 or more of the search fields below | Up to `limit` posts, 20 by default |

A search combines every search field you send into 1 X search.

| Search field | Posts it finds |
| - | - |
| `query` | An X search, with the operators of [Search Tweets](/api-reference/x/search-tweets) |
| `username` | 1 account's posts. A handle, `@handle`, or profile URL |
| `listId` | A public List's posts. A List ID or URL |
| `quotesOf` | The quotes of 1 post. A post ID or URL |
| `repliesTo` | The replies in 1 post's conversation. A post ID or URL |
| `sinceTime` & `untilTime` | Posts between 2 times. An ISO 8601 time or Unix seconds |

`queryType` sets the order, `Latest` or `Top`. For more posts than 1 call
returns, send the same search again with `cursor` set to `next_cursor`. Stop
when `has_next_page` is `false`.

```json theme={null}
{
  "username": "NASA",
  "sinceTime": "2026-09-25T00:00:00Z",
  "untilTime": "2026-09-26T00:00:00Z"
}
```

## Compare with an earlier answer

Send the `results` of an earlier call as `baseline` to see what changed. Each
row in the new `results` gets `monitor`, and `analysisSummary.monitor` counts
the posts per status. The comparison costs nothing extra.

```json theme={null}
{
  "query": "Sony WH-1000XM6 lang:en",
  "baseline": [
    { "id": "1893456789012345678", "answers": { "sentiment": "positive", "intensity": 1.2 } }
  ]
}
```

A baseline row needs the post's `id`, or `tweet.id`, and its `answers`. Send
whole result rows when you have them. Their `monitor` carries the settings'
fingerprint, so rows from other settings show as `not_comparable`.

| `monitor.status` | Meaning |
| - | - |
| `first_run` | You sent no baseline |
| `new_to_baseline` | The baseline has no row for this post |
| `unchanged` | Every decision matches the baseline |
| `changed` | At least 1 decision clearly moved. `changes` lists each 1 |
| `not_comparable` | Other settings made the row, or it answers none of these questions |

A decision counts as changed only when the new answer clearly leaves the old
one, so near ties between calls stay `unchanged`. Keep `analysis` the same
between calls for comparable answers.

## Budget the analysis

Each analyzed post costs 2 credits. Each text in `texts` costs the same. Posts
in `unanalyzed` cost nothing.

When credits cover fewer posts than you asked for, fewer come back. The posts
left over appear in `unanalyzed` with `insufficient_credits`. If credits cover
no post, the call returns `402 insufficient_credits`.

| `unanalyzed[].reason` | Meaning | What to do |
| - | - | - |
| `post_unavailable` | X returns no such post | Skip the post |
| `missing_text` | The post has no text | Skip the post |
| `insufficient_credits` | Credits ran out | Add credits, then send it again |
| `context_limit` | The settings leave the post no room | Shorten the context or questions |
| Any other reason | The analysis failed (`status: "failed"`) | Send the post again |

## Body

<ParamField body="tweetIds" type="string[]">
  Up to 100 post IDs or post URLs, such as `x.com/nasa/status/20`. A post named
  twice is analyzed once. Send `tweetIds`, `texts`, or a search, not more than 1.
</ParamField>

<ParamField body="query" type="string">
  X search to analyze, with the operators of [Search Tweets](/api-reference/x/search-tweets).
</ParamField>

<ParamField body="username" type="string">
  Analyze 1 account's posts. Send a handle, with or without `@`, or a profile URL.
</ParamField>

<ParamField body="listId" type="string">
  Analyze a public List's posts. Send its ID or URL.
</ParamField>

<ParamField body="quotesOf" type="string">
  Analyze the quotes of 1 post. Send its ID or URL.
</ParamField>

<ParamField body="repliesTo" type="string">
  Analyze the replies in 1 post's conversation. Send its ID or URL.
</ParamField>

<ParamField body="sinceTime" type="string">
  Inclusive start of the search, such as `2026-09-25T00:00:00Z`. Unix seconds
  also work. A time without an offset is UTC.
</ParamField>

<ParamField body="untilTime" type="string">
  Exclusive end of the search. Unix seconds also work. A time without an offset
  is UTC.
</ParamField>

<ParamField body="queryType" type="string">
  Search order: `Latest` or `Top`. Defaults to `Latest`.
</ParamField>

<ParamField body="limit" type="integer">
  Maximum posts to analyze from the search. Range: `1-100`. Defaults to `20`.
  Credits can return fewer.
</ParamField>

<ParamField body="cursor" type="string">
  The `next_cursor` of the page before. Send the same search with it.
</ParamField>

<ParamField body="texts" type="string[]">
  Up to 100 texts of your own, such as drafts. Each must contain words. Reads
  nothing from X.
</ParamField>

<ParamField body="baseline" type="object[]">
  Rows of an earlier answer to compare with, up to 10,000, such as its
  `results`. Each needs the post's `id`, or `tweet.id`, and its `answers`. Each
  new result's `monitor` says whether the post is new, changed, or unchanged.
  The comparison costs nothing extra.
</ParamField>

<ParamField body="analysis" type="object">
  Optional settings. Leave it out to ask the route's own questions.

  <Expandable title="Analysis fields">
    <ParamField body="targets" type="object[]">
      Up to 100 brands, people, assets, or topics the questions are about. Each
      has a `name` and optional `aliases`, such as a ticker or product name.
    </ParamField>

    <ParamField body="context" type="string">
      Background the AI reads with every post, such as your product or market.
    </ParamField>

    <ParamField body="questions" type="object[]">
      1 to 8 questions of your own, which replace the route's questions. Each
      needs an `id`, a `type` of `choice`, `score`, or `probability`, a
      `version`, and `instructions`. A choice adds `categories`, a score adds
      `levels`, and a probability can add `criteria`.
    </ParamField>

    <ParamField body="preset" type="string">
      A ready set of questions, used when `questions` is left out: `brand`,
      `complaints`, `competitors`, `purchase_intent`, `product_feedback`,
      `news`, `sentiment`, or `market`. `complaints`, `competitors`,
      `purchase_intent`, and `product_feedback` need `targets`.
    </ParamField>

    <ParamField body="maxContextBytes" type="integer">
      Bytes of post, quote, and settings the AI reads. Range: `1-64000`.
      Defaults to `64000`. Xquik cuts a longer post to fit.
    </ParamField>

    <ParamField body="concurrency" type="integer">
      Posts analyzed at the same time. Range: `1-16`. Defaults to `16`.
    </ParamField>
  </Expandable>
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Full account API key. An OAuth bearer token also works.
</ParamField>

<ParamField header="Authorization" type="string">
  Send `Bearer xq_your_guest_key_here` for an active `paid_reads` guest key.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`. The body can be up to 256 KB.
</ParamField>

## Response

### 200 OK

<ResponseField name="results" type="object[]">
  Each analyzed post with its answers. Each row costs 2 credits.

  <Expandable title="Result fields">
    <ResponseField name="tweet" type="object">
      The post, as [Search Tweets](/api-reference/x/search-tweets) returns it. Your own texts have the IDs `text:1`, `text:2`, and so on.
    </ResponseField>

    <ResponseField name="answers" type="object">
      Each decision by question ID. A choice gives its category, a score its level from `0`, and a probability its value from `0` to `1`.
    </ResponseField>

    <ResponseField name="analysis" type="object">
      The answers in full. `status` is `succeeded`. `answers` gives each answer's `value`, `confidence`, and `probabilities`, or a `probability`. `questions` names the ID and version of each question. `contextBytes` and `contextAvailability` say what the AI read beside the post's text.
    </ResponseField>

    <ResponseField name="sourceDomains" type="string[]">
      Hostnames the post links to, without `t.co`.
    </ResponseField>

    <ResponseField name="cashtags" type="string[]">
      Cashtags in the post's text, in upper case.
    </ResponseField>

    <ResponseField name="monitor" type="object">
      The comparison with `baseline`: `status`, `changedQuestionIds`, `changes` with each `previous` and `current` decision, and the settings' `configurationFingerprint`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="unanalyzed" type="object[]">
  Each post without an analysis. These cost nothing.

  <Expandable title="Unanalyzed fields">
    <ResponseField name="id" type="string">
      Post ID, or `text:1` and so on for your own texts.
    </ResponseField>

    <ResponseField name="status" type="string">
      `skipped` or `failed`.
    </ResponseField>

    <ResponseField name="reason" type="string">
      `post_unavailable`, `missing_text`, `insufficient_credits`, or `context_limit`. Any other reason means the analysis failed. Send the post again.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="analysisSummary" type="object">
  Totals of the answers across the analyzed posts.

  <Expandable title="Summary fields">
    <ResponseField name="schemaVersion" type="integer">
      Version of the summary's shape.
    </ResponseField>

    <ResponseField name="rows" type="object">
      Posts by outcome: `analyzed`, `failed`, and `skipped`.
    </ResponseField>

    <ResponseField name="engagement" type="integer">
      Likes, reposts, replies, and quotes of the analyzed posts.
    </ResponseField>

    <ResponseField name="questions" type="object">
      Totals by question ID. A choice has `counts`, `shares`, `engagementShares`, and `top` posts per category. A score has `mean`, `engagementWeightedMean`, and `levels`. A probability has `mean`, `yes`, and `no`.
    </ResponseField>

    <ResponseField name="targets" type="object[]">
      Per target in `analysis.targets`: `name`, `mentions`, `share`, `engagement`, `choices`, and `top` posts.
    </ResponseField>

    <ResponseField name="cashtags" type="object[]">
      Posts and category counts per cashtag, most posts first, up to 20.
    </ResponseField>

    <ResponseField name="sourceDomains" type="object[]">
      Posts per linked hostname, most posts first, up to 20.
    </ResponseField>

    <ResponseField name="monitor" type="object">
      Posts per `monitor.status` in `statuses`, and up to 50 changed posts in `changedRows`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="has_next_page" type="boolean">Whether the search has more posts. Always `false` for `tweetIds` and `texts`.</ResponseField>
<ResponseField name="next_cursor" type="string">Send it as `cursor` with the same search for the next page. Empty when there is none.</ResponseField>

```json theme={null}
{
  "results": [
    {
      "tweet": {
        "id": "1893456789012345678",
        "text": "Need help with my laptop. It shuts down at 30% battery and I have a deadline tonight.",
        "likeCount": 3,
        "retweetCount": 0,
        "replyCount": 5
      },
      "analysis": {
        "status": "succeeded",
        "answers": [
          {
            "questionId": "request",
            "questionVersion": "1",
            "type": "choice",
            "value": "support",
            "confidence": 0.96,
            "probabilities": { "support": 0.96, "recommendation": 0.03, "other": 0.01 }
          },
          {
            "questionId": "urgency",
            "questionVersion": "1",
            "type": "score",
            "value": 2,
            "confidence": 0.84,
            "probabilities": { "0": 0.02, "1": 0.14, "2": 0.84 },
            "levels": ["No rush.", "Within days.", "Right now."]
          }
        ]
      },
      "answers": { "request": "support", "urgency": 2 },
      "sourceDomains": [],
      "cashtags": []
    }
  ],
  "unanalyzed": [],
  "analysisSummary": {
    "schemaVersion": 1,
    "rows": { "analyzed": 1, "failed": 0, "skipped": 0 },
    "engagement": 8,
    "questions": {
      "request": {
        "type": "choice",
        "counts": { "support": 1, "recommendation": 0, "other": 0 },
        "shares": { "support": 1, "recommendation": 0, "other": 0 }
      },
      "urgency": { "type": "score", "mean": 2 }
    },
    "targets": [],
    "cashtags": [],
    "sourceDomains": []
  },
  "has_next_page": false,
  "next_cursor": ""
}
```

### 400 Invalid input

```json theme={null}
{
  "error": "invalid_input",
  "message": "Rename \"Feature Request\". Write question IDs & category names in lower case without hyphens, such as feature_request."
}
```

The body names no posts, names more than 1 source, or has an invalid field.
The message says what to send instead. The request costs nothing.

### 401 Unauthenticated

Anonymous requests get `WWW-Authenticate: Bearer` and a guest wallet checkout action. This is not a Payment challenge.

```json theme={null}
{ "error": "unauthenticated" }
```

Missing or invalid API key. Check the `x-api-key` header value.

### 402 Payment required

Full account keys can receive `no_subscription`, `subscription_inactive`, `no_credits`, or `insufficient_credits` with account payment options. Guest keys receive only the guest top-up action.

The failed request creates no checkout. Ask the user to confirm before calling any checkout or top-up route.

### 403 Protected account

```json theme={null}
{ "error": "x_account_protected" }
```

A search that needs a protected author returns this. Choose a public account.
The request costs nothing.

### 502 X API unavailable

```json theme={null}
{ "error": "x_api_unavailable" }
```

The read service returned an error. Retry after a short delay.

### 503 Service busy

```json theme={null}
{ "error": "x_api_unavailable" }
```

Xquik is busy. Wait for the `Retry-After` header, then send the request again.
The request costs nothing.

### 429 Rate limit exceeded

```json theme={null}
{ "error": "rate_limit_exceeded", "retryAfter": 60 }
```

You exceeded your tier rate limit. Wait for the `Retry-After` header before retrying.

### 424 Dependency failed

```json theme={null}
{ "error": "x_api_unavailable" }
```

The normalized v1 response contract can return 424 when the read service is unavailable.

## Tweet classification API questions

### How do I classify tweets by topic?

Send 1 `choice` question whose categories are your topics, with `other` for the
rest. Each row's `answers` holds the topic, and the summary counts posts per
topic.

### How many questions and categories can I use?

Up to 8 questions per call. A choice takes 2 to 255 categories, and a score 2 or
more levels. The price stays 2 credits per post.

### Can I set my own confidence threshold?

Yes. `results[].analysis.answers` gives each answer's `confidence` and the
probability of every category or level. Keep the rows above your threshold.

### Can I classify my own texts, such as support tickets?

Yes. Send up to 100 texts in `texts`. Xquik reads nothing from X for them.

### How much does classification cost?

Each analyzed post costs 2 credits, whatever the number of questions. Posts in
`unanalyzed` and refused requests cost nothing.

### Does this replace X's official API?

No. This page documents Xquik, an independent third-party service.
It does not document X's official API.

<Note>
  **Next steps.** [Brand mentions](/api-reference/x/brand-monitoring) answers ready brand questions, or [Viral score](/api-reference/x/viral-score) rates how posts read.
</Note>

<div className="related-api-links">
  <Accordion title="Related tweet, reply & media APIs" icon="link">
    * Tweets: [Get tweet](/api-reference/x/get-tweet) · [Batch tweets](/api-reference/x/batch-tweets) · [Tweet thread](/api-reference/x/tweet-thread) · [Hidden replies](/api-reference/x/tweet-hidden-replies) · [Translate tweet](/api-reference/x/tweet-translation) · [Embed tweet](/api-reference/x/tweet-embed) · [Resolve links](/api-reference/x/resolve-links) · [Tweet subtitles](/api-reference/x/tweet-subtitles) · [X Article](/api-reference/x/get-article)
    * Analysis: [Sentiment analysis](/api-reference/x/sentiment-analysis) · [Brand mentions](/api-reference/x/brand-monitoring) · [News classification](/api-reference/x/news-classification) · [Market signals](/api-reference/x/market-signals) · [Viral score](/api-reference/x/viral-score) · [Classify posts](/api-reference/x/classify-tweets)
    * Engagement: [Tweet replies](/api-reference/x/tweet-replies) · [Quote tweets](/api-reference/x/tweet-quotes) · [Likers](/api-reference/x/favoriters) · [Reposters](/api-reference/x/retweeters) · [Check repost](/api-reference/x/tweet-repost-check)
    * Profiles: [User tweets](/api-reference/x/user-tweets) · [Batch user tweets](/api-reference/x/batch-user-tweets) · [User replies](/api-reference/x/user-replies) · [User likes](/api-reference/x/user-likes) · [User media](/api-reference/x/user-media) · [User highlights](/api-reference/x/user-highlights) · [User articles](/api-reference/x/user-articles)
    * Feeds: [List tweets](/api-reference/x/list-tweets) · [Trends](/api-reference/x/trends) · [Trend locations](/api-reference/x/trend-locations) · [Search Spaces](/api-reference/x/search-spaces) · [Get Space](/api-reference/x/get-space) · [Space replay](/api-reference/x/space-replay) · [Get broadcast](/api-reference/x/get-broadcast) · [Hashflags](/api-reference/x/hashflags) · [Search places](/api-reference/x/search-places) · [Download media](/api-reference/x/download-media)
  </Accordion>
</div>


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