> ## 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 batch user lookup API & profile details

> Retrieve up to 100 X user profiles by ID or username in one request, with bios, verification state, follower counts, and profile media. 1 credit per user.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-batch-users-200">
      ```json theme={null}
      {
        "users": [
          {
            "id": "9876543210",
            "username": "elonmusk",
            "name": "Elon Musk"
          }
        ],
        "has_next_page": false,
        "next_cursor": "",
        "requested_count": 2,
        "processed_count": 2,
        "returned_count": 1,
        "unavailable_ids": [
          "1234567890"
        ],
        "failed_ids": [],
        "unprocessed_ids": []
      }
      ```
    </Tab>

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

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

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

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

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

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

    <Tab title="503" id="response-x-batch-users-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>

<ParamField query="minFollowers" type="integer">
  Require this minimum follower count. Filtering happens before billing.
</ParamField>

<ParamField query="maxFollowers" type="integer">
  Allow this maximum follower count. Missing counts pass this filter.
</ParamField>

<ParamField query="minFollowing" type="integer">
  Require this minimum following count.
</ParamField>

<ParamField query="maxFollowing" type="integer">
  Allow this maximum following count. Missing counts pass this filter.
</ParamField>

<ParamField query="minStatuses" type="integer">
  Require this minimum post count.
</ParamField>

<ParamField query="maxStatuses" type="integer">
  Allow this maximum post count. Missing counts pass this filter.
</ParamField>

<ParamField query="minAccountAgeDays" type="integer">
  Require this minimum account age in days.
</ParamField>

<ParamField query="verifiedOnly" type="boolean">
  When `true`, only return verified profiles.
</ParamField>

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

<ParamField query="hasWebsite" type="boolean">
  When `true`, require a profile website.
</ParamField>

<ParamField query="hasLocation" type="boolean">
  When `true`, require a profile location.
</ParamField>

<ParamField query="bioContains" type="string">
  Require every comma-separated or line-separated bio term.
</ParamField>

<ParamField query="locationContains" type="string">
  Require this text in the profile location.
</ParamField>

<ParamField query="usernameContains" type="string">
  Require this text in the username.
</ParamField>

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

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://xquik.com/api/v1/x/users/batch?ids=44196397,987654321" \
    -H "x-api-key: xq_your_api_key_here" | jq
  ```

  ```javascript Node.js theme={null}
  const ids = ["44196397", "987654321"];
  const response = await fetch(`https://xquik.com/api/v1/x/users/batch?ids=${ids.join(",")}`, {
    headers: { "x-api-key": "xq_your_api_key_here" },
  });
  const data = await response.json();
  const profileRows = data.users.map((user) => ({
    user_id: user.id,
    username: user.username,
    display_name: user.name,
    bio: user.description ?? null,
    follower_count: user.followers ?? null,
    following_count: user.following ?? null,
    verified: user.verified ?? false,
    profile_image_url: user.profilePicture ?? null,
  }));
  const retryNow = data.failed_ids;
  const retryAfterCredits = data.unprocessed_ids;
  ```

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

  ids = ["44196397", "987654321"]
  response = requests.get(
      "https://xquik.com/api/v1/x/users/batch",
      params={"ids": ",".join(ids)},
      headers={"x-api-key": "xq_your_api_key_here"},
  )
  data = response.json()
  profile_rows = [
      {
          "user_id": user["id"],
          "username": user["username"],
          "display_name": user["name"],
          "bio": user.get("description"),
          "follower_count": user.get("followers"),
          "following_count": user.get("following"),
          "verified": user.get("verified", False),
          "profile_image_url": user.get("profilePicture"),
      }
      for user in data["users"]
  ]
  retry_now = data["failed_ids"]
  retry_after_credits = data["unprocessed_ids"]
  ```
</CodeGroup>

The Node.js and Python snippets build profile rows. They do not print
full profile objects. Store `profileRows` or `profile_rows` with the original
ID list. Retry `failed_ids` now. Retry `unprocessed_ids` after adding credits.
Skip `unavailable_ids`, which have no profile on X.

## Direct batch user handoff

Use `GET /x/users/batch` when a CRM, warehouse, enrichment, lead scoring, or
agent workflow already has numeric X user IDs. One JSON response returns the
profile details. Use [Get User](/api-reference/x/twitter-profile-lookup) when you have one ID
or username to resolve.

Store `requested_ids`, `user_id`, `username`, `display_name`, profile metrics,
verification state, `profile_image_url`, `has_next_page`, and `next_cursor`.
Join returned users by `user_id`. Do not rely on response order. Send at
most 100 IDs per request. Batch requests always return `has_next_page: false`
and `next_cursor: ""`. Zero affordable results return
`402 insufficient_credits`.

<CardGroup cols={2}>
  <Card title="Known IDs" icon="key-round">
    Send comma-separated X user IDs in `ids`. Keep the original list as
    `requested_ids` for retry and audit rows.
  </Card>

  <Card title="Profile rows" icon="user-round">
    Store each returned `id` as `user_id` with `username`, `name`,
    `description`, `followers`, `following`, `verified`, and `verifiedType`.
  </Card>

  <Card title="Missing rows" icon="triangle-alert">
    Retry `failed_ids` now & `unprocessed_ids` after adding credits. Skip
    `unavailable_ids`, which have no profile on X. With `usernames`, these
    lists name the usernames as sent, and a profile link by its username.
  </Card>

  <Card title="No pagination" icon="route">
    A batch returns one page: `has_next_page: false` and
    `next_cursor: ""`. Do not paginate it.
  </Card>
</CardGroup>

## Which lookup endpoint?

<CardGroup cols={2}>
  <Card title="One profile" icon="user-round">
    Use [`GET /x/users/{id}`](/api-reference/x/twitter-profile-lookup) for one username or one
    user ID.
  </Card>

  <Card title="Many known IDs" icon="key-round">
    Use `GET /x/users/batch` for up to 100 comma-separated user IDs,
    usernames, or profile links in one request.
  </Card>

  <Card title="Name or partial handle" icon="search">
    Use [`GET /x/users/search`](/api-reference/x/search-users) before batch
    lookup when the workflow starts from a name, brand, or handle fragment.
  </Card>

  <Card title="Tweet IDs" icon="list">
    Use [`GET /x/tweets?ids=`](/api-reference/x/batch-tweets) when the input
    list contains tweet IDs instead of user IDs.
  </Card>

  <Card title="Audience pages" icon="users">
    Use [`GET /x/users/{id}/followers`](/api-reference/x/followers) or
    [`GET /x/users/{id}/following`](/api-reference/x/following) when the job
    starts from an account audience.
  </Card>

  <Card title="Saved exports" icon="file-spreadsheet">
    Use [`Create extraction`](/api-reference/extractions/create) when the source
    is a follower, following, timeline, media, or search job instead of an
    existing ID list.
  </Card>
</CardGroup>

## Enrich a known set of user IDs

Use batch lookup after another workflow already identified exact profiles.
Examples include follower exports, tweet authors, community members, or CRM
records.

Prepare one deduplicated ID list. Keep the original source beside each ID.
After lookup, map every returned profile back to its source row.

Useful enrichment fields include:

* Current username and profile name.
* Biography, location, and verification state.
* Follower and following counts.
* Profile image and account creation time.

Mark missing IDs as missing. Do not shift remaining profiles into another
row's position. Join responses by numeric user ID.

Split oversized workloads into supported batches. Save each completed batch
before starting the next. A failed enrichment job can then resume from the last saved batch.

## Query parameters

<ParamField query="ids" type="string">
  Comma-separated numeric user IDs. Maximum 100 per request. Send `ids` or
  `usernames`, not both.
</ParamField>

<ParamField query="usernames" type="string">
  Comma-separated X usernames, with or without `@`, or profile links such as
  `x.com/nasa`, in place of `ids`. Maximum 100 per request. A name in another case
  is the same account. A link & a username for 1 account are read once.
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Full account key. Sessions and OAuth also work.
</ParamField>

<ParamField header="Authorization" type="string">
  `Bearer xq_your_guest_key_here` for `paid_reads`.
</ParamField>

## Response

### 200 OK

<ResponseField name="users" type="object[]">
  Array of user profiles matching the requested IDs.
  **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="accountBasedIn" type="object | null">
    Country or region X shows for the account, with `value`, `level` & `observedAt`. X infers it from
    account access. It states no nationality or exact location. `null` when X shows none. Omitted when
    X withholds it.
  </ResponseField>

  <ResponseField name="accountBasedInUnavailable" type="boolean">
    `true` when X withheld `accountBasedIn` from this read. Retry later to get it. Omitted otherwise.
  </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 or banner image URL. Omitted if unavailable.
  </ResponseField>

  <ResponseField name="mediaCount" type="number">
    Total number of media tweets posted. 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 X flags the account 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 X marks the account 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 (for example `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>

  <ResponseField name="isBlueVerified" type="boolean">
    Whether the account has X Premium verification. Omitted if unavailable.
  </ResponseField>

  <ResponseField name="isVerified" type="boolean">
    Normalized verification status. Omitted if unavailable.
  </ResponseField>

  <ResponseField name="profileBannerUrl" type="string">
    Profile banner URL. Omitted if unavailable.
  </ResponseField>

  <ResponseField name="protected" type="boolean">
    Whether the account protects its posts. Omitted if unavailable.
  </ResponseField>

  <ResponseField name="communityRole" type="string">
    Role within the requested community context. Omitted outside community results.
  </ResponseField>
</ResponseField>

<ResponseField name="has_next_page" type="boolean">Always `false` for batch requests.</ResponseField>
<ResponseField name="next_cursor" type="string">Always empty for batch requests.</ResponseField>
<ResponseField name="unavailable_ids" type="string[]">IDs with no profile on X. Skip them.</ResponseField>
<ResponseField name="failed_ids" type="string[]">IDs that failed to load. They cost nothing. Retry them.</ResponseField>
<ResponseField name="unprocessed_ids" type="string[]">IDs skipped for low credits. Retry them after adding credits.</ResponseField>

```json theme={null}
{
  "users": [
    {
      "id": "44196397",
      "username": "elonmusk",
      "name": "Elon Musk",
      "followers": 200000000,
      "verified": true
    }
  ],
  "has_next_page": false,
  "next_cursor": ""
}
```

### 400 Missing IDs

```json theme={null}
{ "error": "missing_ids", "message": "ids parameter required" }
```

### 400 Too many IDs

```json theme={null}
{ "error": "too_many_ids", "message": "Max 100 IDs per request" }
```

### 400 Invalid usernames

```json theme={null}
{
  "error": "invalid_user_ids",
  "message": "usernames must contain 1-100 X usernames or profile links such as x.com/nasa."
}
```

Each `usernames` value must be a username or a link to a profile on x.com or twitter.com.
A post link, a user ID link, another site, and the text `undefined` or `null` answer 400.
Send a user ID in `ids`. 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" }
```

### 402 Payment required

Account keys get account options. Guest keys get guest top-up only.
No checkout starts automatically. Confirm any payment action.

### 502 X API unavailable

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

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

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

<Note>
  **Related.** [Batch Tweets](/api-reference/x/batch-tweets) · [Get User](/api-reference/x/twitter-profile-lookup)
</Note>

<div className="related-api-links">
  <Accordion title="Related follower, list & community APIs" icon="link">
    * Profiles: [Search users](/api-reference/x/search-users) · [Search autocomplete](/api-reference/x/search-autocomplete) · [Get user](/api-reference/x/twitter-profile-lookup) · [Batch users](/api-reference/x/batch-users)
    * Followers: [Followers](/api-reference/x/followers) · [Following](/api-reference/x/following) · [Follower IDs](/api-reference/x/follower-ids) · [Following IDs](/api-reference/x/following-ids) · [Creator subscriptions](/api-reference/x/user-subscriptions) · [Affiliates](/api-reference/x/user-affiliates) · [Similar accounts](/api-reference/x/user-similar) · [Verified followers](/api-reference/x/verified-followers) · [Followers you know](/api-reference/x/followers-you-know) · [Check follower](/api-reference/x/check-follower)
    * Lists: [Search lists](/api-reference/x/search-lists) · [User lists](/api-reference/x/user-lists) · [List memberships](/api-reference/x/user-list-memberships) · [List members](/api-reference/x/list-members) · [List followers](/api-reference/x/list-followers)
    * Communities: [Find](/api-reference/x/community-find) · [Popular](/api-reference/x/community-popular) · [Topics](/api-reference/x/community-topics) · [Suggested](/api-reference/x/community-suggested) · [Details](/api-reference/x/community-info) · [Members](/api-reference/x/community-members) · [Moderators](/api-reference/x/community-moderators) · [Timeline](/api-reference/x/community-tweets) · [Media](/api-reference/x/community-media) · [Keyword search](/api-reference/x/community-search)
  </Accordion>
</div>


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