> ## 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 timeline API & latest tweets

> Read the newest tweets of up to 20 Twitter or X users in one request, with full text, authors, media, and engagement metrics. 1 credit per tweet returned.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-batch-user-tweets-200">
      ```json theme={null}
      {
        "tweets": [
          {
            "id": "1234567890",
            "text": "Just launched our new feature!",
            "createdAt": "2025-01-15T12:00:00Z",
            "likeCount": 42,
            "retweetCount": 5
          }
        ],
        "has_next_page": false,
        "next_cursor": "",
        "unavailable_ids": [
          "1234567890"
        ],
        "unprocessed_ids": []
      }
      ```
    </Tab>

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

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

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

    <Tab title="424" id="response-x-batch-user-tweets-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-user-tweets-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-user-tweets-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-user-tweets-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>

<Note>
  Repost records include `retweetedAt`, the repost event's UTC ISO 8601 timestamp. It is `null` when
  that timestamp is unavailable. The API omits it for original posts. The nested original post keeps
  its own creation date. This field does not report every account that reposted a post. [Request
  per-account timestamps with Get retweeters.](/api-reference/x/retweeters#retweet-timestamps)
</Note>

## When to use the batch timeline

Use this route to read the newest tweets of up to 20 users at once. It charges only for returned tweets. Use [Get user timeline](/api-reference/x/user-tweets) to page through 1 user's older tweets.

<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 tweet returned** · Accepts account credits and guest `paid_reads`
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://xquik.com/api/v1/x/users/batch/tweets?usernames=nasa,openai&pageSize=5" \
    -H "x-api-key: xq_your_api_key_here" | jq
  ```

  ```javascript Node.js theme={null}
  const usernames = ["nasa", "openai"];
  const params = new URLSearchParams({ usernames: usernames.join(","), pageSize: "5" });
  const response = await fetch(`https://xquik.com/api/v1/x/users/batch/tweets?${params}`, {
    headers: { "x-api-key": "xq_your_api_key_here" },
  });
  const data = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(data));

  const tweetRows = data.tweets.map((tweet) => ({
    tweet_id: tweet.id,
    text: tweet.text,
    author_id: tweet.author?.id ?? null,
    author_username: tweet.author?.username ?? null,
    created_at: tweet.createdAt ?? null,
    like_count: tweet.likeCount ?? null,
    is_pinned: tweet.isPinned ?? false,
  }));
  const sendAgain = data.unprocessed_ids;
  const skip = data.unavailable_ids;
  ```

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

  usernames = ["nasa", "openai"]
  response = requests.get(
      "https://xquik.com/api/v1/x/users/batch/tweets",
      params={"usernames": ",".join(usernames), "pageSize": 5},
      headers={"x-api-key": "xq_your_api_key_here"},
  )
  data = response.json()
  if not response.ok:
      raise RuntimeError(data)

  tweet_rows = [
      {
          "tweet_id": tweet["id"],
          "text": tweet["text"],
          "author_id": (tweet.get("author") or {}).get("id"),
          "author_username": (tweet.get("author") or {}).get("username"),
          "created_at": tweet.get("createdAt"),
          "like_count": tweet.get("likeCount"),
          "is_pinned": tweet.get("isPinned", False),
      }
      for tweet in data["tweets"]
  ]
  send_again = data["unprocessed_ids"]
  skip = data["unavailable_ids"]
  ```
</CodeGroup>

The Node.js and Python snippets build tweet rows. They do not print full
response pages. Send `unprocessed_ids` again. Skip `unavailable_ids`.

## Several timelines in 1 call

Tweets arrive user by user, in the order you named the users. Each user's
tweets are newest first, after the pinned tweet. Group rows by `author.id`.

A call returns up to 20 tweets per user. Set `pageSize` to return fewer.
A batch returns 1 page: `has_next_page` is `false` & `next_cursor` is empty.

| Result | Where it appears | What to do |
| - | - | - |
| Tweets were read | `tweets` | Store the rows. Each costs 1 credit. |
| No account on X | `unavailable_ids` | Skip the user. It costs nothing. |
| Protected tweets | `unavailable_ids` | Skip the user. It costs nothing. |
| The read failed | `unprocessed_ids` | Send the user again. It costs nothing. |
| The call ran out of time | `unprocessed_ids` | Send the user again. It costs nothing. |
| Credits ran out | `unprocessed_ids` | Add credits, then send the user again. |

A user with no tweets appears in neither list & adds no rows.

## Query parameters

<ParamField query="ids" type="string">
  Comma-separated numeric user IDs. Maximum 20 per request. Send `ids` or
  `usernames`, not both. A user named twice is read once.
</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 20 per request. A name in another case
  is the same account.
</ParamField>

<ParamField query="pageSize" type="integer">
  Tweets per user. Range: `1-20`. Defaults to `20`. Also read as `limit`, `count`,
  `max_results`, `maxItems`, `max_items` or `per_page`.
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Full account API key. Session cookie and OAuth authentication are also supported.
</ParamField>

<ParamField header="Authorization" type="string">
  Send `Bearer xq_your_guest_key_here` for an active `paid_reads` guest key.
</ParamField>

## Response

### 200 OK

<ResponseField name="tweets" type="object[]">
  Each user's newest tweets, in the order the users were named.
  **Tweet object fields.**

  <ResponseField name="id" type="string">Tweet ID.</ResponseField>
  <ResponseField name="text" type="string">Tweet text.</ResponseField>
  <ResponseField name="type" type="string">Tweet type. Omitted if unavailable.</ResponseField>
  <ResponseField name="createdAt" type="string">ISO 8601 creation timestamp.</ResponseField>
  <ResponseField name="isNoteTweet" type="boolean">Whether this is a Note Tweet. Omitted if unavailable.</ResponseField>
  <ResponseField name="isPinned" type="boolean">Whether the author pinned this post to their profile. Omitted if unavailable.</ResponseField>
  <ResponseField name="likeCount" type="number">Like count. Omitted if unavailable.</ResponseField>
  <ResponseField name="retweetCount" type="number">Retweet count. Omitted if unavailable.</ResponseField>
  <ResponseField name="replyCount" type="number">Reply count. Omitted if unavailable.</ResponseField>
  <ResponseField name="quoteCount" type="number">Quote tweet count. Omitted if unavailable.</ResponseField>
  <ResponseField name="viewCount" type="number">View count. Omitted if unavailable.</ResponseField>
  <ResponseField name="bookmarkCount" type="number">Bookmark count. Omitted if unavailable.</ResponseField>
  <ResponseField name="url" type="string">Permalink URL on X. Omitted if unavailable.</ResponseField>
  <ResponseField name="lang" type="string">Tweet language code. Omitted if unavailable.</ResponseField>
  <ResponseField name="isReply" type="boolean">Whether the tweet is a reply. Omitted if unavailable.</ResponseField>
  <ResponseField name="conversationId" type="string">Conversation thread ID. Omitted if unavailable.</ResponseField>
  <ResponseField name="isQuoteStatus" type="boolean">Whether this tweet quotes another tweet. Omitted if unavailable.</ResponseField>
  <ResponseField name="isRetweet" type="boolean">Whether this row is a retweet. `text` carries the original post in full.</ResponseField>
  <ResponseField name="entities" type="object">Parsed entities. Omitted if unavailable.</ResponseField>

  <ResponseField name="author" type="object">
    Tweet author profile. Omitted if unavailable.
    **Author object fields.**
    <ResponseField name="id" type="string">Author user ID.</ResponseField>
    <ResponseField name="username" type="string">Author handle without `@`.</ResponseField>
    <ResponseField name="name" type="string">Author display name. Omitted if unavailable.</ResponseField>
    <ResponseField name="followers" type="number">Follower count. Omitted if unavailable.</ResponseField>
    <ResponseField name="verified" type="boolean">Whether the author is verified. Omitted if unavailable.</ResponseField>
    <ResponseField name="profilePicture" type="string">Author profile image URL. Omitted if unavailable.</ResponseField>
  </ResponseField>

  <ResponseField name="media" type="object[]">
    Media attachments. Omitted when the tweet has no media.
    **Media object fields.**
    <ResponseField name="mediaUrl" type="string">Direct media URL.</ResponseField>
    <ResponseField name="videoVariants" type="object[]">Available video renditions with bitrate, content type, and URL. Omitted for images.</ResponseField>
    <ResponseField name="type" type="string">Media type.</ResponseField>
    <ResponseField name="url" type="string">Shortened URL from the tweet text.</ResponseField>
  </ResponseField>

  <ResponseField name="quoted_tweet" type="object">Embedded quoted tweet. Omitted if not a quote tweet.</ResponseField>
  <ResponseField name="retweeted_tweet" type="object">Original retweeted tweet. Omitted if not a retweet.</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[]">Users with no account on X or with protected tweets. They cost nothing. Skip them.</ResponseField>
<ResponseField name="unprocessed_ids" type="string[]">Users the call did not read. They cost nothing. Send them again.</ResponseField>

Both lists name each user as you sent it: an ID, or a username. A profile link is named by its username.

```json theme={null}
{
  "tweets": [
    {
      "id": "1893456789012345678",
      "text": "Hello world!",
      "createdAt": "2026-03-27T10:00:00.000Z",
      "likeCount": 42,
      "retweetCount": 5,
      "author": {
        "id": "9876543210",
        "username": "username",
        "name": "Xquik",
        "followers": 12000,
        "verified": true,
        "profilePicture": "https://pbs.twimg.com/profile_images/example.jpg"
      }
    }
  ],
  "has_next_page": false,
  "next_cursor": "",
  "unavailable_ids": ["1234567890"],
  "unprocessed_ids": []
}
```

### 400 Missing IDs

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

Send `ids` or `usernames`.

### 400 Too many IDs

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

### 400 Invalid user IDs

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

Each `ids` value must be a numeric user ID. Each `usernames` value must be a username or a link to a profile on x.com or twitter.com. Sending both `ids` & `usernames` also answers 400. 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

Full account keys can receive `no_subscription`, `subscription_inactive`, `no_credits`, or `insufficient_credits` with account payment options. Guest keys receive only the guest top-up action.

The failed request creates no checkout. Ask the user to confirm before calling any checkout or top-up route.

### 502 X API unavailable

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

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

### 503 No user could be read

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

No user's tweets could be read. The request costs nothing. Wait for the `Retry-After` header, then send it again.

### 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.** [Get user timeline](/api-reference/x/user-tweets) · [Batch Users](/api-reference/x/batch-users) · [Batch Tweets](/api-reference/x/batch-tweets)
</Note>

<div className="related-api-links">
  <Accordion title="Related tweet, reply & media APIs" icon="link">
    * Tweets: [Get tweet](/api-reference/x/get-tweet) · [Batch tweets](/api-reference/x/batch-tweets) · [Tweet thread](/api-reference/x/tweet-thread) · [Hidden replies](/api-reference/x/tweet-hidden-replies) · [Translate tweet](/api-reference/x/tweet-translation) · [Embed tweet](/api-reference/x/tweet-embed) · [Resolve links](/api-reference/x/resolve-links) · [Tweet subtitles](/api-reference/x/tweet-subtitles) · [X Article](/api-reference/x/get-article)
    * Engagement: [Tweet replies](/api-reference/x/tweet-replies) · [Quote tweets](/api-reference/x/tweet-quotes) · [Likers](/api-reference/x/favoriters) · [Reposters](/api-reference/x/retweeters) · [Check repost](/api-reference/x/tweet-repost-check)
    * Profiles: [User tweets](/api-reference/x/user-tweets) · [Batch user tweets](/api-reference/x/batch-user-tweets) · [User replies](/api-reference/x/user-replies) · [User likes](/api-reference/x/user-likes) · [User media](/api-reference/x/user-media) · [User highlights](/api-reference/x/user-highlights) · [User articles](/api-reference/x/user-articles)
    * Feeds: [List tweets](/api-reference/x/list-tweets) · [Trends](/api-reference/x/trends) · [Trend locations](/api-reference/x/trend-locations) · [Search Spaces](/api-reference/x/search-spaces) · [Get Space](/api-reference/x/get-space) · [Space replay](/api-reference/x/space-replay) · [Get broadcast](/api-reference/x/get-broadcast) · [Hashflags](/api-reference/x/hashflags) · [Search places](/api-reference/x/search-places) · [Download media](/api-reference/x/download-media)
  </Accordion>
</div>


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