---
name: x-articles
description: Find X Articles (the long-form post type on X) that pass a view threshold, keep them in a local dataset with a CSV, and tag each one with one of 22 broad topics. Use to find, track or refresh high-performing X Articles ("find articles over 100k views", "what articles are blowing up this week"). Needs an Apify account for the scans and the claude CLI for tagging.
---
 
# X ARTICLES FINDER
 
What this is: the finder behind the public dataset of X Articles over 100k views (research by @fuckgrowth, Sep 2026). It searches X for posts that link an X Article, keeps the articles over a view threshold, merges them into a local state file and rewrites a CSV after every run, and a second script tags every article with one broad topic through the claude CLI. The dataset it built is at https://docs.google.com/spreadsheets/d/1GIl5AAbi5VD34plMPaeoU60zh2Q3pWfyCOKvjsTlZ4Q (CSV: https://docs.google.com/spreadsheets/d/1GIl5AAbi5VD34plMPaeoU60zh2Q3pWfyCOKvjsTlZ4Q/export?format=csv&gid=0) and the research built on it is at https://docs.google.com/document/d/10UnoOKmYl0bzqpYmE2QeTlkMG8sZvU-zezulmgWfeNg .
 
## INSTALL
1. Save this folder as `~/.claude/skills/x-articles/` with `SKILL.md` and `scripts/` (find_articles.py, tag_articles.py, lang_check.py, coverage_check.py).
2. Python 3.9 or newer; the finder uses the standard library only.
3. An Apify account and API token, exported as `APIFY_TOKEN` (the finder reads nothing else for credentials): `export APIFY_TOKEN=...`
4. For tagging, the claude CLI (Claude Code) installed and logged in, since tagging runs `claude -p` headlessly with Haiku.
Everything is stored in `~/.x-articles/` (or the folder you pass with `--state-dir`): `articles.json` (every match ever found, keyed by post id, with metrics refreshed when a post shows up again), `runs/<timestamp>.json` (the raw Apify items of each scan), `articles.csv` (rewritten after every run, newest first), `runs.csv` (one line per run) and an optional `config.json` with your defaults (min_views, limit, lang, min_faves, max_items).
 
## HOW IT WORKS
- Discovery runs the Apify actor `apidojo~tweet-scraper` with the X search query `url:x.com/i/article` plus `min_faves:N` and `lang:xx`, sorted Latest. Every Article post carries a t.co link that resolves to x.com/i/article/<id>, which is why the url: operator finds them; `filter:articles` does not exist and returns nothing.
- Each result carries an `article` object (id, title, previewText, coverImage, firstPublishedAt), views come as `viewCount`, and the article URL is x.com/<handle>/article/<article id>.
- X search has no minimum-views operator, so the finder over-fetches with a likes floor (`min_faves`, default 100) and filters by views locally.
- One article can be posted many times, so the finder keeps the highest-view post per article; `firstPublishedAt` can be older than the post, and the "Posted" column uses the post date.
- Apify charges about $0.40 per 1,000 posts scanned (800 posts cost about $0.32), so quote the cost before any run above about $5.
- Followers are the author's count at the time of the scan, not at posting, and accounts that went viral usually grew after the post.
 
## RUN
```
python3 ~/.claude/skills/x-articles/scripts/find_articles.py                                   # defaults: 100k views, 10 new rows, English
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --min-views 50000 --limit 0 --lang any
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --extra "since:2026-09-01" --dry-run
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --from-run <apifyRunId>           # reprocess an old run, no new cost
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --from-file ~/.x-articles/runs/<file>.json --dry-run   # offline, no token needed
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --write-only                      # rewrite the CSV from local state after tagging
```
Flags: `--min-views` (default 100000), `--limit` for new rows added per run (default 10, 0 = all), `--lang` (default en, `any` for all), `--min-faves` (default 100), `--max-items` (default 800), `--extra` for raw operators appended to the query, `--since`/`--until`/`--window-days` for date windows, `--lang-mode operator|local`, `--state-dir`, `--csv`, `--after-write`, `--dry-run`.
Reading the output: "oldest scanned" tells you how far back the Latest feed reached. When it covers only a few hours and you want older winners, raise `--max-items`, raise `--min-faves` (fewer and stronger posts per scan) or use date windows.
To study what fails as well as what wins, keep every article a scan returns with `--min-views 0 --limit 0` and compare the ones over and under 100k; the research compared 6,835 articles over 100k with 33,643 under from the same scans.
 
## BACKFILL (date ranges)
```
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --since 2026-01-01 --until 2026-09-01 --window-days 1 --min-faves 50 --limit 0 --max-items 30000 --lang-mode local
python3 ~/.claude/skills/x-articles/scripts/coverage_check.py ~/.x-articles/runs/<newest>.json
```
- X search returns at most about 2,000 posts per query, so a window that comes back near that number was cut short and its earliest hours are missing. All-language article volume ran at about 850 posts a day at min_faves:50 in 2026 (about 490 English ones in September), so use 1-day windows and check the per-day counts with coverage_check.py after every windowed scan.
- The `lang:` operator combined with `url:` returns nothing for windows older than about 6 weeks, because link-only Article posts are indexed with the language "zxx". For older windows use `--lang-mode local`, which drops the operator and detects English from the article title and preview after the fetch (negative operators such as `-lang:ja -lang:zh -lang:ko` still work there and cut the volume roughly in half).
- Start scans asynchronously and poll the run, as the script does, since the actor's synchronous endpoint timed out after about 4 minutes in testing.
 
## TAGGING (one broad topic per article)
```
python3 ~/.claude/skills/x-articles/scripts/tag_articles.py              # untagged rows only
python3 ~/.claude/skills/x-articles/scripts/tag_articles.py --all        # retag everything after a taxonomy change
python3 ~/.claude/skills/x-articles/scripts/find_articles.py --write-only   # put the tags into the CSV
```
The tagger sends batches of 80 articles (title plus the first 160 characters of the preview) to `claude -p --model claude-haiku-4-5-20251001` with 4 workers, which took about 45 seconds per batch and about 55 minutes for 6,030 rows. The 22 tags are Politics, Crypto, AI, Tech, Business, Marketing, Finance, Health, Self-Improvement, Entertainment, Gaming, Sports, Science, Relationships, Writing, Religion, History, News, Career, Education, Lifestyle and Other, and the model gets this guide:
```
Politics: government, elections, policy, geopolitics, war, activism, political commentary
Crypto: coins, tokens, DeFi, airdrops, trading of crypto, web3 projects, blockchains
AI: AI models, agents, prompting, LLM tools and workflows (even if marketing-flavored), AI industry
Tech: software, hardware, startups' products, programming, gadgets, internet culture of tech (not AI-centric)
Business: entrepreneurship, company strategy, founders, operations, economics of companies, e-commerce
Marketing: growth, content strategy, social media playbooks, ads, branding, sales, copywriting
Finance: stocks, macro, investing, personal finance, real estate, markets (non-crypto)
Health: fitness, nutrition, medicine, mental health, sleep, peptides, biohacking
Self-Improvement: productivity, discipline, mindset, life advice, habits, motivation
Entertainment: movies, TV, music, anime, celebrities, fandom, pop culture, memes, art
Gaming: video games, patch notes, esports, game studios
Sports: any sport, athletes, teams, fitness competitions
Science: research, space, physics, math, biology, engineering explainers (non-AI)
Relationships: dating, marriage, family, parenting, friendship, sex
Writing: writing craft, literature, books, storytelling, essays about language
Religion: faith, spirituality, theology, scripture, philosophy of religion
History: historical essays and stories about the past
News: current events, crime, accidents, investigations, breaking stories not mainly political
Career: jobs, hiring, interviews, workplace, professional growth
Education: learning, schools, courses, study methods, academia
Lifestyle: travel, food, fashion, home, hobbies, personal stories with no other clear topic
Other: nothing above fits
```
The prompt around the guide asks for the single best tag by the article's main subject (title first, preview second) and for a JSON object that maps each item number to its tag, with no prose. To change the taxonomy, edit TAGS and GUIDE in tag_articles.py, then run `--all` and `--write-only`.
 
## LANGUAGE CHECK
`lang_check.py` flags a row as not English when its title and preview carry more Spanish, Portuguese, French, German, Italian or Indonesian stopwords than English ones, which is stricter than the finder's own English filter and is the check the research used. From the command line: `python3 ~/.claude/skills/x-articles/scripts/lang_check.py "title" ["preview"]`.
 
## OPTIONAL: YOUR OWN SHEET
The finder only writes local files. To mirror the CSV somewhere else after every write, pass a command with `{csv}` where the file path goes, for example `--after-write "python3 my_upload.py {csv}"`, using your own credentials in your own script, or import the CSV into any spreadsheet by hand.
 
## REPORT AND LEARN (every run)
Tell the user how many posts were scanned, how many unique articles came back, how many passed the threshold, how many are new, the oldest date reached, the cost and the CSV path, then list the new rows (date, handle, views, title). Keep a `banks/learnings.md` next to this file with one dated line for every operator, actor or data quirk you find, and read it before the next run. Keep secrets out of every file in this skill, since the token lives only in APIFY_TOKEN.
