The last30days Skill and Twitter (X) Data: Install, Backends, Costs

This page is about one specific third-party skill, mvanhorn/last30days-skill, and the X (Twitter) half of it: how to install it, which X backend it picks, what each backend costs and misses, and how to hand it posts from TwitterAPI.io. If you want to know how a SKILL.md file works in general, or to install our own twitterapi-io skill so your agent can query X directly, read the SKILL.md explainer instead; if your agent is Nous Research's Hermes and you want its built-in X options rather than a research brief, the Hermes Agent guide covers that.
Everything below was read from the repository this session: the README, CONFIGURATION.md, HERMES_SETUP.md, SKILL.md and the X backend sources (x_api.py, bird_x.py, x_envelope.py). Config names and flags are quoted from those files, not from memory. We did not run a full /last30days research session; we did generate a real --x-posts file from TwitterAPI.io data and ran it through the repo's own validator, and we measured what a 30-day X window holds with our search endpoint.
What last30days is (and what it is not)
The README describes it as "an AI agent-led search engine scored by upvotes, likes, and real money - not editors." You type /last30days <topic>; the agent resolves the people, handles, subreddits and repos that matter, the Python engine searches every configured source in parallel, scores items by engagement, relevance and freshness, merges the same story across sources, and your host model writes the brief. Its example is a person you are meeting tomorrow: their recent posts, PRs, podcast transcripts and Reddit threads, gathered in one pass.
Facts from the repository. Owner mvanhorn, MIT license, created in January 2026, over 65,000 stars and 5,600 forks when we checked, latest release v3.30.0, Python 3.12+ plus Node.js for the vendored Bird X client. The SKILL.md frontmatter lists allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch and optional keys including X_BEARER_TOKEN, XAI_API_KEY, AUTH_TOKEN and CT0.
What it is not. It is not an X API, not a monitoring service and not a posting tool. It reads; the README points users who want to post or export followers to a separate OpenClaw plugin. Results land as Markdown in ~/Documents/Last30Days/ (LAST30DAYS_MEMORY_DIR), and --emit=json gives a versioned JSON profile for scripts.
How to install last30days on Claude Code, Codex, Grok, Hermes and OpenClaw
The README's install table, condensed. Use one install method per machine: the README notes that Claude Code does not dedupe the marketplace plugin and an npx skills copy, so /last30days would appear twice.
Hermes is the exception. HERMES_SETUP.md says hermes skills install is currently blocked: Hermes's install-time scanner flags the skill's reads of its own API keys and its subprocess calls to yt-dlp and bird as dangerous (19 findings), and --force cannot override that verdict for community sources. The supported path is a clone and copy into ~/.hermes/skills/research/, then hermes skills list and /reload-skills in an open session.
First run. The first /last30days call starts a setup wizard. It asks before reading browser cookies; on yes it saves which browser supplied them (FROM_BROWSER) and records BROWSER_CONSENT=true, never the cookie values. To preview what a run would read and write without researching anything, run python3 skills/last30days/scripts/last30days.py --preflight.
# Claude Code (inside a session)
/plugin marketplace add mvanhorn/last30days-skill
/plugin install last30days
# Codex (or Cursor, Gemini CLI, ...) via the Agent Skills CLI
npx skills add mvanhorn/last30days-skill -g -a codex
# Hermes: the installer is blocked, so clone and copy (from HERMES_SETUP.md)
git clone https://github.com/mvanhorn/last30days-skill.git
mkdir -p ~/.hermes/skills/research
cp -r last30days-skill/skills/last30days ~/.hermes/skills/research/
hermes skills list
# Then, in any host
/last30days AI coding agentsWhere last30days gets its X (Twitter) data: the backend chain
X has more backend options than any other source in the skill: six, and it picks one per run. From CONFIGURATION.md: "The default X backend chain is bird (browser cookies) -> xai (API key) -> xurl (OAuth2 CLI) -> xquik (API key). Cookies beat XAI_API_KEY when both are present." The official X API is deliberately opt-in "so an ambient bearer never spends X API credits when the free path comes back empty." A Grok CLI login is opt-in too, so a leftover ~/.grok/auth.json cannot take over the X lane.
Pinning. LAST30DAYS_X_BACKEND accepts bird, xai, xurl, xquik, grok or xapi and is exclusive: a pinned backend gets no failover. Run the skill's doctor (or --diagnose) to see which backend it "will use" and whether a key is working.
On Grok Bot the order flips. With LAST30DAYS_HOST=grok-bot the chain becomes the official one, xapi then xai then xurl, and before any of them the bot fetches X itself through its built-in X tools (free, nothing to set up), then the X for Grok Bot connector. Browser cookies are never read on that host.
How much X a run asks for. x_api.py sets 10 posts per query on --quick, 30 by default and 60 on --deep; the handle lanes add 8 posts from and 5 posts mentioning each named account. bird_x.py uses 12, 30 and 60. Each xapi search has a 90-second deadline and the handle lanes share a 150-second budget; per CONFIGURATION.md the engine's two X fetches share a budget of 12, 24 or 40 topic rows (quick, default, deep).
What each X path costs, and what a 7-day window misses
Prices first. The official X API pay-per-use rate card on docs.x.com/x-api/getting-started/pricing charges $0.005 per post read and $0.010 per user read, caps pay-per-use at 3 million post reads a month (Enterprise above), and gives $20 of credits on a first card purchase plus a match of the first auto-recharge up to $50. TwitterAPI.io charges 15 credits per tweet returned, $0.15 per 1,000 tweets, with a 15-credit minimum per request. Per 1,000 posts that is $5.00 against $0.15, about 33x.
What a default xapi run costs. From the source constants above, a default run reads about 30 posts per X query; with a second subquery and one named handle it is roughly 60 to 90 posts, so about $0.30 to $0.45 of X API credits per topic, or $0.009 to $0.014 for the same posts on TwitterAPI.io. That is our arithmetic from the repo's constants, not a measured bill: the skill itself reports official X API spend as "unknown" in its budget guard because X does not return a USD amount per call.
The bigger issue is the window. The skill researches 30 days, but an X bearer without full-archive access falls back to recent search and the engine says so: window truncated to 7 days. We measured what that loses. For three AI-agent topics we pulled one Top page for each 3-day slice of a 30-day window (the same slicing the skill's Grok Bot recipe uses for its popular pass), then ranked the pool by likes.
How to read it. 23 of 30 days (77%) of the window are older than a week, so a fair share of older posts is expected; the top posts sit above that line in all three topics. A recent-search-only bearer cannot return any of them. The last column is the other trap: a plain Top-sorted pull without date slices leaned recent too, with only 6% to 26% of posts older than a week, which is why slicing the window matters whichever API you use.
The cookie path is free, but it is your account. Bird reads X through the logged-in session in your browser. The repo's own docs say scraping "carries some account risk"; X can rate-limit, lock or suspend an account whose session makes automated requests. If the account matters to you, use a key-based backend or the hand-off below, which needs no X account.

Using TwitterAPI.io with last30days: the --x-posts hand-off
There is no TwitterAPI.io backend in last30days, and you cannot point an existing backend at our host. The base URLs are hard-coded (https://api.x.com/2 in x_api.py, the provider's own URL in xquik.py), and neither takes a base-URL variable. The supported way to bring X data from anywhere else is the engine's --x-posts flag. CONFIGURATION.md says it "hands the engine an X result the hosting model fetched itself ... it replaces the engine's X fetch for that run and works on any host."
The file format, last30days-x-posts/1. A JSON object with schema, generated_at (must be under 6 hours old), topic (must equal the run's topic), window with from and to dates, a free-text provider, status (ok, partial or error) and calls[]. Each call has a lane (topic, from, mention or related), the handles it covers and posts[]. Each post has exactly eight fields: id, author_handle, created_at, text, likes, reposts, replies, quotes.
We checked it. We built a file from 40 real TwitterAPI.io rows (the "openclaw" slice pull) with the script below and ran it through the repo's own lib/x_envelope.py read() from a clone of the main branch at v3.30.0. Result: accepted 40 of 40, dropped none. We did not run a full research session with it, so the brief's quality with these rows is not measured here.
Run it. Fetch, then pass the file in the same shell. The topic string must match exactly, and the file must not sit inside ~/.config (the engine refuses to read it there).
export TWITTERAPI_IO_API_KEY=... # from twitterapi.io/dashboard
python3 x_posts_twitterapi.py "openclaw" /tmp/x-posts.json
# from a source checkout of the skill
python3 skills/last30days/scripts/last30days.py "openclaw" --x-posts /tmp/x-posts.json
# inside Claude Code or Codex, ask the agent to run the same engine command
# with --x-posts; comparison runs use the per-entity x_posts field of
# --competitors-plan instead (a bare --x-posts on a comparison exits 2)The companion script, line by line
The full script is at the bottom of this page. It has four parts, and each one mirrors a rule from the repo so the file is accepted on the first try.
Window. window_bounds() uses the engine's rule from the Grok Bot recipe: from is today's UTC date minus 30 days, to is today's UTC date. If you run the skill with --days 7, set DAYS = 7 too, or rows outside the engine's window are dropped.
Fetch. Two Latest pages of /twitter/tweet/advanced_search give the newest posts; then ten Top pages, one per 3-day slice with since: and until: operators, surface each slice's most-engaged posts. In our study, all 552 posts returned by 30 such slice requests fell inside their slice bounds. Retweets are excluded with -filter:retweets. Twelve requests return up to 240 tweets, about $0.036 at $0.15 per 1,000; the same 240 posts would be $1.20 at X's $0.005 per post read.
Map. to_row() turns one tweet into the eight allowed fields: id as a string, author.userName, createdAt parsed from X's Thu Oct 16 12:34:56 +0000 2026 format into ISO UTC, text, and likeCount, retweetCount, replyCount, quoteCount.
Write. build_envelope() dedupes by ID, ranks by likes, keeps 40 rows (the deep-depth budget, so the same file serves --deep), stamps generated_at, and writes compact one-line JSON. If nothing came back it writes status: error with the unavailable category instead of an empty ok, as the format requires.
Rate limits. New accounts run at 0.2 requests per second until the first payment, so PAUSE = 5.5 seconds keeps twelve requests at about a minute; after a payment the limit is 20 requests per second (50 on Scale, 500 on Business) and you can drop the pause to 0.1. New accounts that pass the automatic bot check get up to $1 of free credits (at most 100,000 credits, no card, one per person or network, expiring after 31 days), enough for hundreds of these runs; purchased credits never expire.
What X says about last30days: a 2026 snapshot
For the skill itself, we searched X for "last30days" (Latest order) until the search cursor ended: 714 posts across 37 requests, 537 of them from 2026 (earlier matches are unrelated uses of the word). A separate url:last30days-skill search returned 168 posts linking the repo. Treat both as lower bounds of a search index, not a census.
What it tells a builder. Claude is the host people name most, well ahead of Codex, Grok, OpenClaw and Hermes, which matches the README calling Claude Code the recommended install. Attention is concentrated: the top tenth of posts hold three quarters of the likes, so a research run that keeps only a couple of dozen X rows is right to rank by engagement first. And the conversation about the tool is thin next to its star count, a few posts a day, which is a reminder that the X lane matters for the topics you research, not for this one.
Languages. English leads (355 posts), followed by Chinese (89) and Japanese (23); the repo ships translated READMEs, and its query code includes CJK handling.

When you want X data in your agent without a research brief
last30days answers "what happened about this topic this month, across every platform." Many agent tasks are narrower: what did this account post today, who mentioned us, what does this exact search return. For those, a direct X data skill is shorter and gives the agent raw JSON instead of a synthesized brief.
Reads need no X account. Every TwitterAPI.io read above runs on one X-API-Key header, with no X developer app, OAuth flow or browser session. That is the practical difference from the bird and xurl lanes, and why the hand-off file works on headless and CI hosts where the cookie path does not.
How we collected this
Question: what does a 30-day X window hold for AI-agent topics, how much of a month's top engagement sits outside a 7-day recent-search window, what does X say about last30days itself, and does a TwitterAPI.io-built --x-posts file pass the skill's validator.
Queries: last30days (Latest, until the cursor ended, 37 requests), url:last30days-skill (Latest, 10 requests), last30days (Top, 3 requests); for "claude code", "openclaw", "hermes agent" and "codex", one unsliced Top pull of 5 pages over the 30-day window, and for the first three, ten 3-day Top slices of one page each (30 requests). All through /twitter/tweet/advanced_search with retweets excluded on the topic pulls.
Sample and cost: 100 requests and about 1,866 tweets, collected in 2026: about $0.28 at $0.15 per 1,000, or $9.33 for the same posts at X's $0.005 per post read.
Validator check: 40 rows from the openclaw slices, written by the companion script's build_envelope(), read by skills/last30days/scripts/lib/x_envelope.py read() under Python 3.12 from a shallow clone of the main branch (v3.30.0).
What the sample cannot show: search results are an index, not every post; Top order is X's relevance ranking, not pure likes; three topics are not every topic; the per-run cost of the built-in backends is arithmetic from source constants, not a measured bill; and we did not run a full research session, so brief quality is not measured.
Privacy: keyword searches return private individuals, so raw pages stayed on the collection machine; the published study holds aggregates and de-identified rows (no IDs, URLs, text or handles). No individual is named or profiled.
"""Fetch the last N days of X posts for a topic from TwitterAPI.io and write a
last30days-x-posts/1 envelope that /last30days reads with --x-posts <file>."""
import json, os, sys, time
from datetime import datetime, timedelta, timezone
import requests
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_API_KEY"]}
DAYS, SLICES, KEEP = 30, 10, 40 # window, popular-pass slices, rows to keep
PAUSE = 5.5 # 0.2 requests/s before your first payment; 0.1 is plenty once paid (20 requests/s)
def search(query, query_type, pages=1):
"""advanced_search: top-level tweets[], has_next_page, next_cursor."""
out, cursor = [], ""
for _ in range(pages):
r = requests.get(f"{BASE}/twitter/tweet/advanced_search", headers=HEADERS, timeout=30,
params={"query": query, "queryType": query_type, "cursor": cursor})
r.raise_for_status()
time.sleep(PAUSE)
body = r.json()
out += body.get("tweets", [])
if not body.get("has_next_page") or not body.get("next_cursor"):
break
cursor = body["next_cursor"]
return out
def to_row(t):
"""Map one TwitterAPI.io tweet to the eight flat fields the envelope allows."""
created = datetime.strptime(t["createdAt"], "%a %b %d %H:%M:%S %z %Y")
return {"id": str(t["id"]), "author_handle": t["author"]["userName"],
"created_at": created.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"text": t.get("text", ""), "likes": t.get("likeCount", 0) or 0,
"reposts": t.get("retweetCount", 0) or 0, "replies": t.get("replyCount", 0) or 0,
"quotes": t.get("quoteCount", 0) or 0}
def window_bounds(days=DAYS):
"""Same rule as the engine: from = today's UTC date minus N days, to = today."""
today = datetime.now(timezone.utc).date()
return today - timedelta(days=days), today
def build_envelope(topic, tweets, start, end, keep=KEEP):
seen, rows = set(), []
for t in sorted(tweets, key=lambda t: t.get("likeCount", 0) or 0, reverse=True):
if t["id"] not in seen and t.get("text"):
seen.add(t["id"]); rows.append(to_row(t))
return {"schema": "last30days-x-posts/1",
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"topic": topic, "window": {"from": start.isoformat(), "to": end.isoformat()},
"provider": "twitterapi.io", "status": "ok" if rows else "error",
**({} if rows else {"error": "unavailable"}),
"calls": [{"lane": "topic", "handles": [], "posts": rows[:keep]}]}
def main(topic, out_path):
start, end = window_bounds()
span = (end - start) / SLICES
pool = search(f"{topic} -filter:retweets since:{start}_00:00:00_UTC", "Latest", pages=2)
for i in range(SLICES): # one Top page per slice, so older high-engagement posts surface
a = datetime.combine(start + span * i, datetime.min.time())
b = datetime.combine(start + span * (i + 1), datetime.min.time())
pool += search(f"{topic} -filter:retweets since:{a:%Y-%m-%d_%H:%M:%S}_UTC "
f"until:{b:%Y-%m-%d_%H:%M:%S}_UTC", "Top")
envelope = build_envelope(topic, pool, start, end)
with open(out_path, "w", encoding="utf-8") as f:
json.dump(envelope, f, ensure_ascii=False, separators=(",", ":"))
print(f"{len(pool)} tweets fetched, {len(envelope['calls'][0]['posts'])} rows written to {out_path}")
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "/tmp/x-posts.json")
Questions readers ask
What is the last30days skill?
An open-source, MIT-licensed agent skill by GitHub user mvanhorn (mvanhorn/last30days-skill) that searches Reddit, X, YouTube, Hacker News, Polymarket, GitHub and the web for the last 30 days of a topic, scores what people engaged with, and has your agent write a cited brief. It runs in Claude Code, Codex, Cursor, Gemini CLI, Grok Build, Hermes, OpenClaw and other Agent Skills hosts.
How do I install last30days in Claude Code?
Inside Claude Code run /plugin marketplace add mvanhorn/last30days-skill and then /plugin install last30days; the marketplace keeps it updated. The README also supports npx skills add mvanhorn/last30days-skill -g -a claude-code, but use one method per machine or the command appears twice.
Does last30days need an X API key?
No. By default it tries bird, which reads X through your logged-in x.com browser session, then XAI_API_KEY, X's xurl CLI and an Xquik key. The official X API (X_BEARER_TOKEN) is used only when you pin LAST30DAYS_X_BACKEND=xapi, or on Grok Bot after the bot's own X tools. Without any X backend the run still works on Reddit, HN, Polymarket and GitHub.
Why does last30days only show a week of X posts?
If you use X_BEARER_TOKEN and your X developer project lacks full-archive search, the engine falls back to recent search, which reaches about 7 days, and reports window truncated to 7 days. In our study 21 to 23 of the 24 most-liked posts of a 30-day window were older than 7 days. Full-archive access, another backend, or an --x-posts file covers the whole month.
Can I use TwitterAPI.io as the X backend for last30days?
Not as a built-in backend: the X backends have hard-coded hosts. You can use the engine's --x-posts flag, which takes a last30days-x-posts/1 JSON file from any source on any host. The companion script on this page fills it from TwitterAPI.io search in 12 requests; 40 of 40 of our rows passed the repo's validator.
How much does X data cost per last30days run?
Browser cookies cost nothing but run as your account. With the official X API at $0.005 per post read, a default run of roughly 60 to 90 posts is about $0.30 to $0.45 by our arithmetic from the source. Our companion script's 240-tweet pull is about $0.036 at $0.15 per 1,000 tweets.
How do I install last30days on Hermes?
Per HERMES_SETUP.md, hermes skills install is blocked because Hermes's scanner rates the skill dangerous. Clone the repo, mkdir -p ~/.hermes/skills/research, copy skills/last30days into it, check with hermes skills list, and run /reload-skills in an open session.
Is the bird cookie path safe for my X account?
The repo's own docs say scraping with browser cookies "carries some account risk." The calls act as your logged-in account, and X can rate-limit, lock or suspend accounts that make automated requests. Use a separate account, a key-based backend, or an --x-posts file from an API that needs no X account.
Continue
- mvanhorn/last30days-skill on GitHub: README, install table, source requirements (MIT)
- last30days CONFIGURATION.md: X backend chain, LAST30DAYS_X_BACKEND, X_BEARER_TOKEN, --x-posts flag
- last30days x_envelope.py: the last30days-x-posts/1 validator
- last30days HERMES_SETUP.md: Hermes install and the blocked installer
- X API pay-per-use pricing: $0.005 per post read, 3M post-read cap
- TwitterAPI.io documentation: advanced_search and response fields
- X (Twitter) API overview and endpoints
- SKILL.md explained, with the twitterapi-io skill in full
- Hermes Agent and Twitter (X) data options
- using Claude with a Twitter (X) API
- Codex and Twitter (X) data
- Grok Bot and its built-in X tools
- advanced search operators like since: and until:
- TwitterAPI.io pricing per 1,000 tweets
- 01How do you earn TwitterAPI.io credits by writing about the API?
- 02Twitter (X) Lists: What They Are, How to Make One, and How to Use Them
- 03Tweepcred: How Twitter (X) Scores Account Reputation, Read From the Code
- 04Tweet Failed to Send on Twitter (X)? What Each Error Means and How to Fix It
- 05How to Embed a Video on Twitter (X): In a Post or on Your Website
- 06SKILL.md explained: the Agent Skills format, X's own skill, and a Twitter (X) API skill you can install
- 07Twitter (X) API Cost — A Straight Answer
- 08X Handle Marketplace: How to Get Inactive X Handles on Twitter (X) in 2026
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API keyAlready building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.