Install
openclaw skills install @berthelol/x-twitter-news-searchUse this skill to search Twitter/X for recent tweets matching keywords and engagement filters. Returns raw tweet data (text, author, likes, retweets, views, URL). Trigger when the user asks to "search twitter", "find tweets about", "check twitter for", "scan X for", "get tweets about", "what's on twitter about", or any request to fetch tweet data from Twitter/X. Also trigger when a cron job or another agent needs tweet data. This skill only fetches and filters — it does NOT score, summarize, rank, or format results. The calling agent decides what to do with the data.
openclaw skills install @berthelol/x-twitter-news-searchSearch Twitter/X for recent tweets matching keywords and engagement thresholds. Returns raw tweet data as JSON — the calling agent handles scoring, formatting, and delivery.
curl -s -G "https://api.x.com/2/tweets/search/recent" \
--data-urlencode 'query=("AI agent" OR "agentic AI") (lang:en) -is:reply -is:retweet' \
--data-urlencode "max_results=30" \
--data-urlencode "start_time=$(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ)" \
--data-urlencode "tweet.fields=created_at,public_metrics,author_id" \
--data-urlencode "user.fields=username,name" \
--data-urlencode "expansions=author_id" \
--data-urlencode "sort_order=relevancy" \
-H "Authorization: Bearer $TWITTER_BEARER_TOKEN" | jq .
The caller provides these. Use defaults when not specified.
| Parameter | Default | Description |
|---|---|---|
| keywords | (required) | Search terms, combined with OR |
| languages | ["en"] | Language codes for lang: filter |
| min_likes | 0 | Minimum like count (filtered after fetch) |
| min_retweets | 0 | Minimum retweet count (filtered after fetch) |
| lookback_hours | 24 | Time window |
| max_results | 30 | Tweets to return (after filtering) |
| exclude_replies | true | Add -is:reply to query |
| exclude_retweets | true | Add -is:retweet to query |
Verify $TWITTER_BEARER_TOKEN is set:
test -n "$TWITTER_BEARER_TOKEN" && echo "OK" || echo "MISSING"
If missing, tell the user:
hermes config set TWITTER_BEARER_TOKEN <token>Combine keywords with OR. Wrap multi-word phrases in quotes:
("keyword one" OR "keyword two" OR single) -is:reply -is:retweet
Add language filter: (lang:en OR lang:fr)
Available on pay-per-use: lang:, -is:reply, -is:retweet, from:, has:links, has:media
NOT available on pay-per-use: min_faves, min_retweets, since, until, -filter:replies — use API params and post-fetch filtering instead.
Use start_time as an API parameter for the time window:
curl -s -G "https://api.x.com/2/tweets/search/recent" \
--data-urlencode "query=QUERY_HERE" \
--data-urlencode "max_results=50" \
--data-urlencode "start_time=ISO_TIMESTAMP" \
--data-urlencode "tweet.fields=created_at,public_metrics,author_id" \
--data-urlencode "user.fields=username,name" \
--data-urlencode "expansions=author_id" \
--data-urlencode "sort_order=relevancy" \
-H "Authorization: Bearer $TWITTER_BEARER_TOKEN"
Generate start_time:
date -u -d "$N hours ago" +%Y-%m-%dT%H:%M:%SZdate -u -v-${N}H +%Y-%m-%dT%H:%M:%SZFetch 3x max_results to have enough after engagement filtering.
Parse with jq — see references/twitter-api.md for full response schema.
... | jq '[
.data[] as $t |
(.includes.users[] | select(.id == $t.author_id)) as $u |
{
id: $t.id,
text: $t.text,
author: $u.username,
author_name: $u.name,
likes: $t.public_metrics.like_count,
retweets: $t.public_metrics.retweet_count,
replies: $t.public_metrics.reply_count,
views: $t.public_metrics.impression_count,
created_at: $t.created_at,
url: ("https://x.com/" + $u.username + "/status/" + $t.id)
}
] | sort_by(-.likes)'
Filter by engagement:
... | jq --argjson ml 50 --argjson mr 5 '[.[] | select(.likes >= $ml and .retweets >= $mr)]'
Trim to max_results: | .[:30]
Return the filtered array as JSON. Do not score, summarize, or format — just the raw tweet objects.
[], not an error. Some queries have no matches.2026-04-28T00:00:00Zmin_faves/min_retweets operators are not available on pay-per-use.After fetching, confirm: