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

# Get tweet favoriters

> Retrieve users who liked a specific tweet with profile rows, cursor checkpoints, and engagement handoffs

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

<Note>
  Requested result counts are upper bounds for paid authenticated calls. When remaining credits cannot cover the full page or ID list, Xquik returns fewer results. If zero paid results are affordable, it returns `402 insufficient_credits`.
</Note>

<Callout icon="coins" color="#5c3327">
  **1 credit per result returned** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit · Accepts [MPP](/mpp/overview)
</Callout>

<Info>
  Get tweet favoriters returns user profiles for accounts that liked one tweet.
  It is also useful as a tweet likers API, tweet likes API, X tweet likers API,
  Twitter tweet likers API, or users who liked a tweet endpoint. The canonical
  endpoint remains `GET /api/v1/x/tweets/{id}/favoriters`.
</Info>

<CodeGroup>
  ```bash First page theme={null}
  curl https://xquik.com/api/v1/x/tweets/1893456789012345678/favoriters \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```bash Next page theme={null}
  curl -G https://xquik.com/api/v1/x/tweets/1893456789012345678/favoriters \
    --data-urlencode "cursor=abc123" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const tweetId = "1893456789012345678";
  const response = await fetch(`https://xquik.com/api/v1/x/tweets/${tweetId}/favoriters`, {
    headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
  });
  const data = await response.json();
  const nextCursor = data.has_next_page ? data.next_cursor : null;
  const likerRows = data.users.map((user) => ({
    source_tweet_id: tweetId,
    liker_id: user.id,
    username: user.username,
    display_name: user.name,
    can_dm: user.canDm ?? null,
    follower_count: user.followers ?? null,
    following_count: user.following ?? null,
    verified: user.verified ?? false,
    verified_type: user.verifiedType ?? null,
    profile_image_url: user.profilePicture ?? null,
  }));
  const checkpoint = { source_tweet_id: tweetId, next_cursor: nextCursor };

  for (const row of likerRows) {
    process.stdout.write(`${JSON.stringify(row)}\n`);
  }
  process.stdout.write(`${JSON.stringify({ checkpoint })}\n`);
  ```

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

  tweet_id = "1893456789012345678"
  response = requests.get(
      f"https://xquik.com/api/v1/x/tweets/{tweet_id}/favoriters",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
  )
  data = response.json()
  next_cursor = data["next_cursor"] if data["has_next_page"] else None
  liker_rows = [
      {
          "source_tweet_id": tweet_id,
          "liker_id": user["id"],
          "username": user["username"],
          "display_name": user["name"],
          "can_dm": user.get("canDm"),
          "follower_count": user.get("followers"),
          "following_count": user.get("following"),
          "verified": user.get("verified", False),
          "verified_type": user.get("verifiedType"),
          "profile_image_url": user.get("profilePicture"),
      }
      for user in data["users"]
  ]
  checkpoint = {"source_tweet_id": tweet_id, "next_cursor": next_cursor}
  for row in liker_rows:
      print(json.dumps(row))
  print(json.dumps({"checkpoint": checkpoint}))
  ```
</CodeGroup>

The Node.js and Python snippets write JSON Lines liker rows plus a separate
checkpoint instead of raw response pages. Persist each mapped row and the latest
`next_cursor` so an import, giveaway verifier, CRM sync, or agent job can resume
from the last completed page without duplicate rows.

## Direct tweet liker handoff

Use `GET /api/v1/x/tweets/{id}/favoriters` when a workflow needs one row per
account that liked a tweet for giveaway checks, CRM enrichment, audience
analysis, or follow-up jobs. Store `source_tweet_id`, `liker_id`, `username`,
`display_name`, `can_dm`, `follower_count`, `following_count`, `verified`,
`verified_type`, `profile_image_url`, and a separate `next_cursor` checkpoint.

<CardGroup cols={2}>
  <Card title="Liker rows" icon="rows-3">
    Store `users[]` as the profile rows for accounts that liked the source
    tweet.
  </Card>

  <Card title="Stable upserts" icon="key-round">
    Store `users[].id` as `liker_id` with `source_tweet_id` for idempotent
    imports and giveaway checks.
  </Card>

  <Card title="Readable labels" icon="badge">
    Store `users[].username` and `users[].name` for handles, labels, and review
    queues.
  </Card>

  <Card title="Profile enrichment" icon="file-text">
    Store `description`, `location`, `url`, and `profilePicture` when returned
    for CRM and warehouse enrichment.
  </Card>

  <Card title="Audience signals" icon="chart-no-axes-combined">
    Store `followers`, `following`, `verified`, and `verifiedType` for scoring,
    filters, and outreach priority.
  </Card>

  <Card title="DM preflight" icon="message-square">
    Store `canDm` when returned, then use DM endpoints only after a user-approved
    message flow.
  </Card>

  <Card title="Next page" icon="arrow-right">
    Store `has_next_page` and `next_cursor`; pass `next_cursor` back as `cursor`
    only when `has_next_page` is true.
  </Card>

  <Card title="Credit-limited pages" icon="coins">
    Use `users.length`, not a requested page size, for row counts. Low balances
    can return fewer rows.
  </Card>
</CardGroup>

Direct tweet liker reads cost 1 credit per user returned. Low credit balances
can return fewer users than a full page; zero affordable results return
`402 insufficient_credits`. For MPP callers, this endpoint is billed as a
session at USD 0.00015 per user returned.

## Path parameters

<ParamField path="id" type="string" required>
  Tweet ID (numeric string).
</ParamField>

## Query parameters

<ParamField query="cursor" type="string">
  Pagination cursor from `next_cursor` in a previous response. Omit for the
  first page. Pass a cursor only when `has_next_page` is true.
</ParamField>

## Which tweet engagement endpoint?

<CardGroup cols={2}>
  <Card title="Tweet likers" icon="heart">
    Use `GET /x/tweets/{id}/favoriters` for user profiles that liked one source
    tweet.
  </Card>

  <Card title="Retweeters" icon="repeat-2">
    Use [`GET /x/tweets/{id}/retweeters`](/api-reference/x/retweeters) for user
    profiles that reposted one source tweet.
  </Card>

  <Card title="Quote tweets" icon="quote">
    Use [`GET /x/tweets/{id}/quotes`](/api-reference/x/tweet-quotes) when you
    need tweet rows that quote the source tweet.
  </Card>

  <Card title="Tweet replies" icon="message-square-reply">
    Use [`GET /x/tweets/{id}/replies`](/api-reference/x/tweet-replies) when you
    need reply tweet rows under the source tweet.
  </Card>

  <Card title="Saved exports" icon="file-spreadsheet">
    Use [`Create extraction`](/api-reference/extractions/create) with
    `toolType=favoriters` when you need a saved job or CSV, JSON, or XLSX
    export.
  </Card>

  <Card title="DM handoff" icon="send">
    Use [`Send DM`](/api-reference/x-write/send-dm) only after your workflow has
    a user-approved outreach step.
  </Card>
</CardGroup>

## Headers

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

## Response

<Tabs>
  <Tab title="200 OK">
    <ResponseField name="users" type="object[]">
      Array of users who liked the tweet.

      <Expandable title="User object fields">
        <ResponseField name="id" type="string">X user ID.</ResponseField>
        <ResponseField name="username" type="string">X username.</ResponseField>
        <ResponseField name="name" type="string">Display name.</ResponseField>
        <ResponseField name="description" type="string">Profile bio.</ResponseField>
        <ResponseField name="followers" type="number">Follower count.</ResponseField>
        <ResponseField name="following" type="number">Following count.</ResponseField>
        <ResponseField name="verified" type="boolean">Verified status.</ResponseField>
        <ResponseField name="profilePicture" type="string">Profile image URL.</ResponseField>
        <ResponseField name="location" type="string">Profile location.</ResponseField>
        <ResponseField name="createdAt" type="string">Account creation date (ISO 8601).</ResponseField>
        <ResponseField name="statusesCount" type="number">Total number of tweets posted. Omitted if unavailable.</ResponseField>
        <ResponseField name="coverPicture" type="string">Cover/banner image URL. Omitted if unavailable.</ResponseField>
        <ResponseField name="mediaCount" type="number">Total number of media tweets posted. Omitted if unavailable.</ResponseField>
        <ResponseField name="canDm" type="boolean">Whether the user accepts direct messages. Omitted if unavailable.</ResponseField>
        <ResponseField name="url" type="string">Website URL from profile. Omitted if empty.</ResponseField>
        <ResponseField name="favouritesCount" type="number">Total number of tweets liked. Omitted if unavailable.</ResponseField>
        <ResponseField name="hasCustomTimelines" type="boolean">Whether the user has custom timelines. Omitted if unavailable.</ResponseField>
        <ResponseField name="isTranslator" type="boolean">Whether the user is an X translator. Omitted if unavailable.</ResponseField>
        <ResponseField name="withheldInCountries" type="string[]">Country codes where the account is withheld. Omitted if empty.</ResponseField>
        <ResponseField name="possiblySensitive" type="boolean">Whether the account is flagged as possibly sensitive. Omitted if unavailable.</ResponseField>
        <ResponseField name="pinnedTweetIds" type="string[]">IDs of pinned tweets. Omitted if none.</ResponseField>
        <ResponseField name="isAutomated" type="boolean">Whether the account is marked as automated. Omitted if unavailable.</ResponseField>
        <ResponseField name="automatedBy" type="string">Username of the account operator if automated. Omitted if not automated.</ResponseField>
        <ResponseField name="unavailable" type="boolean">Whether the account is unavailable. Omitted if available.</ResponseField>
        <ResponseField name="unavailableReason" type="string">Reason the account is unavailable. Omitted if available.</ResponseField>
        <ResponseField name="verifiedType" type="string">Verification type (e.g. `Business`, `Government`). Omitted if not verified or standard blue check.</ResponseField>
        <ResponseField name="profile_bio" type="object">Structured profile bio with entity annotations. Omitted if unavailable.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="has_next_page" type="boolean">
      Whether more results are available.
    </ResponseField>

    <ResponseField name="next_cursor" type="string">
      Opaque cursor for the next page. Empty string when no more results.
    </ResponseField>

    ```json theme={null}
    {
      "users": [
        {
          "id": "987654321",
          "username": "xquikcom",
          "name": "Xquik",
          "followers": 10000,
          "verified": true
        }
      ],
      "has_next_page": true,
      "next_cursor": "DAADDAABCgABF..."
    }
    ```
  </Tab>

  <Tab title="400 Invalid tweet ID">
    ```json theme={null}
    { "error": "invalid_tweet_id" }
    ```

    The tweet ID is empty or invalid.
  </Tab>

  <Tab title="401 Unauthenticated">
    ```json theme={null}
    { "error": "unauthenticated" }
    ```

    Missing or invalid API key.
  </Tab>

  <Tab title="402 Subscription required">
    ```json theme={null}
    { "error": "no_subscription" }
    ```

    No active subscription or insufficient credits. Possible error values: `no_subscription`, `subscription_inactive`, `no_credits`, `insufficient_credits`.
  </Tab>

  <Tab title="502 X API unavailable">
    ```json theme={null}
    { "error": "x_api_unavailable" }
    ```

    The read service returned an error. Retry after a short delay.
  </Tab>

  <Tab title="429 Rate Limit Exceeded">
    ```json theme={null}
    { "error": "rate_limit_exceeded", "retryAfter": 60 }
    ```

    Your tier rate limit was exceeded. Wait for the `Retry-After` header before retrying.
  </Tab>

  <Tab title="424 Dependency Failed">
    ```json theme={null}
    { "error": "x_api_unavailable" }
    ```

    The normalized v1 response contract can return 424 when the read service is unavailable.
  </Tab>
</Tabs>

<Note>
  **Related:** [Retweeters](/api-reference/x/retweeters) · [Quote tweets](/api-reference/x/tweet-quotes) · [Tweet replies](/api-reference/x/tweet-replies)
</Note>
