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

# Extraction workflow

> Scrape tweets, export followers, estimate credits, start extraction jobs, paginate JSON results, and export CSV, JSON, or XLSX files

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

Run bulk data extractions from X in 5 stages: check credits, estimate costs, run the job, retrieve JSON pages, and export files. Use this workflow to scrape tweets, export followers, pull tweet replies, save CSV/JSON/XLSX files, or hand paginated JSON to a CRM, warehouse, queue, or AI agent.

## Workflow overview

<CardGroup cols={2}>
  <Card title="1. Check credits" icon="credit-card" href="/api-reference/account/get">
    Call `GET /account` to confirm subscription status and available credits.
  </Card>

  <Card title="2. Estimate cost" icon="calculator" href="/api-reference/extractions/estimate">
    Call `POST /extractions/estimate` with the same target you plan to run.
  </Card>

  <Card title="3. Run extraction" icon="play" href="/api-reference/extractions/create">
    Call `POST /extractions` after the estimate fits your balance and result cap.
  </Card>

  <Card title="4. Retrieve JSON" icon="rows-3" href="/api-reference/extractions/get">
    Call `GET /extractions/{id}` and page through `nextCursor` while `hasMore` is true.
  </Card>

  <Card title="5. Export file" icon="download" href="/api-reference/extractions/export">
    Call `GET /extractions/{id}/export` when you need CSV, JSON, XLSX, or another file.
  </Card>
</CardGroup>

<Info>
  Treat `202 Accepted` as a queued run receipt. Credits are reserved after the job starts. Poll `GET /extractions/{id}` before handoff. The run can lower `resultsLimit` to the affordable count or fail with `insufficient_credits`.
</Info>

<Steps>
  <Step title="Check credits">
    Verify your subscription is active and your credit balance can cover the job.
  </Step>

  <Step title="Estimate cost">
    Preview the extraction cost before committing. Check whether it fits within your remaining budget.
  </Step>

  <Step title="Run the extraction">
    Submit the extraction job, store the `202 Accepted` receipt, then poll before handoff.
  </Step>

  <Step title="Retrieve results">
    Paginate through extracted data via the API, or export as CSV, JSON, XLSX, Markdown, PDF, or TXT.
  </Step>

  <Step title="Export files">
    Download CSV, JSON, XLSX, Markdown, PDF, or TXT when a downstream tool expects a file.
  </Step>
</Steps>

## End-to-end agent handoff

Store one checkpoint per extraction run so another worker can resume without rereading logs:

```json theme={null}
{
  "workflow": "reply_extractor_to_csv",
  "request": {
    "toolType": "reply_extractor",
    "targetTweetId": "1893704267862470862",
    "resultsLimit": 500
  },
  "estimate": {
    "estimatedResults": 500,
    "creditsRequired": "500",
    "creditsAvailable": "77000",
    "allowed": true,
    "source": "replyCount"
  },
  "create_receipt": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "running",
    "poll_path": "/api/v1/extractions/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "json_pages": {
    "limit": 1000,
    "page_cursor": null,
    "next_cursor": "990200",
    "has_more": true
  },
  "inventory_path": "/api/v1/extractions?status=completed&toolType=reply_extractor",
  "export_path": "/api/v1/extractions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/export?format=csv",
  "handoff_state": "poll_until_completed_then_export"
}
```

<CardGroup cols={2}>
  <Card title="Estimate checkpoint" icon="calculator">
    Store `estimatedResults`, `creditsRequired`, `creditsAvailable`, `allowed`, and `source` before creating the job.
  </Card>

  <Card title="Create receipt" icon="clipboard-check">
    Store the returned job `id`, `status`, and `poll_path`; result rows arrive from `GET /extractions/{id}`.
  </Card>

  <Card title="Cursor state" icon="shuffle">
    Store `page_cursor`, `next_cursor`, and `has_more` for each JSON page so workers can resume pagination.
  </Card>

  <Card title="File handoff" icon="download">
    Store `inventory_path` for later job lookup and `export_path` for the CSV, JSON, or XLSX download.
  </Card>
</CardGroup>

## Step 1: Check credits

Before running an extraction, call [`GET /account`](/api-reference/account/get)
and store a small planning checkpoint:

```bash cURL theme={null}
curl -s https://xquik.com/api/v1/account \
  -H "x-api-key: xq_YOUR_KEY_HERE" | jq
```

```json theme={null}
{
  "checkpoint_type": "credit_check",
  "plan": "active",
  "credit_balance": "77000",
  "next_action": "estimate_extraction"
}
```

Use `plan` to confirm the subscription is active, and use
`creditInfo.balance` to decide whether to continue, top up, or lower
`resultsLimit` before estimating the extraction.

## Step 2: Estimate cost

Always estimate before running. The estimate endpoint previews the result count and projected credit usage without consuming credits.

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

  ```javascript Node.js theme={null}
  const estimate = await fetch("https://xquik.com/api/v1/extractions/estimate", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      toolType: "reply_extractor",
      targetTweetId: "1893704267862470862",
    }),
  }).then((r) => r.json());

  if (!estimate.allowed) {
    throw new Error(`Insufficient credits: need ${estimate.creditsRequired}, have ${estimate.creditsAvailable}`);
  }

  console.log(`Estimated results: ${estimate.estimatedResults}`);
  ```

  ```python Python theme={null}
  estimate = requests.post(
      "https://xquik.com/api/v1/extractions/estimate",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={
          "toolType": "reply_extractor",
          "targetTweetId": "1893704267862470862",
      },
  ).json()

  if not estimate["allowed"]:
      raise Exception(f"Insufficient credits: need {estimate['creditsRequired']}, have {estimate['creditsAvailable']}")

  print(f"Estimated results: {estimate['estimatedResults']}")
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "allowed": true,
  "creditsRequired": "150",
  "creditsAvailable": "77000",
  "estimatedResults": 150,
  "source": "replyCount",
  "resolvedXUserId": "44196397"
}
```

<CardGroup cols={2}>
  <Card title="Allowed" icon="circle-check">
    `allowed` tells you whether the job can start with the current credit balance.
  </Card>

  <Card title="Estimate source" icon="database">
    `source` names the count used for the estimate, such as `replyCount`, `followers`, or `resultsLimit`.
  </Card>

  <Card title="Estimated results" icon="hash">
    `estimatedResults` is the approximate number of records the job will return.
  </Card>

  <Card title="Credits required" icon="credit-card">
    `creditsRequired` is the projected credit usage for this extraction.
  </Card>

  <Card title="Credits available" icon="coins">
    `creditsAvailable` is your current balance when the estimate runs.
  </Card>

  <Card title="Resolved X user ID" icon="user-check">
    `resolvedXUserId` appears when `targetUsername` resolves to an X user ID.
  </Card>
</CardGroup>

<Warning>
  If `allowed` is `false`, the extraction will return `402`. Top up credits or run a smaller job.
</Warning>

<Tip>
  Add `resultsLimit` to cap the number of results. Both the estimate and extraction endpoints accept this parameter. When `resultsLimit` is lower than the source estimate, the estimate uses `resultsLimit` as the projected count and returns `source: "resultsLimit"`. Otherwise it keeps the source count, such as `followers` or `replyCount`, and the extraction still stops once it reaches the cap.
</Tip>

## Step 3: Run the extraction

Submit the job with the same parameters you used for the estimate.

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

  ```javascript Node.js theme={null}
  const extraction = await fetch("https://xquik.com/api/v1/extractions", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      toolType: "reply_extractor",
      targetTweetId: "1893704267862470862",
    }),
  }).then((r) => r.json());

  console.log(`Job ${extraction.id}: ${extraction.status}`);
  ```

  ```python Python theme={null}
  extraction = requests.post(
      "https://xquik.com/api/v1/extractions",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={
          "toolType": "reply_extractor",
          "targetTweetId": "1893704267862470862",
      },
  ).json()

  print(f"Job {extraction['id']}: {extraction['status']}")
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "toolType": "reply_extractor",
  "status": "running"
}
```

The endpoint returns `202 Accepted` with the job in `running` status. Poll `GET /extractions/{id}` until status is `completed` or `failed`. For large extractions (10,000+ results), this may take up to a few minutes.

## Step 4: Retrieve results

## Data handoff

Choose the handoff based on the system that consumes the extraction.

<CardGroup cols={2}>
  <Card title="App or API pipeline" icon="code">
    Use `GET /extractions/{id}`. Store `job`, `results`, `hasMore`, `nextCursor`.
    Use `limit` up to 1,000 and pass `nextCursor` as `after`.
  </Card>

  <Card title="CRM import" icon="users">
    Use `GET /extractions/{id}/export?format=csv`. Store `User ID`,
    `Username`, `Display Name`, `Followers`, and `Verified`. Upsert by stable X
    user ID when possible.
  </Card>

  <Card title="Warehouse or queue" icon="database">
    Use `GET /extractions/{id}/export?format=json` or paginated JSON. Store
    `xUserId`, `xUsername`, `tweetId`, `tweetText`, `createdAt`. Keep the
    extraction ID with each load for replay and audit.
  </Card>

  <Card title="Analyst review" icon="file-spreadsheet">
    Use `GET /extractions/{id}/export?format=xlsx`. Store export columns plus
    enrichment fields. Use CSV/JSON for automation and XLSX for manual review.
  </Card>
</CardGroup>

Paginated JSON is not row-capped by the export limit. File exports are capped at 100,000 rows, and PDF exports are capped at 10,000 rows.

### Option A: Paginate via API

Fetch results in pages of up to 1,000 records. Use cursor-based pagination to iterate through all results.

<CodeGroup>
  ```bash cURL theme={null}
  # First page
  curl -s "https://xquik.com/api/v1/extractions/77777?limit=1000" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Next page (use nextCursor from previous response)
  curl -s "https://xquik.com/api/v1/extractions/77777?limit=1000&after=990100" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const extractionId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
  let cursor = undefined;
  const allResults = [];

  do {
    const params = new URLSearchParams({ limit: "1000" });
    if (cursor) params.set("after", cursor);

    const data = await fetch(
      `https://xquik.com/api/v1/extractions/${extractionId}?${params}`,
      { headers: { "x-api-key": "xq_YOUR_KEY_HERE" } },
    ).then((r) => r.json());

    allResults.push(...data.results);
    cursor = data.hasMore ? data.nextCursor : undefined;
  } while (cursor);

  console.log(`Fetched ${allResults.length} results`);
  ```

  ```python Python theme={null}
  extraction_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  all_results = []
  cursor = None

  while True:
      params = {"limit": 1000}
      if cursor:
          params["after"] = cursor

      data = requests.get(
          f"https://xquik.com/api/v1/extractions/{extraction_id}",
          headers={"x-api-key": "xq_YOUR_KEY_HERE"},
          params=params,
      ).json()

      all_results.extend(data["results"])

      if data.get("hasMore"):
          cursor = data["nextCursor"]
      else:
          break

  print(f"Fetched {len(all_results)} results")
  ```

  ```go Go theme={null}
  extractionID := "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  cursor := ""
  var allResults []map[string]interface{}

  for {
      url := fmt.Sprintf("https://xquik.com/api/v1/extractions/%s?limit=1000", extractionID)
      if cursor != "" {
          url += "&after=" + cursor
      }

      req, _ := http.NewRequest("GET", url, nil)
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")

      resp, _ := http.DefaultClient.Do(req)
      var data struct {
          Results    []map[string]interface{} `json:"results"`
          HasMore    bool                     `json:"hasMore"`
          NextCursor string                   `json:"nextCursor"`
      }
      json.NewDecoder(resp.Body).Decode(&data)
      resp.Body.Close()

      allResults = append(allResults, data.Results...)

      if !data.HasMore {
          break
      }
      cursor = data.NextCursor
  }

  fmt.Printf("Fetched %d results\n", len(allResults))
  ```
</CodeGroup>

### Durable JSON Lines handoff

Use JSON Lines when a queue, warehouse, or agent needs replayable rows without the export row cap. Write each paginated result with the cursor state that produced it.

```json theme={null}
{
  "extraction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "row_id": "990001",
  "x_user_id": "44196397",
  "x_username": "elonmusk",
  "tweet_id": "1893710452812718080",
  "tweet_text": "This is a great thread, thanks for sharing.",
  "page_cursor": "990100",
  "next_cursor": "990200",
  "has_more": true,
  "handoff_format": "jsonl"
}
```

Store rows in `xquik-extraction-results.jsonl` for queue replay, warehouse loads, or agent audits. Keep `page_cursor` and `next_cursor` so the job can resume from the last successful page.

Each result contains user profile data and (for tweet-based tools) tweet data:

<Expandable title="Example response">
  ```json theme={null}
  {
    "id": "990001",
    "xUserId": "44196397",
    "xUsername": "elonmusk",
    "xDisplayName": "Elon Musk",
    "xFollowersCount": 210500000,
    "xVerified": true,
    "xProfileImageUrl": "https://pbs.twimg.com/profile_images/el0n.jpg",
    "tweetId": "1893710452812718080",
    "tweetText": "This is a great thread, thanks for sharing.",
    "tweetCreatedAt": "2026-02-24T10:05:00.000Z",
    "createdAt": "2026-02-24T10:06:12.000Z"
  }
  ```
</Expandable>

<Info>
  Only `id`, `xUserId`, and `createdAt` are guaranteed on every result. All other fields are omitted when unavailable (never `null`). Check for field presence before accessing.
</Info>

### Option B: Export as file

Download results as CSV, JSON, XLSX, Markdown, PDF, or TXT. Exports include enrichment data such as bios, locations, and engagement metrics.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s "https://xquik.com/api/v1/extractions/77777/export?format=csv" \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -o extraction-reply_extractor-77777.csv
  ```

  ```javascript Node.js theme={null}
  const extractionId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
  const response = await fetch(
    `https://xquik.com/api/v1/extractions/${extractionId}/export?format=csv`,
    { headers: { "x-api-key": "xq_YOUR_KEY_HERE" } },
  );

  const blob = await response.blob();
  // Save to file or process as needed
  ```

  ```python Python theme={null}
  response = requests.get(
      f"https://xquik.com/api/v1/extractions/{extraction_id}/export",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      params={"format": "csv"},
  )

  with open("extraction-reply_extractor-77777.csv", "wb") as f:
      f.write(response.content)
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("GET", "https://xquik.com/api/v1/extractions/77777/export?format=csv", nil)
  req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")

  resp, _ := http.DefaultClient.Do(req)
  defer resp.Body.Close()

  file, _ := os.Create("extraction-reply_extractor-77777.csv")
  defer file.Close()
  io.Copy(file, resp.Body)
  ```
</CodeGroup>

<CardGroup cols={2}>
  <Card title="CSV" icon="table">
    `format=csv` returns `text/csv; charset=utf-8` for spreadsheets and data
    pipelines.
  </Card>

  <Card title="JSON" icon="braces">
    `format=json` returns `application/json; charset=utf-8` for API clients and
    programmatic processing.
  </Card>

  <Card title="Markdown" icon="file-text">
    `format=md` returns `text/markdown; charset=utf-8` for documentation and
    issue handoffs.
  </Card>

  <Card title="Markdown document" icon="file-text">
    `format=md-document` returns `text/markdown; charset=utf-8` for longer
    markdown documents.
  </Card>

  <Card title="PDF" icon="file-down">
    `format=pdf` returns `application/pdf` for reports and sharing. PDF exports
    are capped at `10,000` rows.
  </Card>

  <Card title="TXT" icon="file">
    `format=txt` returns `text/plain; charset=utf-8` for plain text and logs.
  </Card>

  <Card title="XLSX" icon="file-spreadsheet">
    `format=xlsx` returns
    `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` for
    Excel and formatted reports.
  </Card>
</CardGroup>

<Warning>
  Exports are capped at 100,000 rows (10,000 for PDF). For larger extractions, use the paginated API to retrieve all results.
</Warning>

## Tool types reference

All 23 extraction tools grouped by target type. Each requires a specific target field.

### Tweet-based tools

<CardGroup cols={2}>
  <Card title="Reply authors" icon="message-square">
    Use `reply_extractor` with `targetTweetId` to extract users who replied to a
    tweet.
  </Card>

  <Card title="Repost authors" icon="repeat-2">
    Use `repost_extractor` with `targetTweetId` to extract users who reposted a
    tweet.
  </Card>

  <Card title="Quote authors" icon="quote">
    Use `quote_extractor` with `targetTweetId` to extract users who quote-posted
    a tweet.
  </Card>

  <Card title="Like authors" icon="heart">
    Use `favoriters` with `targetTweetId` to extract visible users who liked a
    post. Liker identities can be unavailable even when the post reports likes.
  </Card>

  <Card title="Thread tweets" icon="message-square-text">
    Use `thread_extractor` with `targetTweetId` to extract all tweets in a
    thread.
  </Card>

  <Card title="Article content" icon="file-text">
    Use `article_extractor` with `targetTweetId` to extract article content from
    a tweet.
  </Card>
</CardGroup>

### User-based tools

<CardGroup cols={2}>
  <Card title="Follower profiles" icon="users">
    Use `follower_explorer` with `targetUsername` to extract followers of an
    account.
  </Card>

  <Card title="Following profiles" icon="user-plus">
    Use `following_explorer` with `targetUsername` to extract accounts followed
    by a user.
  </Card>

  <Card title="Verified followers" icon="badge-check">
    Use `verified_follower_explorer` with `targetUsername` to extract verified
    followers of an account.
  </Card>

  <Card title="Mention tweets" icon="at-sign">
    Use `mention_extractor` with `targetUsername` to extract tweets mentioning
    an account.
  </Card>

  <Card title="Account posts" icon="send">
    Use `post_extractor` with `targetUsername` to extract posts from an
    account.
  </Card>

  <Card title="Liked tweets" icon="heart">
    Use `user_likes` with `targetUsername` to extract tweets liked by a user.
  </Card>

  <Card title="Media posts" icon="image">
    Use `user_media` with `targetUsername` to extract media posts from a user.
  </Card>
</CardGroup>

### Community tools

<CardGroup cols={2}>
  <Card title="Community members" icon="users">
    Use `community_extractor` with `targetCommunityId` to extract members of a
    community.
  </Card>

  <Card title="Community moderators" icon="shield-check">
    Use `community_moderator_explorer` with `targetCommunityId` to extract
    moderators of a community.
  </Card>

  <Card title="Community posts" icon="message-square-text">
    Use `community_post_extractor` with `targetCommunityId` to extract posts
    from a community.
  </Card>

  <Card title="Search within a community" icon="search">
    Use `community_search` with both `targetCommunityId` and `searchQuery` to
    search matching posts inside one community.
  </Card>
</CardGroup>

### List tools

<CardGroup cols={2}>
  <Card title="List members" icon="users">
    Use `list_member_extractor` with `targetListId` to extract members of a
    list.
  </Card>

  <Card title="List tweets" icon="message-square-text">
    Use `list_post_extractor` with `targetListId` to extract tweets from a
    list.
  </Card>

  <Card title="List followers" icon="user-plus">
    Use `list_follower_explorer` with `targetListId` to extract followers of a
    list.
  </Card>
</CardGroup>

### Other tools

<CardGroup cols={2}>
  <Card title="People search" icon="search">
    Use `people_search` with `searchQuery` to find user profiles by keyword.
  </Card>

  <Card title="Space participants" icon="radio">
    Use `space_explorer` with `targetSpaceId` to extract participants of a
    Space.
  </Card>

  <Card title="Tweet search" icon="message-square">
    Use `tweet_search_extractor` with `searchQuery` to extract tweets by
    keyword, hashtag, or structured filters.
  </Card>
</CardGroup>

### Tweet search filters

`tweet_search_extractor` supports 16 optional filter parameters that are converted to X search operators internally. These filters only apply to `tweet_search_extractor` and are ignored by all other tool types. Use structured fields first for common jobs such as search tweets from a user, search tweet replies, scrape tweets with images, or export posts in a date range. Use `advancedQuery` only when you already know the X search operator string you want to append.

<CardGroup cols={2}>
  <Card title="Account filters" icon="at-sign">
    Use `fromUser` for author username, `toUser` for tweets directed to a user,
    and `mentioning` for tweets that mention a user.
  </Card>

  <Card title="Date and language" icon="calendar-days">
    Use `language` for a language code such as `en`, `tr`, or `es`, then bound
    the export with `sinceDate` and `untilDate` in `YYYY-MM-DD` format.
  </Card>

  <Card title="Media and engagement" icon="image">
    Use `mediaType` for `images`, `videos`, `gifs`, or `media`, then set
    `minFaves`, `minRetweets`, or `minReplies` for minimum engagement.
  </Card>

  <Card title="Conversation filters" icon="message-circle">
    Use `verifiedOnly` for verified authors, `replies` to `include`, `exclude`,
    or return `only` replies, and `retweets` to `include`, `exclude`, or return
    `only` retweets.
  </Card>

  <Card title="Query refinement" icon="sliders-horizontal">
    Use `exactPhrase` for exact matches, `excludeWords` for comma-separated
    exclusions, and `advancedQuery` for raw X search operator syntax.
  </Card>
</CardGroup>

**Example: Search for popular English tweets with images from the last week**

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/extractions \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "toolType": "tweet_search_extractor",
      "searchQuery": "artificial intelligence",
      "language": "en",
      "mediaType": "images",
      "minFaves": 100,
      "sinceDate": "2026-03-06",
      "untilDate": "2026-03-13",
      "retweets": "exclude"
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const extraction = await fetch("https://xquik.com/api/v1/extractions", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      toolType: "tweet_search_extractor",
      searchQuery: "artificial intelligence",
      language: "en",
      mediaType: "images",
      minFaves: 100,
      sinceDate: "2026-03-06",
      untilDate: "2026-03-13",
      retweets: "exclude",
    }),
  }).then((r) => r.json());

  console.log(`Job ${extraction.id}: ${extraction.status}`);
  ```
</CodeGroup>

<Tip>
  Combine filters with `resultsLimit` to run targeted, cost-efficient searches. For example, find the top 50 viral tweets about a topic from verified accounts in the last 24 hours.
</Tip>

## MCP equivalent

The same workflow works through the MCP server using the `xquik` sandbox tool:

<CardGroup cols={2}>
  <Card title="Check account" icon="user-check">
    REST route: `GET /account`. MCP call:
    `xquik.request('/api/v1/account')` to confirm subscription, credits, and
    usage before a run.
  </Card>

  <Card title="Estimate extraction" icon="calculator">
    REST route: `POST /extractions/estimate`. MCP call:
    `xquik.request('/api/v1/extractions/estimate', { method: 'POST', body })`
    with the same body you plan to run.
  </Card>

  <Card title="Start extraction" icon="rocket">
    REST route: `POST /extractions`. MCP call:
    `xquik.request('/api/v1/extractions', { method: 'POST', body })` to create
    the background job.
  </Card>

  <Card title="Poll results" icon="refresh-cw">
    REST route: `GET /extractions/{id}`. MCP call:
    `xquik.request('/api/v1/extractions/ID')` to retrieve the job status and
    rows.
  </Card>
</CardGroup>

**Example prompts for AI agents:**

* "How much would it cost to extract all followers of @elonmusk?"
* "Extract visible replies to this tweet: [https://x.com/vercel/status/1893704267862470862](https://x.com/vercel/status/1893704267862470862)"
* "Show me the results of my last extraction."

<Note>
  File export (`GET /extractions/{id}/export`) is only available via the REST API. The MCP server returns results as structured JSON via `xquik.request()`.
</Note>

## Error handling

<CardGroup cols={2}>
  <Card title="Subscribe" icon="credit-card">
    `402 no_subscription` means the account has no active subscription. Subscribe
    from the [dashboard](https://xquik.com), then retry the extraction.
  </Card>

  <Card title="Top up credits" icon="coins">
    `402 insufficient_credits` means the account cannot cover the requested
    extraction. Top up credits, wait for the next grant, or lower `resultsLimit`.
  </Card>

  <Card title="Fix toolType" icon="list-checks">
    `400 invalid_tool_type` means `toolType` is not one of the supported
    extraction tools. Check the [tool types](#tool-types-reference) reference.
  </Card>

  <Card title="Fix required fields" icon="wrench">
    `400 invalid_input` usually means the target field is missing or malformed.
    Match `targetTweetId`, `targetUsername`, `targetCommunityId`,
    `targetListId`, `targetSpaceId`, or `searchQuery` to the selected tool.
  </Card>

  <Card title="Retry read service" icon="refresh-cw">
    `502 x_api_unavailable` means the read service is temporarily unavailable.
    Retry with exponential backoff, then contact support if the error persists.
  </Card>
</CardGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Create Extraction" icon="pickaxe" href="/api-reference/extractions/create">
    Full API reference with request/response schemas.
  </Card>

  <Card title="Estimate Extraction" icon="calculator" href="/api-reference/extractions/estimate">
    Cost estimation endpoint reference.
  </Card>

  <Card title="Export Extraction" icon="download" href="/api-reference/extractions/export">
    CSV, XLSX, and Markdown export with column details.
  </Card>

  <Card title="Billing & Usage" icon="credit-card" href="/guides/billing">
    Pricing, credits, and usage scenarios.
  </Card>
</CardGroup>
