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

> Get an X account's followers by username or user ID with cursor pagination for CRM, warehouse, audience, and agent workflows

<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 returned** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit · Accepts [MPP](/mpp/overview)
</Callout>

<Info>
  Get followers returns follower profiles for one X account by username or numeric user ID. It is also useful as a Follower Export API, X followers API, or Twitter followers API. The canonical endpoint remains `GET /api/v1/x/users/{id}/followers`.
</Info>

<CodeGroup>
  ```bash cURL theme={null}
  # Username follower page
  curl "https://xquik.com/api/v1/x/users/xquikcom/followers?pageSize=200" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Numeric user ID follower page
  curl "https://xquik.com/api/v1/x/users/44196397/followers?pageSize=200" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Resume with next_cursor from the previous page
  curl -G "https://xquik.com/api/v1/x/users/xquikcom/followers" \
    --data-urlencode "cursor=DAACCgACGE..." \
    --data-urlencode "pageSize=200" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const userIdOrUsername = "xquikcom";
  let pageCursor = "";

  for (let pageIndex = 0; pageIndex < 3; pageIndex += 1) {
    const params = new URLSearchParams({ pageSize: "200" });
    if (pageCursor !== "") params.set("cursor", pageCursor);

    const response = await fetch(
      `https://xquik.com/api/v1/x/users/${userIdOrUsername}/followers?${params}`,
      { headers: { "x-api-key": "xq_YOUR_KEY_HERE" } },
    );
    const page = await response.json();
    if (!response.ok) throw new Error(JSON.stringify(page));

    const importRows = page.users.map((user) => ({
      source_user_id_or_username: userIdOrUsername,
      x_user_id: user.id,
      x_username: user.username,
      display_name: user.name,
      description: user.description ?? null,
      location: user.location ?? null,
      website_url: user.url ?? null,
      follower_count: user.followers ?? null,
      following_count: user.following ?? null,
      verified: user.verified ?? false,
      verified_type: user.verifiedType ?? null,
      profile_picture_url: user.profilePicture ?? null,
      cover_picture_url: user.coverPicture ?? null,
      account_created_at: user.createdAt ?? null,
      statuses_count: user.statusesCount ?? null,
      can_dm: user.canDm ?? null,
      page_index: pageIndex,
      page_cursor: pageCursor,
      next_cursor: page.next_cursor,
      has_next_page: page.has_next_page,
    }));
    for (const row of importRows) process.stdout.write(`${JSON.stringify(row)}\n`);

    if (!page.has_next_page || page.next_cursor === "") break;
    pageCursor = page.next_cursor;
  }
  ```

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

  user_id_or_username = "44196397"
  page_cursor = ""

  for page_index in range(3):
      params = {"pageSize": "200"}
      if page_cursor:
          params["cursor"] = page_cursor

      response = requests.get(
          f"https://xquik.com/api/v1/x/users/{user_id_or_username}/followers",
          params=params,
          headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      )
      page = response.json()
      if not response.ok:
          raise RuntimeError(page)

      for user in page["users"]:
          import_row = {
              "source_user_id_or_username": user_id_or_username,
              "x_user_id": user["id"],
              "x_username": user["username"],
              "display_name": user["name"],
              "description": user.get("description"),
              "location": user.get("location"),
              "website_url": user.get("url"),
              "follower_count": user.get("followers"),
              "following_count": user.get("following"),
              "verified": user.get("verified", False),
              "verified_type": user.get("verifiedType"),
              "profile_picture_url": user.get("profilePicture"),
              "cover_picture_url": user.get("coverPicture"),
              "account_created_at": user.get("createdAt"),
              "statuses_count": user.get("statusesCount"),
              "can_dm": user.get("canDm"),
              "page_index": page_index,
              "page_cursor": page_cursor,
              "next_cursor": page["next_cursor"],
              "has_next_page": page["has_next_page"],
          }
          print(json.dumps(import_row, separators=(",", ":")))

      if not page["has_next_page"] or not page["next_cursor"]:
          break
      page_cursor = page["next_cursor"]
  ```
</CodeGroup>

The Node.js and Python snippets write JSON Lines import rows instead of raw
follower pages. Persist each mapped row and the latest `next_cursor` in your
sync job so it can resume from the last completed page.

## Direct follower handoff

Use `GET /api/v1/x/users/{id}/followers` when a CRM, warehouse, audience, or
agent workflow needs follower rows for one profile now. The examples above
write JSON Lines rows with `source_user_id_or_username`, `x_user_id`,
`x_username`, `display_name`, profile enrichment, segmentation fields,
`page_index`, `page_cursor`, `next_cursor`, and `has_next_page` for imports or
upserts. Use
[`follower_explorer`](/guides/follower-export-crm) when you need an estimated
job, saved extraction, or CSV/JSON/XLSX file export.

## Choose live API or saved export

Use this endpoint for current JSON pages when an app, queue, or agent can store
`next_cursor` and process `users[]` immediately. Use `follower_explorer` when
the job needs a cost estimate, reusable extraction ID, stored result pages, or
CSV/JSON/XLSX files after completion.

<CardGroup cols={2}>
  <Card title="Live page" icon="zap">
    Call `GET /x/users/{id}/followers` with `pageSize` and `cursor` for
    low-latency imports, enrichment, queues, or agent handoffs.
  </Card>

  <Card title="Saved export" icon="archive">
    Run `follower_explorer` when operators need estimates, job status,
    paginated saved rows, or file downloads.
  </Card>
</CardGroup>

<CardGroup cols={2}>
  <Card title="Follower rows" icon="rows-3">
    Store `users[]` as the follower profile rows returned on this page.
  </Card>

  <Card title="Stable upserts" icon="key-round">
    Store `users[].id` as `x_user_id` for CRM, warehouse, audience, and agent dedupe.
  </Card>

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

  <Card title="Profile enrichment" icon="file-text">
    Store `users[].description`, `location`, and `url` when returned. Empty profile fields are omitted.
  </Card>

  <Card title="Segmentation inputs" icon="sliders-horizontal">
    Store `users[].followers`, `users[].following`, `verified`, and `verifiedType` for filters and scoring.
  </Card>

  <Card title="Profile media" icon="image">
    Store `users[].profilePicture` and `coverPicture` for enrichment, review queues, or profile previews.
  </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 the requested `pageSize`, for row counts and budget checks. Low balances can return fewer rows.
  </Card>
</CardGroup>

`pageSize` from 20 to 200 is an upper bound. Paid authenticated calls can return fewer rows when the remaining credit balance is lower than the requested page size. Treat the returned `users.length` as the billable row count for that page. A balance that cannot afford any result returns `402 insufficient_credits`. For MPP callers, this endpoint is billed as a session at USD 0.00015 per user returned.

## Which follower endpoint?

* Use `GET /api/v1/x/users/{id}/followers` for one account's live follower page.
* Use [`follower_explorer`](/guides/follower-export-crm) when you need a saved job, CSV, JSON, or XLSX export.
* Use `GET /api/v1/x/users/{id}/following` for accounts the user follows.
* Use `GET /api/v1/x/users/{id}/verified-followers` when you only need verified followers.

## Path parameters

<ParamField path="id" type="string" required>
  Username or numeric user ID. For example, use `xquikcom` or `44196397`.
</ParamField>

## Query parameters

<ParamField query="cursor" type="string">
  Opaque pagination cursor from `next_cursor` in the previous response. Omit for the first page.
</ParamField>

<ParamField query="after" type="string">
  Legacy cursor alias for `cursor`. When both are present, `cursor` wins.
</ParamField>

<ParamField query="pageSize" type="number">
  Results per page. Range: 20-200. Default: `200`. Remaining credits can reduce the returned row count.
</ParamField>

<ParamField query="limit" type="number">
  Legacy page size alias. Use `pageSize`; when both are present, `pageSize` wins.
</ParamField>

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

      <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. Omitted if empty.</ResponseField>
        <ResponseField name="followers" type="number">Follower count.</ResponseField>
        <ResponseField name="following" type="number">Following count.</ResponseField>
        <ResponseField name="verified" type="boolean">Whether the user is verified.</ResponseField>
        <ResponseField name="profilePicture" type="string">Profile picture URL.</ResponseField>
        <ResponseField name="location" type="string">Profile location. Omitted if empty.</ResponseField>
        <ResponseField name="createdAt" type="string">ISO 8601 account creation timestamp.</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">Cursor for the next page.</ResponseField>

    ```json theme={null}
    {
      "users": [
        {
          "id": "987654321",
          "username": "xquikcom",
          "name": "Xquik",
          "followers": 10000,
          "following": 500,
          "verified": true,
          "profilePicture": "https://pbs.twimg.com/profile_images/xquik/photo.jpg",
          "description": "All-in-one X automation platform"
        }
      ],
      "has_next_page": true,
      "next_cursor": "DAACCgACGE..."
    }
    ```
  </Tab>

  <Tab title="400 Invalid user ID">
    ```json theme={null}
    { "error": "invalid_user_id", "message": "User not found or invalid user ID. Check the username or ID." }
    ```
  </Tab>

  <Tab title="404 User not found">
    ```json theme={null}
    { "error": "user_not_found", "message": "X user not found. Check the username." }
    ```
  </Tab>

  <Tab title="401 Unauthenticated">
    ```json theme={null}
    { "error": "unauthenticated" }
    ```
  </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`.

    For [MPP](/mpp/overview) requests without a valid payment credential, 402 returns a `WWW-Authenticate: Payment` challenge header instead.
  </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:** [Follower Export CRM Workflow](/guides/follower-export-crm) for saved CSV, JSON, or XLSX files for imports or upserts, [Following](/api-reference/x/following), [Verified Followers](/api-reference/x/verified-followers), and [Followers You Know](/api-reference/x/followers-you-know).
</Note>
