x-migrate
Migrate X (Twitter) lists and follows between accounts.
Extract members from any X list or following page, then follow them or add them to a list on your destination account — with automatic rate-limit recovery, progress tracking, and a Rich TUI.
Requirements
- Python 3.11+
- Google Chrome installed
Installation
pip install x-migrate
playwright install chromium
Install from source
git clone https://github.com/tongliuTL/x-migrate
cd x-migrate
pip install -e .
playwright install chromium
Quick Start
Tip: All commands support the short alias
xm— use it instead ofx-migrateto save typing.
1. Run setup wizard (first time only)
xm setup
Creates ~/.x-migrate/config.toml with your source and destination Chrome profile paths and daily follow limit.
2. Extract members from an X list
xm extract --source list --url "https://x.com/i/lists/YOUR_LIST_ID"
The
/memberssuffix is added automatically if omitted.
Or extract from someone's following list:
xm extract --source following --account "@handle"
3. Follow those members from your destination account
# Follow up to 20 members (default) with an interactive login prompt:
xm follow
# Increase the daily cap:
xm follow --limit 50
# Unattended mode — skips login prompts (requires a saved browser session):
xm follow --auto
# Auto-resume after a rate-limit hit, fully unattended:
xm follow --auto --backoff 2h
# Walk away and let it finish overnight:
xm follow --auto --limit 400 --backoff 3h
# Preview who would be followed without opening a browser:
xm follow --dry-run
4. Add to a list instead of following
xm list-add --list-name "My List Name"
5. Check progress
xm report
Shows migration progress from local data (no browser needed). To cross-check against your live following list:
xm report --verify
Rate Limits
X typically allows ~400 follows per day. x-migrate detects rate limiting automatically and can either stop (default) or sleep and retry:
| Behaviour | Command |
|---|---|
| Stop on rate limit, re-run manually next day | xm follow |
| Stop on rate limit, skip login prompt on re-run | xm follow --auto |
| Auto-retry after 2 hours, fully unattended | xm follow --auto --backoff 2h |
Accepted backoff formats: 2h, 90m, 3600s. Progress is saved after every follow — it is always safe to Ctrl-C mid-run.
The --limit cap is the total for the entire invocation, across all retry sessions.
Configuration
~/.x-migrate/config.toml:
source_profile = "/path/to/source/profile"
dest_profile = "/path/to/dest/profile"
daily_limit = 20 # default for --limit when not specified
active_job = "a1b2c3d4e5f6" # auto-set by xm extract
Data Storage
All data is stored under ~/.x-migrate/:
| Path | Contents |
|---|---|
config.toml |
Settings — profiles, daily limit, active job |
profile_source/ |
Chrome profile for source account (saved login session) |
profile_dest/ |
Chrome profile for destination account |
progress/ |
Per-job JSON files tracking follow status per user |
Commands
| Command | Description |
|---|---|
xm setup |
Create or update ~/.x-migrate/config.toml |
xm extract |
Extract members from a list or following page |
xm follow |
Follow extracted members on destination account |
xm list-add |
Add extracted members to a named list |
xm report |
Show local progress; --verify cross-checks live data |
All commands also work as x-migrate <command>.
xm follow options
| Flag | Default | Description |
|---|---|---|
--limit N |
daily_limit from config |
Maximum follows for this invocation |
--auto |
off | Skip login prompts (uses saved browser session) |
--backoff DURATION |
0 (disabled) |
Sleep duration after a rate limit before retrying |
--dry-run |
off | Print the follow plan without opening a browser |
Development
git clone https://github.com/tongliuTL/x-migrate
cd x-migrate
pip install -e ".[dev]"
pytest
See CONTRIBUTING.md for guidelines on submitting changes.
Releasing a new version
- Update
versioninpyproject.toml - Add a
## [X.Y.Z] — YYYY-MM-DDsection toCHANGELOG.md - Commit and tag:
git commit -m "chore: bump version and changelog (vX.Y.Z)" git tag vX.Y.Z git push && git push --tags
- GitHub Actions runs tests, builds the wheel, publishes a GitHub Release, and pushes to PyPI automatically.
Pre-releases (e.g. v0.2.0-beta.1) are automatically marked as pre-release on GitHub.
Contributing
Bug reports, feature requests, and pull requests are welcome — see CONTRIBUTING.md.
License
Metadata
Release files for x-migrate 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| x_migrate-0.2.0.tar.gz | 34.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| x_migrate-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.8 kB
Release files / x_migrate-0.2.0.tar.gz
| Download URL | x_migrate-0.2.0.tar.gz |
|---|---|
| Size | 34.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e474d7f4708cacbaabe2784aec7511813f870082ab8be3c0c55946a4ba00f40c
|
|
BLAKE2b-256 checksum How to use checksums |
ff6192deb4140bd57b716b6bf55a3910de0fe7479c6d63185d4f40f6ce52fb2a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 3, 2026.
Transparency logRelease files / x_migrate-0.2.0-py3-none-any.whl
| Download URL | x_migrate-0.2.0-py3-none-any.whl |
|---|---|
| Size | 20.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3eb33ab02f529e7d6ec37ca3fa96e8a9c92296ee3a91e2a5c364624b3a0ec7ae
|
|
BLAKE2b-256 checksum How to use checksums |
651fcb7863c015c5e87b9d9e22f9ec37b2821c2231b9c4d1d77e4ae7838b07b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 3, 2026.
Transparency log