Read-only scraping of logged-in X/Twitter data via a harvest-then-replay hybrid: a stealth-browser login harvests the session once, then reads are replayed over httpx. No client-transaction-id replay.
Project description
scraper-for-x
Read-only scraping of logged-in X/Twitter data via a harvest-then-replay hybrid: a stealth browser (or a cookie import) logs you in once and harvests the session, then every read afterward is a plain httpx GraphQL request — no browser in the loop, no x-client-transaction-id replay.
Read DISCLAIMER.md before using this. Using this tool violates X's Terms of Service, publishing it exposes its maintainer, and scraping other people's tweets can make you a data controller over their personal data under GDPR. Use a dedicated/throwaway account, not your primary one.
Installation
Base install — cookie-import login only, no browser dependency:
pip install scraper-for-x
The [browser] extra — adds a stealth browser for scrape-x login:
pip install "scraper-for-x[browser]"
If you only ever import cookies from a session you already have (e.g. exported from your own logged-in browser), the base install is all you need.
Quick Start
# 1. One-time interactive login — opens a real browser window, you log in by hand.
scrape-x login
# 2. Fetch a profile's tweets.
scrape-x fetch nasa --limit 50
CLI overview
| Command | Purpose |
|---|---|
scrape-x login |
One-time login: headed stealth browser by default, or --cookies FILE to import an existing session |
scrape-x status |
Check whether the persisted session is logged in, expired, or rate-limited |
scrape-x setup |
Provision the login browser into an isolated cache (requires [browser]) |
scrape-x doctor |
Authenticated round-trip + query-id freshness check (--refresh re-anchors query-ids from x.com's main.js, browser-free) |
scrape-x fetch <identifier> |
A profile's tweets/media (--limit, --since, --until, --by screen_name|id) — --replies not yet implemented, see below |
scrape-x search <query> |
Not yet implemented, see below |
scrape-x tweet <identifier> |
A single tweet plus its reply/conversation thread (--replies) |
fetch/search/tweet all share --format json|ndjson, --output PATH, --profile NAME, --profile-dir PATH, --wait-on-limit, --max-wait, --raw (+ --no-redact), and -v/--verbose. See the CLI Reference for every flag and exit code.
Known limitation: search and fetch --replies (v0.1.0)
Live-verified 2026-07-05: X's SearchTimeline and UserTweetsAndReplies GraphQL operations require a fresh, single-use x-client-transaction-id header on every request — unlike plain profile fetches and single-tweet lookups, which this package proved work over plain httpx replay with no such header. A captured transaction-id cannot be harvested once and reused like a session cookie or query-id; reproducing X's generator for it is exactly the fragility this package's harvest-then-replay architecture was built to avoid (see twikit#408).
Both scrape-x search and scrape-x fetch --replies fail fast with a clear FeatureNotImplementedError (exit code 1) rather than a confusing network error. A browser-observe fallback for these two operations specifically (the same approach scrape-fb uses) is on the roadmap — see wiki/FAQ-and-Troubleshooting.md.
Python API
from scraper_for_x import XScraper
XScraper(profile="default").login() # one-time, opens a headed browser
with XScraper(profile="default") as x:
tweets = x.fetch_user_tweets("nasa", limit=50)
for tweet in x.iter_user_tweets("nasa", limit=50):
... # must be consumed inside the `with` block
x.fetch_tweet("https://x.com/nasa/status/1234567890")
# x.search(...) raises FeatureNotImplementedError -- see "Known limitation" above.
Documentation
This README covers the essentials. For everything else, see the wiki:
- Installation
- Quick Start
- CLI Reference — every flag, every exit code
- Python API Reference
- Configuration — profiles, environment variables
- Output Schema — every
Tweet/User/Mediafield - Security and Privacy — the full threat model behind DISCLAIMER.md
- FAQ and Troubleshooting
- Contributing
License
MIT — see LICENSE. The license covers the code; it does not cover what you do with the data you collect (see DISCLAIMER.md).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scraper_for_x-0.1.0.tar.gz.
File metadata
- Download URL: scraper_for_x-0.1.0.tar.gz
- Upload date:
- Size: 110.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01134e0e9578b09b5d10210ba94be44ce02acd47a2d82430b8c8cf1f8f983c31
|
|
| MD5 |
0dca45177f47877dde7615025609ceef
|
|
| BLAKE2b-256 |
161dd51fdc2eb3ba254bee91b6a189af78a60c13b08a41ab117b67459e2f79ce
|
Provenance
The following attestation bundles were made for scraper_for_x-0.1.0.tar.gz:
Publisher:
publish.yml on tjdwls101010/Scraper-for-X
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scraper_for_x-0.1.0.tar.gz -
Subject digest:
01134e0e9578b09b5d10210ba94be44ce02acd47a2d82430b8c8cf1f8f983c31 - Sigstore transparency entry: 2078882388
- Sigstore integration time:
-
Permalink:
tjdwls101010/Scraper-for-X@481a43d8d10607f7f4506e50f11b6d8b823ff968 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tjdwls101010
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@481a43d8d10607f7f4506e50f11b6d8b823ff968 -
Trigger Event:
release
-
Statement type:
File details
Details for the file scraper_for_x-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scraper_for_x-0.1.0-py3-none-any.whl
- Upload date:
- Size: 49.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
817afc3b5a6eca4a2947d0f4f63b77cbb166748147afe3cd56c7c57dab5da298
|
|
| MD5 |
f6777c5b980009270a32e3393507febb
|
|
| BLAKE2b-256 |
89edbd6ee710ebe769f20fe862dac84e02dd0616dab015326a27bc24402e6dff
|
Provenance
The following attestation bundles were made for scraper_for_x-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on tjdwls101010/Scraper-for-X
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scraper_for_x-0.1.0-py3-none-any.whl -
Subject digest:
817afc3b5a6eca4a2947d0f4f63b77cbb166748147afe3cd56c7c57dab5da298 - Sigstore transparency entry: 2078882558
- Sigstore integration time:
-
Permalink:
tjdwls101010/Scraper-for-X@481a43d8d10607f7f4506e50f11b6d8b823ff968 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tjdwls101010
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@481a43d8d10607f7f4506e50f11b6d8b823ff968 -
Trigger Event:
release
-
Statement type: