> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arrays.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Search X (Twitter) feed

> [OpenAPI JSON Spec](/docs/output/v1_social-feeds_x_search_get.json)
Full-text search over the tweet corpus via the TweetSearchService
(ESSearch — BM25 ranking on full_text + multilingual analyzers).
Currently only the registry index is searched; pass an optional
`handle` to restrict the search to a single author.

`q` is optional: when omitted, the endpoint returns the latest
tweets (sorted by published_at DESC) within the time window.

Anti-scrape rule: when both `q` and `handle` are empty, `since`
must be within the last 30 days (default 30d if omitted). Pass
either `q` or `handle` to widen the window back to the 10y default.

`sort` controls ordering: `relevance` (default, BM25 over
full_text), `latest` (reverse-chronological by published_at), or
`hottest` (by view_count, may be stale). Use `latest` for
recency/discovery so recent tweets are not buried under
higher-scoring older matches.

Returns HTTP 501 if the upstream search backend is not configured.

Update polling: `updated_after` + `updated_before` (Unix seconds,
inclusive after, exclusive before) return the tweets whose
`updated_at` moved inside the window, ordered by `updated_at` ASC
then `platform_id` ASC, paged by `pagination.cursor`. `updated_at`
records projection time for a first index or a change to engagement
counts, text, deleted flag, `has_media`, normalized media content,
or a `referenced_tweets[]` entry's `id`, `text` or `text_status`.
Identical observations and retries retain it; `last_observed_at`
advances on every re-observation and is not a substitute.
A window reaching past what the sync writer has confirmed searchable
is served up to that boundary rather than refused:
`pagination.indexed_through` (Unix seconds) reports how far the page
is complete, `has_more` stays true, and a `Retry-After` header gives
the wait. Page until `has_more` is false before advancing your own
checkpoint; a page carrying `indexed_through` is never drained.
Partial search results and an unreadable indexing checkpoint still
return HTTP 503; retry the same bounds and cursor.
The window may span at most 7 days. `sort` is ignored in this mode
and `offset` is rejected; `q`, `handle`, `content_type`, `has_media`,
`since` and `until` stay optional intersections and are bound into
the cursor, so send the cursor back unchanged with the same window
and the same filters. `limit` may change between pages. An omitted
cursor with `has_more=false` means the window is drained; start each
new window without the old cursor. An omitted cursor with
`has_more=true` means nothing was indexed yet in this window: ask
again for the same bounds with no cursor. This is current-state polling,
not a snapshot or a deletion feed: overlap your windows, dedupe by
`platform_id` on content, and expect intermediate versions to
coalesce. Deleted tweets are not surfaced through the window.



## OpenAPI

````yaml /api-reference/openapi.json get /v1/social-feeds/x/search
openapi: 3.0.0
info:
  contact: {}
  description: >-
    Welcome to Arrays!


    Arrays is a unified financial data layer for both financial institutions and
    retail users, delivering comprehensive market data across crypto and equity
    markets. Our datasets cover fundamentals, ETFs, options, crypto, and more.


    Visit [arrays.org](https://arrays.org) to request an API key and start your
    free trial. You can explore our full API offerings in the documentation
    below.


    For any questions regarding APIs or pricing, please contact
    support@arrays.org.
  title: Arrays API
  version: '1.0'
servers:
  - url: https://data-tools.prd.arrays.org/api
security: []
paths:
  /v1/social-feeds/x/search:
    get:
      tags:
        - x-feed
      summary: Search X (Twitter) feed
      description: |-
        [OpenAPI JSON Spec](/docs/output/v1_social-feeds_x_search_get.json)
        Full-text search over the tweet corpus via the TweetSearchService
        (ESSearch — BM25 ranking on full_text + multilingual analyzers).
        Currently only the registry index is searched; pass an optional
        `handle` to restrict the search to a single author.

        `q` is optional: when omitted, the endpoint returns the latest
        tweets (sorted by published_at DESC) within the time window.

        Anti-scrape rule: when both `q` and `handle` are empty, `since`
        must be within the last 30 days (default 30d if omitted). Pass
        either `q` or `handle` to widen the window back to the 10y default.

        `sort` controls ordering: `relevance` (default, BM25 over
        full_text), `latest` (reverse-chronological by published_at), or
        `hottest` (by view_count, may be stale). Use `latest` for
        recency/discovery so recent tweets are not buried under
        higher-scoring older matches.

        Returns HTTP 501 if the upstream search backend is not configured.

        Update polling: `updated_after` + `updated_before` (Unix seconds,
        inclusive after, exclusive before) return the tweets whose
        `updated_at` moved inside the window, ordered by `updated_at` ASC
        then `platform_id` ASC, paged by `pagination.cursor`. `updated_at`
        records projection time for a first index or a change to engagement
        counts, text, deleted flag, `has_media`, normalized media content,
        or a `referenced_tweets[]` entry's `id`, `text` or `text_status`.
        Identical observations and retries retain it; `last_observed_at`
        advances on every re-observation and is not a substitute.
        A window reaching past what the sync writer has confirmed searchable
        is served up to that boundary rather than refused:
        `pagination.indexed_through` (Unix seconds) reports how far the page
        is complete, `has_more` stays true, and a `Retry-After` header gives
        the wait. Page until `has_more` is false before advancing your own
        checkpoint; a page carrying `indexed_through` is never drained.
        Partial search results and an unreadable indexing checkpoint still
        return HTTP 503; retry the same bounds and cursor.
        The window may span at most 7 days. `sort` is ignored in this mode
        and `offset` is rejected; `q`, `handle`, `content_type`, `has_media`,
        `since` and `until` stay optional intersections and are bound into
        the cursor, so send the cursor back unchanged with the same window
        and the same filters. `limit` may change between pages. An omitted
        cursor with `has_more=false` means the window is drained; start each
        new window without the old cursor. An omitted cursor with
        `has_more=true` means nothing was indexed yet in this window: ask
        again for the same bounds with no cursor. This is current-state polling,
        not a snapshot or a deletion feed: overlap your windows, dedupe by
        `platform_id` on content, and expect intermediate versions to
        coalesce. Deleted tweets are not surfaced through the window.
      parameters:
        - description: >-
            Search query (BM25 over full_text; `#tag` and `@user` route to
            filter clauses). Omit for reverse-chronological listing.
          in: query
          name: q
          schema:
            type: string
        - description: >-
            Restrict to tweets authored by this Twitter handle (no @,
            case-insensitive)
          in: query
          name: handle
          schema:
            type: string
        - description: Start time (unix seconds)
          in: query
          name: since
          schema:
            type: integer
        - description: End time (unix seconds)
          in: query
          name: until
          schema:
            type: integer
        - description: Filter by content type
          in: query
          name: content_type
          style: form
          explode: false
          schema:
            type: array
            items:
              enum:
                - original
                - retweet
                - quote
                - reply
              type: string
        - description: Only return tweets with media when true
          in: query
          name: has_media
          schema:
            type: boolean
        - description: Max results (default 50, max 200)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: >-
            Result ordering: relevance (default, BM25), latest
            (reverse-chronological), or hottest (by view_count)
          in: query
          name: sort
          schema:
            type: string
            enum:
              - relevance
              - latest
              - hottest
        - description: Inclusive tweet-update bound (Unix seconds); requires updated_before
          in: query
          name: updated_after
          schema:
            type: integer
            format: int64
        - description: >-
            Exclusive tweet-update bound (Unix seconds); must be greater than
            updated_after and at most 7 days past it
          in: query
          name: updated_before
          schema:
            type: integer
            format: int64
        - description: Opaque update-window continuation; incompatible with offset
          in: query
          name: cursor
          schema:
            type: string
      responses:
        '200':
          description: Tweet list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/model.APIResponse'
                  - properties:
                      data:
                        items:
                          $ref: '#/components/schemas/model.XFeedTweet'
                        type: array
                    type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/model.APIResponse'
                  - properties:
                      error:
                        $ref: '#/components/schemas/model.APIError'
                    type: object
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/model.APIResponse'
                  - properties:
                      error:
                        $ref: '#/components/schemas/model.APIError'
                    type: object
        '501':
          description: Search backend not yet wired
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/model.APIResponse'
                  - properties:
                      error:
                        $ref: '#/components/schemas/model.APIError'
                    type: object
        '503':
          description: >-
            Update polling not available on this backend yet, or the indexing
            boundary cannot be read
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/model.APIResponse'
                  - properties:
                      error:
                        $ref: '#/components/schemas/model.APIError'
                    type: object
      security:
        - ApiKeyAuth: []
components:
  schemas:
    model.APIResponse:
      properties:
        data: {}
        error:
          $ref: '#/components/schemas/model.APIError'
        metadata: {}
        pagination: {}
        request_id:
          type: string
        success:
          type: boolean
      type: object
    model.XFeedTweet:
      properties:
        bookmark_count:
          example: 34
          type: integer
        content_type:
          example: original
          type: string
        conversation_id:
          example: '1799123456789'
          type: string
        display_name:
          example: Cathie Wood
          type: string
        entity_mentions:
          allOf:
            - $ref: '#/components/schemas/model.XFeedEntityMentions'
          description: EntityMentions：MVP 永远为空对象 / 空数组（PRD D7-13）；保留字段供 V2 填充。
        first_observed_at:
          example: '2025-04-25T12:00:00Z'
          type: string
        full_text:
          example: Disruptive innovation will continue.
          type: string
        hashtags:
          example:
            - AI
            - ETF
          items:
            type: string
          type: array
        in_reply_to_user_id:
          example: '44196397'
          type: string
        last_edited_at:
          example: '2025-04-25T12:40:00Z'
          type: string
        last_observed_at:
          example: '2025-04-25T12:40:00Z'
          type: string
        like_count:
          example: 1234
          type: integer
        media:
          items:
            $ref: '#/components/schemas/model.XFeedMedia'
          type: array
        media_json:
          type: string
        mentions:
          example:
            - elonmusk
          items:
            type: string
          type: array
        meta_json:
          description: |-
            MetaJSON / MediaJSON 透传 JSONB；客户端按需自行解析。
            Media 是 MediaJSON 解析后的结构化版本，便于直接消费。
          type: string
        platform_id:
          example: '1799123456789'
          type: string
        profile_image_url:
          description: >-
            ProfileImageURL is the AUTHOR's avatar, joined from the account
            profile

            at read time (tweets carry no avatar of their own). Only the
            internal

            /v1/alva/news-x path populates it; the public /social-feeds/x
            endpoints

            leave it empty — get the avatar from
            /social-feeds/x/entities/handle/{h}

            there. Also empty when the author has no stored profile.
          example: https://pbs.twimg.com/profile_images/1234/avatar_normal.jpg
          type: string
        published_at:
          example: '2025-04-25T12:34:56Z'
          type: string
        quote_count:
          description: >-
            Extended engagement + threading fields (Task 7).

            Pointer types parallel to the four counters above — nil means
            missing /

            unknown, &0 is a real zero. With value-type int64 + omitempty, the
            JSON

            encoder silently dropped genuine 0 values.
          example: 12
          type: integer
        referenced_tweet_id:
          description: >-
            Deprecated: use ReferencedTweets instead. Equals
            ReferencedTweets[0].ID /

            ReferencedTweets[0].Type for backward compat with arrays-skills
            v1.1.0 and

            earlier. Multi-ref tweets only expose the first ref here (X API
            ordering);

            use ReferencedTweets[] for the full picture. Will be removed in PR-b

            (~1 week after PR-a stabilizes, once arrays-skills v1.2+ no longer

            references these fields).
          example: '1799000000001'
          type: string
        referenced_tweet_type:
          example: quoted
          type: string
        referenced_tweets:
          description: |-
            ReferencedTweets replaces the legacy referenced_tweet_id/type pair.
            X API v2 allows a single tweet to reference multiple others.
          items:
            $ref: '#/components/schemas/model.TweetReference'
          type: array
        reply_count:
          example: 78
          type: integer
        retweet_count:
          example: 56
          type: integer
        source:
          allOf:
            - $ref: '#/components/schemas/model.XFeedTweet'
          description: >-
            Source is the nested source tweet for retweet/quote/reply. Always
            nil

            for content_type="original". Handler-hydrated via batch lookup in

            hydrateSources (see Task 14). Source.Source is naturally nil — the

            gRPC server doesn't populate it and we don't recurse hydration.
        twitter_handle:
          example: CathieDWood
          type: string
        updated_at:
          description: >-
            UpdatedAt is the projection time for the first index or a change to
            an

            engagement count, text, deleted flag, has_media, normalized media
            content,

            or a referenced_tweets[] entry's id, text or text_status.

            Identical observations and retries retain it; LastObservedAt is
            independent.

            Empty on documents indexed before the field existed and on the
            /by-handle

            and /by-url paths, which read ClickHouse.
          example: '2025-04-25T12:40:00.123456Z'
          type: string
        url:
          example: https://x.com/CathieDWood/status/1799123456789
          type: string
        view_count:
          example: 90000
          type: integer
      type: object
    model.APIError:
      properties:
        code:
          example: RESOURCE_NOT_FOUND
          type: string
        details:
          items:
            $ref: '#/components/schemas/model.APIErrorDetail'
          type: array
        docs_url:
          type: string
        examples:
          items:
            type: string
          type: array
        hint:
          type: string
        message:
          example: The requested resource was not found.
          type: string
        suggestions:
          items:
            type: string
          type: array
      type: object
    model.XFeedEntityMentions:
      properties:
        people:
          items:
            $ref: '#/components/schemas/model.XFeedPersonMention'
          type: array
        tickers:
          items:
            $ref: '#/components/schemas/model.XFeedTickerMention'
          type: array
        topics:
          items:
            $ref: '#/components/schemas/model.XFeedTopicMention'
          type: array
      type: object
    model.XFeedMedia:
      properties:
        alt_text:
          type: string
        duration_ms:
          example: 15000
          type: integer
        height:
          example: 800
          type: integer
        media_key:
          example: 3_1234
          type: string
        preview_image_url:
          type: string
        type:
          example: photo
          type: string
        url:
          example: https://pbs.twimg.com/media/foo.jpg
          type: string
        variants:
          items:
            $ref: '#/components/schemas/model.XFeedMediaVariant'
          type: array
        width:
          example: 1200
          type: integer
      type: object
    model.TweetReference:
      properties:
        author_display_name:
          example: Elon Musk
          type: string
        author_external_id:
          description: >-
            AuthorExternalID is the twitter_id of the referenced tweet's author,

            captured at ingestion time from X API's includes.tweets[]. Empty
            when

            unknown (legacy rows pre-backfill). The handler uses it to enable a

            PK-prefix fast path inside hydrateSources.
          example: '44196397'
          type: string
        author_handle:
          example: elonmusk
          type: string
        id:
          example: '1799000000001'
          type: string
        published_at:
          example: '2025-04-25T12:34:56Z'
          type: string
        text:
          type: string
        text_status:
          description: |-
            TextStatus says why Text is empty. /search path only, like Text:
              ok          the referenced tweet was indexed when this version was
                          written; Text is its full text
              pending     the reply-parent fetch was queued and not done when this
                          version was written; a newer version (new updated_at) with
                          the text comes only if this tweet is fetched again after
                          the parent lands
              not_found   the fetch ran and X has no live copy (deleted, protected or
                          suspended)
              not_fetched Arrays does not fetch this tweet (replies from accounts on
                          the reply-parent blocklist, parents more than one hop up,
                          quoted/retweeted tweets not returned inline)
            Absent on documents indexed before the field existed.
          enum:
            - ok
            - pending
            - not_found
            - not_fetched
          example: ok
          type: string
        type:
          description: '"retweeted" | "quoted" | "replied_to"'
          example: quoted
          type: string
      type: object
    model.APIErrorDetail:
      properties:
        field:
          type: string
        got:
          type: string
        reason:
          type: string
      type: object
    model.XFeedPersonMention:
      properties:
        confidence:
          type: number
        mention_method:
          type: string
        person_id:
          type: integer
      type: object
    model.XFeedTickerMention:
      properties:
        confidence:
          type: number
        mention_method:
          type: string
        ticker:
          type: string
      type: object
    model.XFeedTopicMention:
      properties:
        matched_keywords:
          items:
            type: string
          type: array
        topic_id:
          type: string
      type: object
    model.XFeedMediaVariant:
      properties:
        bit_rate:
          type: integer
        content_type:
          example: video/mp4
          type: string
        url:
          type: string
      type: object
  securitySchemes:
    ApiKeyAuth:
      description: 'API Key authentication. Example: "your-api-key-here"'
      in: header
      name: X-API-Key
      type: apiKey

````

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