Skip to main content

x-api

Use when: monitoring curated X accounts, cashtags, finance tweet pulls. Paid API ($/post). NOT general search (/research).

Source facts

Repository
markusstrasser/skills
Last source activity
November 5, 2026 at 06:34
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
6 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
x-api
description
Use when: monitoring curated X accounts, cashtags, finance tweet pulls. Paid API ($/post). NOT general search (/research).
user-invocable
true
argument-hint
[probe USERNAME | pull --config FILE]
allowed-tools
["Read","Glob","Grep","Bash","Write"]
effort
low
# X API v2 Client Generic, project-agnostic interface to the X (Twitter) API v2 pay-per-use developer tier. Built for daily-batch monitoring of a curated account list. To read one public post or thread, use `xread <url>`: it needs no login and costs nothing. **Authentication:** Bearer token from `X_API_BEARER_TOKEN` env var. Caller is responsible for loading the token (e.g., from `.env.local`, `~/.env`, or launchd plist EnvironmentVariables). ## Subcommands ### `probe USERNAME [MAX_RESULTS] [START_TIME_ISO]` Smoke-test auth, pull recent tweets from a single account, dump JSON to `.scratch/x_probe_<username>.json`. Reports cost. ``` uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/probe.py aleabitoreddit 10 uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/probe.py aleabitoreddit 100 2026-04-01T00:00:00Z ``` ### `search.py query|verify|thread` — full-archive search (added 2026-08-20) Promoted after being hand-rolled 4× across two research sessions. Wraps `/2/tweets/search/all` with ledger discipline and the operational lessons baked in. ``` uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/search.py query '"greater male variability" lang:en' --out hits.jsonl --label mytopic uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/search.py verify StuartJRitchie lakens cremieuxrecueil uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/search.py thread 1234567890 --author whyvert uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/search.py query 'immigration lang:en' --start-time 2026-06-05T00:00:00Z --end-time 2026-08-01T00:00:00Z --pages 1 --max 50 --out summer.jsonl --label summer ``` Date windows (2026-09-05): `query --start-time/--end-time` pass the official full-archive parameters on every page. Use UTC `YYYY-MM-DDTHH:MM:SSZ`; start is inclusive and end exclusive. Malformed dates, future dates and start >= end fail before an API request. Both flags are optional. Keep `--pages` and `--max` explicit for a bounded sample; without dates, returned pages do not establish coverage of a requested historical interval. [Official endpoint reference](https://docs.x.com/x-api/posts/search-all-posts). Evidence: the 2026-09-05 immigration review requested June–September material; the prior CLI exposed no time parameters and its returned sample began August 10. Rules it encodes (learned the expensive way): - **verify handles BEFORE `from:` queries** — a wrong training-data handle reads as a silent zero, and squatters shadow real names (<100-follower flag printed). - **Quotes in, pure RTs out by default** (`-is:retweet` auto-appended; quotes carry the commentary). `--with-retweets` to override. - **A zero result means "not keyword-reachable in this phrasing," never "doesn't exist"** — search the discourse's own vocabulary, not your methods jargon. - 402 = vendor credit balance (developer.x.com), NOT the local $100/mo ledger cap. - Every returned post bills $0.005 — `--pages` caps spend deliberately. ### `pull --config FILE [--since-hours 24] [--max-pages 2] [--digest-out PATH] [--tracked-tickers-file PATH] [--themes-dir PATH]` Read a JSON account-list, pull tweets since N hours ago, filter for cashtags and material-claim keywords, emit a markdown digest with coverage delta. Hard cap $100/month spend (refuses to run if MTD exceeds). Cost ledger is wallet-scoped at `~/.local/state/x-api/cost_ledger.jsonl` (NOT CWD-scoped — spend from every repo counts against the one monthly cap). ``` uv run --project ~/Projects/skills python3 ~/Projects/skills/x-api/scripts/pull.py \ --config .claude/config/x_curated_accounts.json \ --tracked-tickers-file <(ls analysis/entities/*.md | xargs -I {} basename {} .md) \ --themes-dir analysis/themes \ --digest-out .scratch/social_digest_$(date -u +%Y-%m-%d).md ``` ## Library `scripts/x_api.py` — `get_user`, `get_user_tweets`, `CostTally`, `log_cost`, `month_to_date_usd`. Import from external scripts via: ```python import sys from pathlib import Path sys.path.insert(0, str(Path.home() / "Projects/skills/x-api/scripts")) from x_api import CostTally, get_user, get_user_tweets, log_cost, month_to_date_usd ``` ## Pricing reference (2026-05-01) | Resource | Price | Note | |---|---|---| | Post read | $0.005 | Each tweet returned, not per request | | User lookup | $0.010 | Per `/users/by/username` or `/users/:id` | | Owned reads | $0.001 | Reads of the auth'd app's own data only | | Monthly post-read cap | 2,000,000 | Hard limit at PPU tier | See `~/Projects/intel/.scratch/x_api_features_research.md` for full feature map (Lists endpoint, mentions endpoint, context_annotations). ## Architecture notes - **Lists endpoint refactor deferred** until curated list grows past ~5 accounts. `/2/lists/:id/tweets` cuts request count ~10x for the same per-post cost; not worth the refactor for 1-3 accounts. - **Server-side cashtag extraction** is enabled via `tweet.fields=entities`. Each tweet's `entities.cashtags` field returns X's own ticker tagging, more accurate than text regex (skips `$` in money figures and quoted text). - **Material-claim regex** is a starting heuristic. Replace with LLM classification (Haiku/Flash) once enough volume justifies the cost. - **Materiality is domain-configurable.** The finance/8-K keyword set is the default; pass an optional `"material_keywords": [...]` list in the config JSON to override it for a non-finance domain (e.g. AI-research account monitoring). Config-driven keywords match as a leading-`\b`-anchored PREFIX (not whole-word) — a stem like `"distill"` also catches "distillation"/"distilled". No `material_keywords` key in the config ⇒ the finance default is unchanged (`pull.py::build_material_pattern`). - **Zero tweets pulled for an active account is usually not a bug** — the default `get_user_tweets` excludes replies and retweets, and a short (24-48h) window can simply miss an account's last original post. Before assuming a resolution/pagination/API bug, run `probe.py USERNAME N START_ISO` with a wide window (e.g. 7d) and read the raw response: it prints the resolved user id/name/follower count (catches wrong-account resolution) and each tweet's exact timestamp (catches window-vs-cadence mismatches). For accounts whose signal is mostly in-thread commentary, set per-account `"include_replies": true` in the config — confirmed case 2026-07-20: an account's only post in a 7-day window was a reply, silently excluded until this flag was added (`get_user_tweets(include_replies=)`). ## What this skill does NOT do - Posting tweets, DMs, or any write actions - Real-time streaming (Filtered Stream — defer until >50 accounts) - Account discovery (curated list management is caller's responsibility) - LLM claim extraction (text classification belongs in caller, model choice varies per project) - Auto-updates to track-record memory files (caller decides when to write)
View on GitHub