Scrapes tweets from X (Twitter) by search query, user handle, or direct URL β returns full tweet data including text, author info, engagement metrics, media, and hashtags...
Search query / handle / URL β structured tweet list (text, author, metrics, media, entities)
All process output to user (progress updates, process notifications) follows the user's language.
Collect tweets matching a search query, from specific user profiles, or from direct URLs, extracting complete structured data for each tweet.
https://x.com/search?q=... or https://x.com/{handle} or a direct tweet/list URLIf browser-act has been confirmed available in the current session β skip this step.
Invoke browser-act via Skill tool to load usage. If installation or configuration issues arise, follow its guidance to resolve then retry.
If login status for X has been confirmed in the current session β skip this step.
Otherwise: open https://x.com and observe the left sidebar:
User refuses or cannot log in β terminate execution.
This Skill's operational boundary = what the user can manually do in their browser. It reads tweet data already rendered in the X DOM, never bypassing authentication. JS is encapsulated in
scripts/files, invoked viaeval "$(python scripts/xxx.py)". Use the bash tool for execution.
Extracts all currently visible tweets from the page via React internal state (React Fiber). Works on search pages, profile pages, list pages, and any X page rendering tweet articles.
Wait for tweets to appear before extracting:
wait --selector "article[data-testid='tweet']" --state attached --timeout 15000
Extract: eval "$(python scripts/extract-tweets.py)"
Returns a JSON array. Each element:
[
{
"id": "2059255862548738182", // tweet ID
"url": "https://x.com/NASA/status/2059255862548738182", // direct link
"text": "Full tweet text including hashtags and URLs", // full_text field
"created_at": "2026-05-26T12:50:31.000Z", // ISO 8601
"lang": "en", // ISO 639-1 language code, null if unknown
"author_id": "11348282", // author user ID
"author_name": "NASA", // display name
"author_screen_name": "NASA", // @handle (without @)
"author_profile_image": "https://pbs.twimg.com/profile_images/.../photo.jpg",
"author_followers": 92080161, // follower count
"author_following": 305, // following count
"author_verified": false, // legacy blue checkmark
"author_blue_verified": true, // X Blue / Gold / Gray checkmark
"author_location": "Washington, D.C.", // profile location, null if not set
"author_description": "Explore the universe...", // bio, null if empty
"like_count": 82579,
"retweet_count": 11952,
"reply_count": 4230,
"quote_count": 1850,
"bookmark_count": 12400,
"view_count": 25923006, // null if not available
"is_retweet": false,
"is_quote": false,
"is_reply": false,
"in_reply_to_tweet_id": null, // parent tweet ID if is_reply=true
"in_reply_to_user": null, // @handle of replied-to user
"conversation_id": "2059255862548738182",
"hashtags": ["AI", "Space"], // without #
"urls": ["https://example.com/article"], // expanded URLs from entities
"mentions": ["SpaceX", "ESA"], // @handles without @
"media": [
{
"type": "video", // "photo", "video", "animated_gif"
"url": "https://pbs.twimg.com/amplify_video_thumb/.../img/thumb.jpg",
"alt_text": null,
"video_variants": [
{"bitrate": 2176000, "url": "https://video.twimg.com/.../1280x720/video.mp4"},
{"bitrate": 832000, "url": "https://video.twimg.com/.../640x360/video.mp4"}
]
}
],
"source_name": "Twitter for iPhone", // client used to post
"source_url": "http://twitter.com/download/iphone"
}
]
searchTerms (keyword / advanced query):
https://x.com/search?q={url_encoded_query}&src=typed_query&f=livehttps://x.com/search?q={url_encoded_query}&src=typed_querytwitterHandles (scrape a user's profile tweets):
https://x.com/{handle} β shows all tweets/retweetsfrom:{handle} as the search query β more filter-compatiblestartUrls (direct URLs): navigate to the URL as-is. Supported types:
https://x.com/{user}/status/{id} β single tweet conversationhttps://x.com/{handle} β profile timelinehttps://x.com/search?q=... β use directlyhttps://x.com/i/lists/{list_id} β list timelineAppend these operators to the base query string (space-separated):
| Parameter | Query operator | Example |
|---|---|---|
tweetLanguage |
lang:{code} |
lang:en |
onlyVerifiedUsers |
filter:verified |
|
onlyTwitterBlue |
filter:blue_verified |
|
onlyImage |
filter:images |
|
onlyVideo |
filter:videos |
|
onlyQuote |
filter:quote |
|
author |
from:{handle} |
from:NASA |
inReplyTo |
to:{handle} |
to:NASA |
mentioning |
@{handle} |
@NASA |
minimumRetweets |
min_retweets:{n} |
min_retweets:100 |
minimumFavorites |
min_faves:{n} |
min_faves:500 |
minimumReplies |
min_replies:{n} |
min_replies:10 |
start |
since:{YYYY-MM-DD} |
since:2024-01-01 |
end |
until:{YYYY-MM-DD} |
until:2024-06-01 |
geotaggedNear + withinRadius |
near:"{location}" within:{radius} |
near:"New York" within:15mi |
geocode |
geocode:{lat},{lon},{radius} |
geocode:40.7,-74.0,10km |
-filter:retweets |
exclude retweets |
Example: Scrape English tweets from NASA since 2024 with β₯100 likes, excluding retweets:
query = "from:NASA lang:en since:2024-01-01 min_faves:100 -filter:retweets"
url = "https://x.com/search?q=" + encodeURIComponent(query) + "&src=typed_query&f=live"
DOM Pagination (scroll to load more):
X dynamically appends new tweet articles to the DOM as the user scrolls. Tweets already rendered remain in the DOM (no virtualization for typical result sets < ~500 tweets).
Loop pattern:
len(results)scroll down --amount 2000wait stablecollected >= max_items (if limit specified)Deduplication: track seen IDs in a Python set, filter before appending to output.
result count >= 1 and id field non-null rate = 100% and text field non-null rate = 100%
__reactFiber prefix includes a session-specific hash (e.g., __reactFiber$ozawbbp0gp). The script uses startsWith('__reactFiber') which is stable across deployments; only fails if React is replaced with a different framework.view_count is null for older tweets, low-engagement tweets, or when X doesn't expose it. This is a data availability issue, not an extraction failure.filter:blue_verified: Whether X's search engine respects this operator may vary; verify results if blue-verified filtering is critical.customMapFunction: Not applicable β this is a source-platform-specific JS transform parameter, not a data filter relevant to browser extraction.Path: {working-directory}/browser-act-skill-forge-memories/x-tweet-scraper-x-tweet-search.memory.md
Before execution: If the file exists, read it first β it records unexpected situations encountered during past executions (e.g., a strategy has become ineffective); adjust strategy order accordingly.
After execution: If an unexpected situation is encountered (strategy became ineffective, page redesigned, anti-scraping upgraded, better path discovered), append a line:
{YYYY-MM-DD}: {what happened} β {conclusion}
Normal execution does not write to the file. Do not record what keywords were used or how many results were returned β those are task outputs, not experience.