Skip to main content
An x_list Connector Instance polls the posts of one X list through the X API v2 and turns each post into a Record. Edited posts become corrections, and posts that are deleted or made protected within the recheck window are withdrawn. Read the overview first for what every kind shares: creation, credentials, health, disable.

What you bring

  • The x-list plugin pinned. x_list comes from the first-party connector plugin plugins/x-list. make dev, make verify and the Railway image pin it; elsewhere pin it. Coming from the former built-in kind: until it is pinned, instances report unsupported_connector_kind and keep their checkpoint, then resume without duplicates. The engine setting x.api_endpoint is gone; the pin’s configuration.api_endpoint replaces it, for test fakes only.
  • An X developer account and app. Create a Project and App in the X Developer Console. The account owner pays for API usage; check X’s pricing page and your console for current rates, deduplication rules and plan caps. Quivr makes no cost commitment.
  • An app-only bearer token. Deposit it as the credential. It must be able to read the list. Set expires_at if your organization rotates tokens on a schedule.
  • The consumer secret (optional). It is stored encrypted, and needed only for webhook mode, which receives posts in real time.
  • A list id. This is the numeric id in the list URL (https://x.com/i/lists/<list_id>). Use a public list, or a list the app’s token can read. An app-only token cannot read another account’s private list.

Create an instance

schedule.interval_seconds defaults to 120 s. The deployment floor is 30 s.

What is collected

  • Record Key is the id of the original post. An edited post keeps its Record: X gives each edit a new post id, but the connector keys every version by the first id in edit_history_tweet_ids.
  • Version content is the post text, or the full text of a long post, as the body Part.
  • Relations. Quoted, replied-to and reposted posts become Relations (quotes, replies_to, reposts) to Records of the same Source Namespace. They resolve only if that post was collected too.
  • The connector.x_list extension (v1) records the post id, edit history, author (id, username, name), created_at, lang, conversation_id, entities (URLs, mentions, hashtags), referenced posts, media references (key, type, URL, preview URL, alt text) and the post URL. Media files are not downloaded.
  • Edits. An edit becomes a new Record Version (a correction). The Source Position is the newest version’s id, so an older version can never replace a newer one.

How polling works

The X list endpoint has no since_id. Each run therefore reads the list newest first and stops at the newest post of the previous completed pass (the watermark) or at the start point. A run reads at most 10 pages of up to 100 posts. A longer backlog continues in the next run from the saved position, and the watermark only advances once the whole backlog is read. The checkpoint is saved only after each page is accepted, so a restart never loses or duplicates posts. X’s list endpoint may not reach far back, so a large backfill can be partial.

Deletion and protection

X’s developer policy requires stored content to be removed once it is deleted or made protected on X. The connector checks recent posts by id (GET /2/tweets?ids=, 100 per request):
  • Deleted, protected, suspended or otherwise unavailable posts are withdrawn (Tombstone, reason source_withdrawn), which removes them from search and retrieval.
  • Only posts within recheck_window_seconds of their creation are checked. The check runs every recheck_interval_seconds. A deletion is reflected after at most about the recheck interval plus the polling interval (≈ 12 minutes with the defaults).
  • At most 2,000 posts are tracked. If more arrive within the window, the oldest leave the recheck early. health.diagnostics.recheck_dropped_posts counts them.
  • A withdrawn Record stays withdrawn. If the author later makes the post public again, it is not re-collected; the attempt shows as item_rejected.
Known limitation: deletions older than the recheck window are not detected. The policy’s removal obligation applies whatever the post’s age, including within 24 hours of a removal request from X or the author. This connector does not yet cover posts older than recheck_window_seconds. If you must honour those requests, remove the Records manually (POST /v0/records/withdrawals) until long-tail compliance support ships. Raising the window to its 7-day maximum narrows the gap but raises read costs.
health.diagnostics shows the current coverage:

Interval and spend

X bills reads per post returned, not per request, and deduplicates repeated reads of the same post within a UTC day (a “soft guarantee”). Roughly, the posts read per UTC day are:
  • New posts. Every new post in the list is read once.
  • The top page. It is re-read on each poll but deduplicated, so it costs at most about 100 extra posts per UTC day.
  • Rechecks. Each post is read at most once more on each UTC day it stays in the window. That is about one extra read per post with a 24 h window, and more with longer windows.
A shorter interval lowers latency. Because of the per-day deduplication, it does not multiply cost. Controls:
  • health.usage. items_read estimates today’s billed post reads (UTC) and previous_day_items_read yesterday’s. It counts each post once per UTC day, as X deduplicates: the day’s first list page, new posts, and the first recheck of a post each day. X’s deduplication is a soft guarantee, so compare with the console when the numbers matter.
  • max_reads_per_day. Once reached, polling for new posts stops until the next UTC day, and last_error.code is daily_read_cap_reached while health stays healthy. Deletion rechecks continue, because compliance comes first. A single request can overshoot the cap by up to 100 posts.
  • The spend limit in the X Developer Console. It is the hard stop; when it is reached, X answers 402 (below).

Health codes

access_error lasts until a later successful poll.

Troubleshooting

  • Nothing is collected.
    • Without backfill_since, only posts created after the first poll are collected.
    • Check that last_success_at advances and that health.usage.items_read grows.
  • An edited post shows the old text. The correction appears on the next poll after the edit. Check the Record’s current Version.
  • A post deleted on X is still searchable.
    • Check health.diagnostics.last_recheck_at and whether the post is older than recheck_window_seconds (see the known limitation above).
    • If recheck_dropped_posts is non-zero, the list produced more than 2,000 posts within the window.
  • Costs are higher than expected. Compare health.usage with the console, lower the recheck window, or set max_reads_per_day.