> ## 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 user timeline

> Retrieve one user's X profile timeline with cursor-based pagination, engagement metrics, replies, and media

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

<Info>
  Get user timeline is the User Timeline API for a single public X profile.
  The same endpoint is also useful when you call it "user tweets," "profile
  timeline," or "X user timeline." Keep the canonical route as
  `GET /api/v1/x/users/{id}/tweets`.
</Info>

<CodeGroup>
  ```bash cURL theme={null}
  # Username profile timeline
  curl https://xquik.com/api/v1/x/users/elonmusk/tweets \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Numeric user ID profile timeline
  curl https://xquik.com/api/v1/x/users/44196397/tweets \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Replies and parent tweet context
  curl -G https://xquik.com/api/v1/x/users/elonmusk/tweets \
    --data-urlencode "includeReplies=true" \
    --data-urlencode "includeParentTweet=true" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq

  # Page 2 - pass next_cursor from the previous response
  curl -G https://xquik.com/api/v1/x/users/elonmusk/tweets \
    --data-urlencode "cursor=abc123" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const userIdOrUsername = "elonmusk";
  const response = await fetch(`https://xquik.com/api/v1/x/users/${userIdOrUsername}/tweets`, {
    headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
  });
  let page = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(page));

  let pageCursor = "";
  for (let pageIndex = 0; pageIndex < 3; pageIndex += 1) {
    const timelineRows = page.tweets.map((tweet) => ({
      source_user_id_or_username: userIdOrUsername,
      tweet_id: tweet.id,
      text: tweet.text,
      author_id: tweet.author?.id ?? null,
      author_username: tweet.author?.username ?? null,
      author_name: tweet.author?.name ?? null,
      author_followers: tweet.author?.followers ?? null,
      author_verified: tweet.author?.verified ?? null,
      author_profile_picture: tweet.author?.profilePicture ?? null,
      created_at: tweet.createdAt ?? null,
      is_reply: tweet.isReply ?? false,
      in_reply_to_id: tweet.inReplyToId ?? null,
      like_count: tweet.likeCount ?? null,
      reply_count: tweet.replyCount ?? null,
      retweet_count: tweet.retweetCount ?? null,
      quote_count: tweet.quoteCount ?? null,
      view_count: tweet.viewCount ?? null,
      media_urls: (tweet.media ?? []).map((item) => item.mediaUrl).filter(Boolean),
      page_index: pageIndex,
      page_cursor: pageCursor,
      next_cursor: page.next_cursor,
      has_next_page: page.has_next_page,
    }));
    for (const row of timelineRows) process.stdout.write(`${JSON.stringify(row)}\n`);

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

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

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

  user_id_or_username = "44196397"
  page_cursor = ""

  for page_index in range(3):
      params = {"cursor": page_cursor} if page_cursor else {}
      response = requests.get(
          f"https://xquik.com/api/v1/x/users/{user_id_or_username}/tweets",
          params=params,
          headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      )
      page = response.json()
      if not response.ok:
          raise RuntimeError(page)

      for tweet in page["tweets"]:
          timeline_row = {
              "source_user_id_or_username": user_id_or_username,
              "tweet_id": tweet["id"],
              "text": tweet["text"],
              "author_id": (tweet.get("author") or {}).get("id"),
              "author_username": (tweet.get("author") or {}).get("username"),
              "author_name": (tweet.get("author") or {}).get("name"),
              "author_followers": (tweet.get("author") or {}).get("followers"),
              "author_verified": (tweet.get("author") or {}).get("verified"),
              "author_profile_picture": (tweet.get("author") or {}).get("profilePicture"),
              "created_at": tweet.get("createdAt"),
              "is_reply": tweet.get("isReply", False),
              "in_reply_to_id": tweet.get("inReplyToId"),
              "like_count": tweet.get("likeCount"),
              "reply_count": tweet.get("replyCount"),
              "retweet_count": tweet.get("retweetCount"),
              "quote_count": tweet.get("quoteCount"),
              "view_count": tweet.get("viewCount"),
              "media_urls": [
                  item["mediaUrl"] for item in tweet.get("media", []) if item.get("mediaUrl")
              ],
              "page_index": page_index,
              "page_cursor": page_cursor,
              "next_cursor": page["next_cursor"],
              "has_next_page": page["has_next_page"],
          }
          print(json.dumps(timeline_row, separators=(",", ":")))

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

## User timeline handoff

Use `GET /x/users/{id}/tweets` when a CRM, queue worker, or warehouse job needs
one user's profile timeline. This endpoint accepts either a username or numeric
user ID and returns recent public posts from that profile.
The examples above write JSON Lines rows with the source profile, tweet ID,
text, author ID, username, display name, follower count, verified state, profile
image URL, reply context, engagement counts, media URLs, and cursor fields so a
worker can resume from the last saved `next_cursor`.

## Build a profile timeline job

Use these checkpoints when a timeline sync needs to switch between plain profile
posts, replies, media-only rows, and resumable page pulls.

<CardGroup cols={2}>
  <Card title="Original posts" icon="list">
    Omit `includeReplies` to fetch the profile timeline without replies.
  </Card>

  <Card title="Replies with context" icon="message-square">
    Set `includeReplies=true` and `includeParentTweet=true` when support,
    community, or research rows need the parent tweet context.
  </Card>

  <Card title="Media timeline" icon="image">
    Use a `mediaType` filter for filtered timeline rows, or switch to
    [`User media`](/api-reference/x/user-media) when every row should contain
    media.
  </Card>

  <Card title="Cursor checkpoint" icon="database">
    Store `page_cursor`, `next_cursor`, and `has_next_page` before requesting
    another page.
  </Card>
</CardGroup>

```json theme={null}
{
  "timeline_job_id": "profile-timeline-q2",
  "timeline_route": "GET /api/v1/x/users/{id}/tweets",
  "user_id_or_username": "elonmusk",
  "include_replies": true,
  "include_parent_tweet": true,
  "cursor_param": "cursor",
  "page_cursor": "",
  "next_cursor": "DAADDAABCgABF...",
  "has_next_page": true,
  "media_handoff_route": "GET /api/v1/x/users/{id}/media"
}
```

## Which timeline endpoint?

* Use `GET /api/v1/x/users/{id}/tweets` for one user's profile timeline. It
  returns original profile posts by default.
* Add `includeReplies=true` when the sync needs replies, and add
  `includeParentTweet=true` when reply rows need parent context.
* Use `GET /api/v1/x/users/{id}/replies` when every page should include replies
  by default.
* Use `GET /api/v1/x/users/{id}/media` when every returned row should contain
  profile media.
* Use `GET /api/v1/x/tweets/search` for keyword, operator, or advanced search.
* Use `GET /api/v1/x/timeline` for the authenticated account's home timeline.

## Path parameters

<ParamField path="id" type="string" required>
  X username or numeric user ID. Use a username such as `elonmusk` when the
  profile handle is known, or a numeric ID such as `44196397` when you store
  stable user IDs.
</ParamField>

## Query parameters

<ParamField query="cursor" type="string">
  Pagination cursor for the profile timeline. Omit it for the first page, then
  pass the `next_cursor` value from the previous response to fetch the next
  page.
</ParamField>

<ParamField query="includeReplies" type="boolean" default="false">
  Include reply tweets in the profile timeline. Defaults to `false`, which
  returns original profile posts without replies.
</ParamField>

<ParamField query="includeParentTweet" type="boolean" default="false">
  Include parent tweet context for returned replies. Defaults to `false`; set it
  to `true` when replies need conversation context.
</ParamField>

### Tweet result filters

These optional filters apply to `tweets[]` returned by this route. They keep the
same user target and filter rows after each page is fetched, so selective
filters can return fewer rows than an unfiltered page.

<ParamField query="fromUser" type="string">
  Filter to tweets authored by this username. The `@` prefix is optional.
</ParamField>

<ParamField query="toUser" type="string">
  Filter to replies directed to this username.
</ParamField>

<ParamField query="mentioning" type="string">
  Filter to tweets that mention this username.
</ParamField>

<ParamField query="language" type="string">
  Filter by tweet language code, such as `en`, `tr`, or `es`.
</ParamField>

<ParamField query="sinceDate" type="string">
  Filter to tweets created on or after this date or timestamp.
</ParamField>

<ParamField query="untilDate" type="string">
  Filter to tweets created before this date or timestamp. A `YYYY-MM-DD` value includes the whole day before the boundary.
</ParamField>

<ParamField query="mediaType" type="string">
  Filter by attached media or links. Values: `images`, `videos`, `gifs`, `media`, `links`, `none`.
</ParamField>

<ParamField query="minFaves" type="integer">
  Minimum like count.
</ParamField>

<ParamField query="minRetweets" type="integer">
  Minimum retweet count.
</ParamField>

<ParamField query="minReplies" type="integer">
  Minimum reply count.
</ParamField>

<ParamField query="minQuotes" type="integer">
  Minimum quote count.
</ParamField>

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

<ParamField query="replies" type="string">
  Reply mode. Values: `include`, `exclude`, `only`.
</ParamField>

<ParamField query="retweets" type="string">
  Retweet mode. Values: `include`, `exclude`, `only`.
</ParamField>

<ParamField query="quotes" type="string">
  Quote mode. Values: `include`, `exclude`, `only`.
</ParamField>

<ParamField query="exactPhrase" type="string">
  Exact text that must appear in the tweet.
</ParamField>

<ParamField query="excludeWords" type="string">
  Words or quoted phrases to exclude from returned tweets. Separate with spaces, commas, or lines.
</ParamField>

<ParamField query="anyWords" type="string">
  Words or quoted phrases where at least 1 term must appear in the tweet. Separate with spaces, commas, or lines.
</ParamField>

<ParamField query="hashtags" type="string">
  Hashtags to match. Separate with spaces, commas, or lines. The `#` prefix is optional.
</ParamField>

<ParamField query="cashtags" type="string">
  Cashtags to match. Separate with spaces, commas, or lines. The `$` prefix is optional.
</ParamField>

<ParamField query="url" type="string">
  URL substring or domain that must appear in tweet URL entities.
</ParamField>

<ParamField query="conversationId" type="string">
  Filter to tweets in this conversation thread.
</ParamField>

<ParamField query="inReplyToTweetId" type="string">
  Filter to replies to this tweet ID.
</ParamField>

<ParamField query="quotesOfTweetId" type="string">
  Filter to quote tweets of this tweet ID.
</ParamField>

<ParamField query="retweetsOfTweetId" type="string">
  Filter to retweets of this tweet ID.
</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="tweets" type="object[]">
      Array of tweets by the user.

      <Expandable title="tweet object">
        <ResponseField name="id" type="string">Tweet ID.</ResponseField>
        <ResponseField name="text" type="string">Tweet text content.</ResponseField>
        <ResponseField name="type" type="string">Tweet type. Omitted if unavailable.</ResponseField>
        <ResponseField name="createdAt" type="string">ISO 8601 creation timestamp. Omitted if unavailable.</ResponseField>
        <ResponseField name="isNoteTweet" type="boolean">Whether this is a Note Tweet (long-form post). 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="inReplyToId" type="string">Tweet ID being replied to. Omitted if not a reply.</ResponseField>
        <ResponseField name="inReplyToUserId" type="string">User ID being replied to. Omitted if not a reply.</ResponseField>
        <ResponseField name="inReplyToUsername" type="string">Username being replied to. Omitted if not a reply.</ResponseField>
        <ResponseField name="conversationId" type="string">Conversation thread ID. Omitted if unavailable.</ResponseField>
        <ResponseField name="source" type="string">Client used to post the tweet. Omitted if unavailable.</ResponseField>
        <ResponseField name="displayTextRange" type="number[]">Start and end offsets for rendered tweet text. Omitted if unavailable.</ResponseField>
        <ResponseField name="isLimitedReply" type="boolean">Whether replies are limited. Omitted if unavailable.</ResponseField>
        <ResponseField name="isQuoteStatus" type="boolean">Whether this tweet quotes another tweet. Omitted if unavailable.</ResponseField>
        <ResponseField name="entities" type="object">Parsed entities (URLs, hashtags, mentions). Omitted if unavailable.</ResponseField>
        <ResponseField name="contentDisclosure" type="object">Disclosure metadata for paid partnership and AI-generated media labels. Includes `advertising.isPaidPromotion` and `aiGenerated.hasAiGeneratedMedia` when X returns them. Omitted if unavailable.</ResponseField>

        <ResponseField name="author" type="object">
          Tweet author profile. Omitted if unavailable.

          <Expandable title="author object">
            <ResponseField name="id" type="string">Author user ID.</ResponseField>
            <ResponseField name="username" type="string">Author X username.</ResponseField>
            <ResponseField name="name" type="string">Author display name.</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">Profile picture URL. Omitted if unavailable.</ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="media" type="object[]">
          Media attachments. Omitted if unavailable.

          <Expandable title="media item">
            <ResponseField name="mediaUrl" type="string">Direct media URL.</ResponseField>
            <ResponseField name="type" type="string">Media type.</ResponseField>
            <ResponseField name="url" type="string">Shortened URL from the tweet text.</ResponseField>
          </Expandable>
        </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>
      </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}
    {
      "tweets": [
        {
          "id": "1893456789012345678",
          "text": "User's tweet content",
          "createdAt": "2026-02-24T10:00:00.000Z",
          "likeCount": 500,
          "retweetCount": 120,
          "replyCount": 45,
          "viewCount": 25000,
          "contentDisclosure": {
            "advertising": { "isPaidPromotion": true },
            "aiGenerated": {
              "detectionSource": "UserDeclared",
              "hasAiGeneratedMedia": true
            }
          },
          "url": "https://x.com/elonmusk/status/1893456789012345678",
          "author": {
            "id": "44196397",
            "username": "elonmusk",
            "name": "Elon Musk",
            "followers": 150000000,
            "verified": true,
            "profilePicture": "https://pbs.twimg.com/profile_images/example.jpg"
          },
          "media": [{ "type": "photo", "mediaUrl": "https://pbs.twimg.com/media/example.jpg" }]
        }
      ],
      "has_next_page": true,
      "next_cursor": "DAADDAABCgABF..."
    }
    ```
  </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." }
    ```

    The user ID is empty or invalid.
  </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" }
    ```

    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`.
    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:** [User replies timeline](/api-reference/x/user-replies) · [User media](/api-reference/x/user-media) · [User likes](/api-reference/x/user-likes) · [User mentions timeline](/api-reference/x/user-mentions)
</Note>
