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

# Search tweets

> Search tweets by keyword, hashtag, X query operator, Tweet ID, or X status URL and return paginated JSON tweet data for CRM, agents, or export handoff

<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. Large `limit` pulls are resumable: if `has_next_page` is `true`, pass
  `next_cursor` back as `cursor` with the same query, filters, `queryType`, and
  `limit`. If zero paid results are affordable, it returns
  `402 insufficient_credits`.
</Note>

<Info>
  Use Search Tweets for keyword, hashtag, operator, and filtered discovery. For
  exact lookup, send a Tweet ID or X status URL in `q` with no time params. For
  plain account timelines, call
  [Get user timeline](/api-reference/x/user-tweets)
  (`GET /x/users/{id}/tweets`). Date params append `since:` and `until:`
  search operators to `q`, so
  `q=from:username&sinceTime=2026-05-01&untilTime=2026-05-02` stays on search.
</Info>

<Callout icon="coins" color="#5c3327">
  **1 credit per tweet returned** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl -G https://xquik.com/api/v1/x/tweets/search \
    --data-urlencode "q=giveaway" \
    --data-urlencode "fromUser=xquikcom" \
    --data-urlencode "mediaType=media" \
    --data-urlencode "verifiedOnly=true" \
    --data-urlencode "queryType=Latest" \
    -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/tweets/search \
    --data-urlencode "q=giveaway" \
    --data-urlencode "fromUser=xquikcom" \
    --data-urlencode "mediaType=media" \
    --data-urlencode "cursor=abc123" \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const params = new URLSearchParams({
    q: "giveaway",
    fromUser: "xquikcom",
    mediaType: "media",
  });
  const response = await fetch(`https://xquik.com/api/v1/x/tweets/search?${params}`, {
    headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
  });
  const firstPage = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(firstPage));

  let page = firstPage;
  let pageCursor = "";
  for (let pageIndex = 0; pageIndex < 3; pageIndex += 1) {
    const searchRows = page.tweets.map((tweet) => ({
      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,
      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),
      query: params.get("q"),
      page_index: pageIndex,
      page_cursor: pageCursor,
      next_cursor: page.next_cursor,
      has_next_page: page.has_next_page,
    }));
    for (const row of searchRows) process.stdout.write(`${JSON.stringify(row)}\n`);

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

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

## Direct API handoff

Use `GET /x/tweets/search` when an app, queue worker, CRM enrichment job, or
agent needs the latest matching tweets without creating a stored extraction job.
It returns paginated JSON for live search pages and app ingestion. The examples
above write JSON Lines rows with tweet fields, author ID, username, display
name, follower count, verified state, profile image URL, media, and cursor
fields so a worker can resume from the last saved `next_cursor`.

Use [`tweet_search_extractor`](/guides/tweet-search-export) instead when a team
needs an estimate, extraction ID, saved result pages, or CSV, JSON, and XLSX
downloads after completion.

<CardGroup cols={2}>
  <Card title="Live search page" icon="search">
    Call `GET /x/tweets/search` with `q`, filters, `queryType`, `limit`, and
    `cursor` for low-latency JSON rows.
  </Card>

  <Card title="Exact tweet lookup" icon="hash">
    Send a plain Tweet ID or X status URL in `q` when the source queue stores links.
  </Card>

  <Card title="Saved export job" icon="archive">
    Run `tweet_search_extractor` for estimates, job status, stored pages, and
    downloadable files.
  </Card>
</CardGroup>

For explicit `limit` pulls, treat `limit` as a batch-size upper bound. If the
response returns fewer tweets than `limit` and `has_next_page` is `true`, store
`next_cursor` and continue with the same `q`, structured filters, `queryType`,
and `limit` plus `cursor`.

For account date windows, `sinceTime` and `untilTime` append `since:` and
`until:` operators to `q`, so
`q=from:username&sinceTime=2026-05-01&untilTime=2026-05-02` behaves like
`from:username since:2026-05-01 until:2026-05-02`. Use `queryType=Latest` for
chronological backfills. Add keywords for ranked search.

For bounded `limit`, bare `q=from:username` with no time params is a user
timeline pull and returns one page with `has_next_page: false`. Add a search
term or use `fromUser` with a keyword for search pagination.

For exact lookups, a plain Tweet ID or X status URL in `q` returns that tweet
when available. Send no cursor on the first lookup; cursor requests return an
empty final page for exact IDs.

For CSV or XLSX output, project the returned `tweets[]` rows locally or use
[`tweet_search_extractor`](/guides/tweet-search-export) for saved CSV, JSON, or
XLSX files.

<CardGroup cols={2}>
  <Card title="Tweet rows" icon="rows-3">
    Store `tweets[]` as the matching tweet rows for app ingestion, analyst export, or retrieval.
  </Card>

  <Card title="Tweet keys" icon="key-round">
    Store `tweets[].id` as the stable tweet key for dedupe, CRM notes, queues, and follow-up lookups.
  </Card>

  <Card title="Search context" icon="message-square-text">
    Store `tweets[].text` and `tweets[].createdAt` for search hit context and time ordering.
  </Card>

  <Card title="Author joins" icon="user-round">
    Store `tweets[].author.id`, `tweets[].author.username`, `tweets[].author.name`,
    `tweets[].author.followers`, `tweets[].author.verified`, and
    `tweets[].author.profilePicture` for author joins and enrichment.
  </Card>

  <Card title="Scoring fields" icon="chart-no-axes-combined">
    Store engagement counts for scoring, routing, and prioritization.
  </Card>

  <Card title="Relationship context" icon="git-branch">
    Store `tweets[].media`, `quoted_tweet`, and `retweeted_tweet` to preserve attached media and relationship context when available.
  </Card>

  <Card title="Next page" icon="arrow-right">
    Store `has_next_page` and `next_cursor` as the cursor handoff. For bounded
    `limit` batches, keep the same query, filters, `queryType`, and `limit`
    when resuming.
  </Card>

  <Card title="File exports" icon="file-spreadsheet">
    Use `tweet_search_extractor` when the output must be saved CSV, JSON, or XLSX.
  </Card>
</CardGroup>

`limit` is an upper bound from 1 to 200 for a bounded request. Omit it for a
simple cursor-driven page loop. When a bounded request returns
`has_next_page: true`, continue by sending the same `q`, filters, `queryType`,
and `limit` with `cursor` set to `next_cursor`. This can happen when the route
returns a partial batch before reaching `limit`.

Tweet search costs 1 credit per tweet returned. Low credit balances can return fewer tweets than `limit`; zero affordable results return `402 insufficient_credits`. Retry `429` with the `Retry-After` header, and retry `424` or `502` after a short backoff.

## Query parameters

<ParamField query="q" type="string" required>
  Search query. Supports X search operators such as `from:username`, `to:username`, `#hashtag`, and boolean operators. A plain Tweet ID or X status URL returns the exact tweet when available.
</ParamField>

<ParamField query="queryType" type="string">
  Sort order for search results. `Top` returns most relevant tweets, `Latest` returns most recent. Defaults to `Latest`.
</ParamField>

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

<ParamField query="sinceTime" type="string">
  Filter tweets published after this date. Uses X search `since:` operator format (e.g. `2026-03-01`).
</ParamField>

<ParamField query="untilTime" type="string">
  Filter tweets published before this date. Uses X search `until:` operator format (e.g. `2026-03-27`).
</ParamField>

<ParamField query="limit" type="integer">
  Maximum number of tweets to collect in one bounded request (1-200). If the
  response includes `has_next_page: true`, keep the same `limit` when continuing
  with `cursor`.
</ParamField>

### Structured filters

Structured filters are part of the public Search Tweets API. Xquik converts
them into X search operators before fetching results, then filters returned rows
again when the response fields are available. Keep the same filters on every
cursor request.

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

### Search-only operators

These query parameters apply only to `GET /x/tweets/search` because they map to
search operators before the request runs. Use `advancedQuery` only when you
already have trusted raw X search operator syntax to append.

<ParamField query="listId" type="string">
  Search within this X List ID.
</ParamField>

<ParamField query="place" type="string">
  Search within this X place ID.
</ParamField>

<ParamField query="placeCountry" type="string">
  Search within this country code.
</ParamField>

<ParamField query="pointRadius" type="string">
  Geo point radius in X search syntax, such as `-73.99 40.73 25mi`.
</ParamField>

<ParamField query="boundingBox" type="string">
  Geo bounding box in X search syntax, such as `-74.1 40.6 -73.9 40.8`.
</ParamField>

<ParamField query="advancedQuery" type="string">
  Raw X search operators appended to the final search query.
</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 matching 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. 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. 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 labels. Omitted if unavailable.</ResponseField>

        <ResponseField name="author" type="object">
          Tweet author. Omitted if author data is 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="following" type="number">Following 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>
            <ResponseField name="coverPicture" type="string">Cover image URL. Omitted if unavailable.</ResponseField>
            <ResponseField name="description" type="string">Profile bio. Omitted if unavailable.</ResponseField>
            <ResponseField name="location" type="string">Profile location. Omitted if unavailable.</ResponseField>
            <ResponseField name="createdAt" type="string">Account creation date. Omitted if unavailable.</ResponseField>
            <ResponseField name="statusesCount" type="number">Total tweet count. Omitted if unavailable.</ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="media" type="object[]">
          Attached media items. Omitted when the tweet has no attached media.

          <Expandable title="media item">
            <ResponseField name="mediaUrl" type="string">Direct media URL (pbs.twimg.com).</ResponseField>
            <ResponseField name="type" type="string">Media type: `photo`, `video`, or `animated_gif`.</ResponseField>
            <ResponseField name="url" type="string">Shortened t.co URL from the tweet text.</ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="quoted_tweet" type="object">Embedded quoted tweet (same shape as tweet object). Omitted if not a quote tweet.</ResponseField>
        <ResponseField name="retweeted_tweet" type="object">Original retweeted tweet (same shape as tweet object). Omitted if not a retweet.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="has_next_page" type="boolean">
      Whether more results are available. Pass `next_cursor` to fetch the next page.
    </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": "Tweet content here",
          "createdAt": "2026-02-24T10:00:00.000Z",
          "likeCount": 150,
          "retweetCount": 42,
          "replyCount": 10,
          "quoteCount": 5,
          "viewCount": 12400,
          "bookmarkCount": 8,
          "url": "https://x.com/xquikcom/status/1893456789012345678",
          "lang": "en",
          "author": {
            "id": "987654321",
            "username": "xquikcom",
            "name": "Xquik",
            "followers": 10000,
            "verified": true,
            "profilePicture": "https://pbs.twimg.com/profile_images/xquik/photo.jpg"
          },
          "media": [
            {
              "mediaUrl": "https://pbs.twimg.com/media/example.jpg",
              "type": "photo",
              "url": "https://t.co/abc123"
            }
          ]
        }
      ],
      "has_next_page": true,
      "next_cursor": "DAADDAABCgABF..."
    }
    ```
  </Tab>

  <Tab title="400 Missing query">
    ```json theme={null}
    { "error": "missing_query" }
    ```

    The `q` query parameter is empty or missing.
  </Tab>

  <Tab title="401 Unauthenticated">
    ```json theme={null}
    { "error": "unauthenticated" }
    ```

    Missing or invalid API key. Check the `x-api-key` header value.
  </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>
  **Next steps:** [Tweet Search Export Workflow](/guides/tweet-search-export) when you need saved CSV, JSON, or XLSX files, [Get Tweet](/api-reference/x/get-tweet) to fetch full details for a specific tweet, or [Get User](/api-reference/x/get-user) to look up an author profile.
</Note>
