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

> Retrieve the home timeline from the authenticated X account with cursor-based pagination

<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
</Callout>

<Note>
  Requires a connected X account. Uses user-authenticated access.
</Note>

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

  # Page 2
  curl -G https://xquik.com/api/v1/x/timeline \
    --data-urlencode "cursor=abc123" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const baseUrl = "https://xquik.com/api/v1/x/timeline";
  const seenTweetIds = new Set();
  let pageCursor = "";

  for (let pageIndex = 0; pageIndex < 3; pageIndex += 1) {
    const params = new URLSearchParams();
    if (pageCursor !== "") params.set("cursor", pageCursor);
    if (seenTweetIds.size > 0) {
      params.set("seenTweetIds", Array.from(seenTweetIds).join(","));
    }

    const query = params.toString();
    const response = await fetch(query === "" ? baseUrl : `${baseUrl}?${query}`, {
      headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
    });
    const page = await response.json();
    if (!response.ok) throw new Error(JSON.stringify(page));

    const timelineRows = page.tweets.map((tweet) => {
      seenTweetIds.add(tweet.id);
      return {
        timeline_source: "home",
        tweet_id: tweet.id,
        tweet_url: tweet.url ?? null,
        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;
  }
  ```

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

  seen_tweet_ids = set()
  page_cursor = ""

  for page_index in range(3):
      params = {}
      if page_cursor:
          params["cursor"] = page_cursor
      if seen_tweet_ids:
          params["seenTweetIds"] = ",".join(sorted(seen_tweet_ids))

      response = requests.get(
          "https://xquik.com/api/v1/x/timeline",
          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"]:
          seen_tweet_ids.add(tweet["id"])
          timeline_row = {
              "timeline_source": "home",
              "tweet_id": tweet["id"],
              "tweet_url": tweet.get("url"),
              "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>

## Home timeline handoff

Use `GET /x/timeline` when an inbox, CRM, monitor seed job, or agent needs the
authenticated account's home feed. The examples write JSON Lines rows with
home timeline source, tweet ID, URL, text, author ID, username, display name,
follower count, verified state, profile image URL, reply context, engagement
counts, media URLs, `seenTweetIds`, and cursor fields. Store processed tweet
IDs and pass them as `seenTweetIds` with the last saved `next_cursor` to reduce
duplicates.

<CardGroup cols={2}>
  <Card title="Home feed rows" icon="home">
    Store one row per `tweets[]` item with `timeline_source: "home"` for the
    connected account.
  </Card>

  <Card title="Seen tweet dedupe" icon="list-checks">
    Add processed tweet IDs to `seenTweetIds` before requesting the next page.
  </Card>

  <Card title="Cursor checkpoint" 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="Account-scoped sync" icon="lock-keyhole">
    Keep home timeline rows in account-scoped inbox, CRM, monitor seed, or agent
    memory systems.
  </Card>
</CardGroup>

## Query parameters

<ParamField query="cursor" type="string">
  Pagination cursor. Pass the `next_cursor` value from the previous response to fetch the next page.
</ParamField>

<ParamField query="seenTweetIds" type="string">
  Comma-separated tweet IDs to exclude from results. Empty entries are ignored. Use this to avoid returning tweets the user has already seen.
</ParamField>

## Which timeline endpoint?

<CardGroup cols={2}>
  <Card title="Home timeline" icon="home">
    Use `GET /x/timeline` for the connected account's home feed.
  </Card>

  <Card title="Profile timeline" icon="user-round">
    Use [`GET /x/users/{id}/tweets`](/api-reference/x/user-tweets) for one
    public profile's timeline.
  </Card>

  <Card title="Mentions timeline" icon="at-sign">
    Use [`GET /x/users/{id}/mentions`](/api-reference/x/user-mentions) for
    public mentions of one account.
  </Card>

  <Card title="Saved tweets" icon="bookmark">
    Use [`GET /x/bookmarks`](/api-reference/x/bookmarks) for tweets the
    connected account saved.
  </Card>

  <Card title="Notifications" icon="bell">
    Use [`GET /x/notifications`](/api-reference/x/notifications) for compact
    inbox activity rows.
  </Card>

  <Card title="Monitor events" icon="radio">
    Use [`List events`](/api-reference/events/list) after account or keyword
    monitors have captured replayable webhook events.
  </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="tweets" type="object[]">
      Array of timeline tweets.

      <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. 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": "Timeline tweet content",
          "createdAt": "2026-02-24T10:00:00.000Z",
          "likeCount": 200,
          "retweetCount": 50,
          "replyCount": 15,
          "url": "https://x.com/user/status/1893456789012345678",
          "author": {
            "id": "44196397",
            "username": "elonmusk",
            "name": "Elon Musk",
            "followers": 150000000,
            "verified": true,
            "profilePicture": "https://pbs.twimg.com/profile_images/example.jpg"
          }
        }
      ],
      "has_next_page": true,
      "next_cursor": "DAADDAABCgABF..."
    }
    ```
  </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="429 Rate Limit Exceeded">
    ```json theme={null}
    {
      "error": "rate_limit_exceeded",
      "message": "Too many requests. Try again later.",
      "retryAfter": 60
    }
    ```

    The API key, user, or plan tier is sending requests too quickly. Respect the `Retry-After` header before retrying.
  </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="424 Dependency Failed">
    ```json theme={null}
    { "error": "x_api_unavailable" }
    ```

    Returned when you opt into the normalized v1 response contract and the read service is unavailable.
  </Tab>
</Tabs>

<Note>
  **Related:** [Notifications](/api-reference/x/notifications) · [Bookmarks](/api-reference/x/bookmarks)
</Note>
