Quick answer: batch known IDs, use profile timelines for one user, use tweet search for keywords, use home timeline for the connected account feed, and use extraction jobs for saved CSV/JSON/XLSX files.
Choose the smallest route
Known tweet IDs
Use
GET /api/v1/x/tweets?ids=... for up to 100 comma-separated tweet IDs in one request.Known user IDs
Use
GET /api/v1/x/users/batch?ids=... for up to 100 comma-separated user IDs in one request.One profile timeline
Use
GET /api/v1/x/users/{id}/tweets for one user’s profile timeline. Pass a username or numeric X user ID.Keyword or advanced search
Use
GET /api/v1/x/tweets/search for keywords, hashtags, operators, date filters, and advanced search pages.Authenticated home timeline
Use
GET /api/v1/x/timeline for the connected account’s home timeline. Pass cursor and optional seenTweetIds.Saved file export
Use
POST /api/v1/extractions/estimate, POST /api/v1/extractions, and GET /api/v1/extractions/{id}/export for CSV, JSON, or XLSX.Batch known IDs before looping
If your app already has tweet IDs or user IDs, batch them first. Store the IDs that return successfully, then request only missing records later.Match timeline intent
Profile timeline
GET /api/v1/x/users/{id}/tweets returns one user’s profile timeline. Use cursor, includeReplies, and includeParentTweet to control pages and reply context.Tweet search
GET /api/v1/x/tweets/search returns matching public posts. Use q, queryType, cursor, and limit for direct API pages.Home timeline
GET /api/v1/x/timeline returns the authenticated account’s home timeline. Use it for feed-style reads, not keyword search./x/users/{id}/tweets for one user’s profile timeline. Use /x/tweets/search for keyword or advanced search. Use /x/timeline for the authenticated home timeline.
Use extraction jobs for files
Use extraction jobs when a workflow needs saved results, repeatable export paths, or analyst-ready files.1
Estimate
Call
POST /api/v1/extractions/estimate with the same target and resultsLimit you plan to run.2
Create
Call
POST /api/v1/extractions, then store the returned job id, status, and poll path.3
Poll JSON
Call
GET /api/v1/extractions/{id} until the job is complete. Store hasMore and nextCursor.4
Export
Call
GET /api/v1/extractions/{id}/export?format=csv, format=json, or format=xlsx for file handoff.Store cursor checkpoints
Store the route, query, cursor, and returned page state together. Treat every cursor as opaque.next_cursor back as cursor. For stored extraction JSON pages, pass nextCursor as after. Do not decode or construct cursors manually. The normalized REST contract and API MCP use has_more and next_cursor, but each endpoint keeps its request cursor name. Use cursor for most X reads, after for events, draws, extractions, and radar, and afterCursor for drafts.
Guard high-volume page loops
High-volume clients must bound work and detect stalled pagination. A page-size parameter is an upper bound, not a completion guarantee.1
Bound the run
Set a maximum row count, page count, and elapsed time before the first request.
2
De-duplicate rows
Store each tweet or user by stable
id. Count unique rows, not array length across pages.3
Follow advancing cursors
Continue while the response says more data exists, even when a filtered page is empty.
4
Stop stalled pagination
Stop with a clear partial-result status if the next cursor is empty, unchanged, or already seen.
5
Persist after each page
Save the request cursor, next cursor, unique row count, and last stable ID before continuing.
Avoid unnecessary media work
For tweet posts, pass public image URLs or one public MP4 URL inmedia on POST /api/v1/x/tweets. Do not call POST /api/v1/x/media first when the tweet media is already public.
Use POST /api/v1/x/media when you need an uploaded mediaId for the one-item media_ids array on POST /api/v1/x/dm/{userId}.
Checklist
- Batch known tweet or user IDs before looping over single lookups.
- Store returned IDs, row counts,
has_next_page,next_cursor, and export paths. - Stop when a cursor is missing or repeats while the response still reports more data.
- Continue through empty filtered pages when the cursor advances.
- Use
resultsLimiton extraction estimates and create requests for bounded jobs. - Use monitors and signed webhooks when recurring checks should push events instead of polling every page.
- Keep public tweet media URLs and uploaded DM media IDs in separate handoffs.