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

# Twitter scraper API & bulk extraction jobs

> Start an export job for tweets, followers, following, replies, profiles, timelines, media, communities, or lists with one of 23 tools. 1 credit per result.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-extractions-create-200">
      ```json theme={null}
      {
        "allowed": true,
        "creditsAvailable": "50000",
        "creditsRequired": "500",
        "estimatedResults": 500,
        "source": "replyCount"
      }
      ```
    </Tab>

    <Tab title="202" id="response-extractions-create-202">
      ```json theme={null}
      {
        "id": "ext_123",
        "pollAfterMs": 1000,
        "statusUrl": "/api/v1/extractions/a1b2c3d4",
        "waitUrl": "/api/v1/extractions/id?wait=10&limit=1&outputMode=compact",
        "toolType": "follower_explorer",
        "status": "running"
      }
      ```
    </Tab>

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

    <Tab title="401" id="response-extractions-create-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required. Provide a valid API key or bearer token."
      }
      ```
    </Tab>

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

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

    <Tab title="404" id="response-extractions-create-404">
      ```json theme={null}
      {
        "error": "not_found",
        "message": "Resource not found."
      }
      ```
    </Tab>

    <Tab title="409" id="response-extractions-create-409">
      ```json theme={null}
      {
        "error": "idempotency_conflict",
        "message": "Idempotency-Key belongs to another request. Use a new key."
      }
      ```
    </Tab>

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

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

    <Tab title="502" id="response-extractions-create-502">
      ```json theme={null}
      {
        "error": "x_api_unavailable",
        "message": "X data source temporarily unavailable. Try again later."
      }
      ```
    </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">
  **1 credit per result extracted** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

## Query parameters

<ParamField query="dry_run" type="boolean">
  Return a cost estimate without creating a job. Defaults to `false`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/extractions \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "toolType": "reply_extractor",
      "targetTweetId": "1893704267862470862",
      "resultsLimit": 500
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/extractions", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Idempotency-Key": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      toolType: "reply_extractor",
      targetTweetId: "1893704267862470862",
      resultsLimit: 500,
    }),
  });
  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests
  from uuid import uuid4

  response = requests.post(
      "https://xquik.com/api/v1/extractions",
      headers={"x-api-key": "xq_YOUR_KEY_HERE", "Idempotency-Key": str(uuid4())},
      json={
          "toolType": "reply_extractor",
          "targetTweetId": "1893704267862470862",
          "resultsLimit": 500,
      },
  )
  data = response.json()
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "crypto/rand"
      "encoding/json"
      "fmt"
      "net/http"
  )

  func main() {
      body, _ := json.Marshal(map[string]interface{}{
          "toolType":      "reply_extractor",
          "targetTweetId": "1893704267862470862",
          "resultsLimit":  500,
      })

      req, err := http.NewRequest("POST", "https://xquik.com/api/v1/extractions", bytes.NewReader(body))
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Idempotency-Key", rand.Text())
      req.Header.Set("Content-Type", "application/json")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      var data map[string]interface{}
      if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
          panic(err)
      }
      fmt.Println(data)
  }
  ```
</CodeGroup>

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. Session cookie authentication is also supported.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

<ParamField header="Idempotency-Key" type="string">
  Generate one unique value for each job. Reuse it only for an exact retry.
</ParamField>

## Body

<ParamField body="toolType" type="string" required>
  Extraction tool to run or estimate. See the endpoint's tool list.
</ParamField>

### Single targets

<ParamField body="targetTweetId" type="string">
  Tweet ID for a tweet-centered extraction.
</ParamField>

<ParamField body="targetUsername" type="string">
  Username for an account-centered extraction. You may include `@`.
</ParamField>

<ParamField body="targetCommunityId" type="string">
  Community ID for a community extraction.
</ParamField>

<ParamField body="targetListId" type="string">
  List ID for a list extraction.
</ParamField>

<ParamField body="targetSpaceId" type="string">
  Space ID for `space_explorer`.
</ParamField>

<ParamField body="searchQuery" type="string">
  Query for `tweet_search_extractor` or `community_search`.
</ParamField>

### Collection targets

<ParamField body="targetTweetIds" type="string[]">
  Process 1-10,000 Tweet IDs in one collection job.
</ParamField>

<ParamField body="targetUsernames" type="string[]">
  Process 1-100 unique usernames in one job. `tweet_search_extractor` collects their posts.
</ParamField>

<ParamField body="targetCommunityIds" type="string[]">
  Process 1-100 unique community IDs in one collection job.
</ParamField>

<ParamField body="targetListIds" type="string[]">
  Process 1-100 unique List IDs in one collection job.
</ParamField>

<ParamField body="searchQueries" type="string[]">
  Process 1-100 unique search queries in one collection job.
</ParamField>

<ParamField body="targets" type="array">
  Process up to 10,000 mixed targets with automatic routing.
</ParamField>

Each target accepts a supported string or `{ "kind": "...", "value": "..." }`.

<ParamField body="relationTargets" type="array">
  Process up to 100 profile relations in one collection job.
</ParamField>

Each relation target uses `{ "relation": "...", "value": "..." }`.

### Collection controls

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

<ParamField body="resultsLimit" type="integer">
  Maximum unique results to emit. Defaults to `10,000`. Use any positive integer.
</ParamField>

<ParamField body="maxItemsPerTarget" type="integer">
  Maximum results collected for each target. Minimum: `1`.
</ParamField>

<ParamField body="maxPagesPerTarget" type="integer">
  Reply pages collected per target. Range: `1-1,000`.
</ParamField>

<ParamField body="startCursor" type="string">
  Resume one reply target from this cursor.
</ParamField>

<ParamField body="dedupeAcrossTargets" type="boolean">
  Merge duplicates across targets. Defaults to `true`.
</ParamField>

<ParamField body="dedupeMode" type="string">
  Duplicate handling: `none`, `first`, or `merge`.
</ParamField>

<ParamField body="overlapMode" type="boolean">
  Use `dedupeMode=merge`. Defaults to `false`.
</ParamField>

<ParamField body="includeSearchTerms" type="boolean">
  Add matched search terms to collection metadata. Defaults to `false`.
</ParamField>

<ParamField body="includeTargetMetadata" type="boolean">
  Add source target metadata to each result. Defaults to `true`.
</ParamField>

<ParamField body="enrichProfiles" type="boolean">
  Add each user's full profile at `enrichmentData.profile`. Applies to
  `follower_explorer`, `following_explorer`, `verified_follower_explorer`,
  `people_search` & `repost_extractor`. Jobs take longer. Defaults to `false`.
</ParamField>

### Reply collection

<ParamField body="collectionStrategy" type="string">
  Strategy: `auto`, `complete`, `direct`, `search`, or `thread`.
</ParamField>

<ParamField body="scope" type="string">
  Reply scope: `all`, `direct`, or `nested`. Defaults to `all`.
</ParamField>

<ParamField body="maxDepth" type="integer">
  Maximum nested reply depth. Minimum: `1`.
</ParamField>

<ParamField body="sort" type="string">
  Order: `relevance`, `latest`, `oldest`, or `likes`.
</ParamField>

<ParamField body="excludeOriginalAuthor" type="boolean">
  Exclude replies from the source author. Defaults to `false`.
</ParamField>

<ParamField body="includeOriginalPost" type="boolean">
  Include the source post. Defaults to `false`.
</ParamField>

<ParamField body="hasMediaOnly" type="boolean">
  Return only replies with media. Defaults to `false`.
</ParamField>

<ParamField body="sinceTime" type="string | integer">
  Reply start time as ISO 8601 or Unix seconds.
</ParamField>

<ParamField body="untilTime" type="string | integer">
  Reply end time as ISO 8601 or Unix seconds.
</ParamField>

### Tweet result filters

<ParamField body="minViews" type="integer">
  Minimum Tweet view count.
</ParamField>

<ParamField body="minBookmarks" type="integer">
  Minimum Tweet bookmark count.
</ParamField>

<ParamField body="maxLikes" type="integer">
  Maximum Tweet like count.
</ParamField>

<ParamField body="maxRetweets" type="integer">
  Maximum Tweet repost count.
</ParamField>

<ParamField body="maxReplies" type="integer">
  Maximum Tweet reply count.
</ParamField>

<ParamField body="maxQuotes" type="integer">
  Maximum Tweet quote count.
</ParamField>

<ParamField body="blueVerifiedOnly" type="boolean">
  Return only Blue-verified Tweet authors. Defaults to `false`.
</ParamField>

<ParamField body="cardName" type="string">
  X search no longer supports `cardName`, so a search with it finds no Tweets.
</ParamField>

<ParamField body="source" type="string">
  X search no longer supports `source`, so a search with it finds no Tweets.
</ParamField>

<ParamField body="excludeSource" type="string">
  X search no longer supports `excludeSource`, so a search ignores it.
</ParamField>

<ParamField body="geocode" type="string">
  X search no longer supports `geocode`, so a search with it finds no Tweets.
</ParamField>

<ParamField body="sinceId" type="string">
  Return Tweets whose IDs exceed this ID.
</ParamField>

<ParamField body="maxId" type="string">
  Return Tweets at or below this ID.
</ParamField>

<ParamField body="near" type="string">
  Match a place name.
</ParamField>

<ParamField body="within" type="string">
  X search no longer supports `within`, so a search with it finds no Tweets. Use
  `near` alone.
</ParamField>

<ParamField body="withinTime" type="string">
  Match Tweets inside a recent time window.
</ParamField>

<ParamField body="nativeRetweets" type="boolean">
  Return only native reposts. Defaults to `false`.
</ParamField>

<ParamField body="safe" type="boolean">
  Enable safe search. Defaults to `false`.
</ParamField>

<ParamField body="news" type="boolean">
  X no longer searches `filter:news`, so leave this unset.
</ParamField>

### Profile result filters

<ParamField body="minFollowers" type="integer">
  Minimum profile follower count.
</ParamField>

<ParamField body="maxFollowers" type="integer">
  Maximum profile follower count.
</ParamField>

<ParamField body="minFollowing" type="integer">
  Minimum profile following count.
</ParamField>

<ParamField body="maxFollowing" type="integer">
  Maximum profile following count.
</ParamField>

<ParamField body="minPosts" type="integer">
  Minimum profile post count.
</ParamField>

<ParamField body="maxPosts" type="integer">
  Maximum profile post count.
</ParamField>

<ParamField body="minAccountAgeDays" type="integer">
  Minimum profile age in days.
</ParamField>

<ParamField body="verifiedType" type="string">
  Match the exact profile verification type.
</ParamField>

<ParamField body="hasWebsite" type="boolean">
  Require a profile website. Defaults to `false`.
</ParamField>

<ParamField body="hasLocation" type="boolean">
  Require a profile location. Defaults to `false`.
</ParamField>

<ParamField body="bioContains" type="string">
  Require bio terms separated by commas or lines.
</ParamField>

<ParamField body="locationContains" type="string">
  Require matching profile location text.
</ParamField>

<ParamField body="usernameContains" type="string">
  Require matching username text.
</ParamField>

### Tweet search filters

These fields apply to `tweet_search_extractor`.

<ParamField body="fromUser" type="string">
  Match an author username without `@`.
</ParamField>

<ParamField body="toUser" type="string">
  Match replies sent to a username.
</ParamField>

<ParamField body="mentioning" type="string">
  Match Tweets mentioning a username.
</ParamField>

<ParamField body="language" type="string">
  Match a language code, such as `en`.
</ParamField>

<ParamField body="sinceDate" type="string">
  Include Tweets on or after `YYYY-MM-DD`.
</ParamField>

<ParamField body="untilDate" type="string">
  Include Tweets up to `YYYY-MM-DD`, a UTC day, inclusive, so its own Tweets count.
</ParamField>

<ParamField body="mediaType" type="string">
  Media: `images`, `videos`, `gifs`, `media`, `links`, or `none`.
</ParamField>

<ParamField body="minFaves" type="integer">
  Minimum like count.
</ParamField>

<ParamField body="minRetweets" type="integer">
  Minimum repost count.
</ParamField>

<ParamField body="minReplies" type="integer">
  Minimum reply count.
</ParamField>

<ParamField body="minQuotes" type="integer">
  Minimum quote count.
</ParamField>

<ParamField body="verifiedOnly" type="boolean">
  Return only verified authors.
</ParamField>

<ParamField body="replies" type="string">
  Reply mode: `include`, `exclude`, or `only`.
</ParamField>

<ParamField body="retweets" type="string">
  Repost mode: `include`, `exclude`, or `only`.
</ParamField>

<ParamField body="quotes" type="string">
  Quote mode: `include`, `exclude`, or `only`.
</ParamField>

<ParamField body="exactPhrase" type="string">
  Match one exact phrase.
</ParamField>

<ParamField body="excludeWords" type="string">
  Exclude words or quoted phrases.
</ParamField>

<ParamField body="anyWords" type="string">
  Match any listed word or quoted phrase.
</ParamField>

<ParamField body="hashtags" type="string">
  Match hashtags separated by spaces, commas, or lines.
</ParamField>

<ParamField body="cashtags" type="string">
  Match cashtags separated by spaces, commas, or lines.
</ParamField>

<ParamField body="url" type="string">
  Match a URL substring or domain.
</ParamField>

<ParamField body="conversationId" type="string">
  Match a conversation ID.
</ParamField>

<ParamField body="inReplyToTweetId" type="string">
  Return only replies to this Tweet ID.
</ParamField>

<ParamField body="quotesOfTweetId" type="string">
  Return only quotes of this Tweet ID.
</ParamField>

<ParamField body="retweetsOfTweetId" type="string">
  Return only reposts of this Tweet ID.
</ParamField>

<ParamField body="listId" type="string">
  Search within a List ID.
</ParamField>

<ParamField body="place" type="string">
  Search within a place ID.
</ParamField>

<ParamField body="placeCountry" type="string">
  Search within a country code.
</ParamField>

<ParamField body="pointRadius" type="string">
  Set a geographic center and radius.
</ParamField>

<ParamField body="boundingBox" type="string">
  Set a geographic bounding box.
</ParamField>

<ParamField body="advancedQuery" type="string">
  Append raw advanced search syntax.
</ParamField>

## Tool types

Choose supported target fields. `resultsLimit` defaults to `10,000`. Send a positive integer to change it.

<CardGroup cols={2}>
  <Card title="Tweet target" icon="message-circle">
    Use `targetTweetId` for tweet-centered jobs:

    * `article_extractor` extracts article content from a tweet.
    * `favoriters` extracts visible users who liked a post. Liker identities can
      be unavailable even when the post reports likes. The job reads through
      your connected X account. Without one, the job fails with `errorMessage`
      set to `errorXAccountRequired`.
    * `quote_extractor` extracts users who quote-tweeted a tweet.
    * `reply_extractor` extracts users who replied to a tweet.
    * `repost_extractor` extracts users who retweeted a tweet.
    * `thread_extractor` extracts all tweets in a thread.
  </Card>

  <Card title="Username target" icon="user">
    Use `targetUsername` for account-centered jobs:

    * `follower_explorer` extracts followers of an account.
    * `following_explorer` extracts accounts followed by a user.
    * `mention_extractor` extracts tweets mentioning an account.
    * `post_extractor` extracts posts from an account.
    * `user_likes` extracts liked tweets that X makes visible. X shows likes only
      to their owner. The job reads through your connected X account. Without
      one, the job fails with `errorMessage` set to `errorXAccountRequired`.
    * `user_media` extracts media posts from a user.
    * `verified_follower_explorer` extracts verified followers of an account.
  </Card>

  <Card title="Community target" icon="users">
    Use `targetCommunityId` for community jobs:

    * `community_extractor` extracts members of a community.
    * `community_moderator_explorer` extracts moderators of a community.
    * `community_post_extractor` extracts posts from a community.
    * `community_search` searches matching posts within that community and also requires `searchQuery`.
  </Card>

  <Card title="Search query" icon="search">
    Use `searchQuery` for keyword jobs:

    * `people_search` searches for users by keyword.
    * `tweet_search_extractor` searches and extracts tweets by keyword or hashtag.
  </Card>

  <Card title="List target" icon="list">
    Use `targetListId` for X List jobs:

    * `list_follower_explorer` extracts followers of a list.
    * `list_member_extractor` extracts members of a list.
    * `list_post_extractor` extracts posts from a list.
  </Card>

  <Card title="Space target" icon="radio">
    Use `targetSpaceId` for Space jobs:

    * `space_explorer` extracts participants of a Space.

    Store `targetSpaceId` beside the returned extraction `id`. Poll
    [Get Extraction](/api-reference/extractions/twitter-extraction-results) or use
    [Export Extraction](/api-reference/extractions/export) after completion to
    read participant user rows.
  </Card>
</CardGroup>

## Collect attached media from mixed sources

Use `tweet_search_extractor` when one job needs several Tweet sources. Set each
source in `targets`. Use `mediaType: "media"` to keep rows with attachments.

```json theme={null}
{
  "toolType": "tweet_search_extractor",
  "targets": [
    { "kind": "tweet", "value": "1893704267862470862" },
    { "kind": "replies", "value": "1893704267862470862" },
    { "kind": "quotes", "value": "1893704267862470862" },
    { "kind": "thread", "value": "1893704267862470862" },
    { "kind": "profile_media", "value": "openai" }
  ],
  "mediaType": "media",
  "resultsLimit": 100
}
```

Full output stores attachments at `enrichmentData.tweet.media`. Add
`outputPreset=flat` while retrieving results to return `media` at row level.
Use `user_media` with `targetUsername` for one profile-only job.

## Response

### 202 Accepted

<ResponseField name="id" type="string">
  Unique extraction job ID (UUID).
</ResponseField>

<ResponseField name="pollAfterMs" type="number">
  Milliseconds to wait before polling.
</ResponseField>

<ResponseField name="statusUrl" type="string">
  Relative URL for status and results.
</ResponseField>

<ResponseField name="waitUrl" type="string">
  Relative polling URL. It waits 10 seconds and returns 1 compact row.
</ResponseField>

<ResponseField name="toolType" type="string">
  Tool type used for this extraction.
</ResponseField>

<ResponseField name="status" type="string">
  Current job status.
</ResponseField>

<Info>
  Prefer `waitUrl` while the job runs. Use `statusUrl` for immediate checks. New jobs include `Location` and `Retry-After`. Terminal replays return `pollAfterMs: 0` and omit `Retry-After`.
</Info>

### 400 Invalid input

The request body is missing or malformed. Send every required field.

### 400 Invalid tool type

Use one of the 23 [tool types](#tool-types).

### 401 Unauthenticated

Missing or invalid API key.

### 402 Insufficient credits

The balance can't cover the extraction. Errors: `no_subscription`, `subscription_inactive`, `no_credits` or `insufficient_credits`.

### 403 Protected account

Post, media & follower tools refuse a protected `targetUsername`. Choose a public account. A multi-target job skips it & ends with `partial_failure`.

### 404 Not found

The target tweet or user doesn't exist, was deleted, or is suspended.

### 409 Idempotency conflict

The key was used for a different request.

### 424 X API dependency failed

Send `xquik-api-contract: 2026-04-29` to get 424 instead of the default `502`.

### 429 Rate limited

Wait `Retry-After` seconds, then retry.

### 502 X API unavailable

The read service is unavailable. Retry with exponential backoff.

<Note>
  **Next steps.** [Get Extraction](/api-reference/extractions/twitter-extraction-results) to retrieve results with pagination, [Export Extraction](/api-reference/extractions/export) to download as CSV, XLSX or Markdown, or [Estimate Extraction](/api-reference/extractions/twitter-scraping-cost-estimator) to check costs before running.
</Note>


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