Skip to content

Repository files navigation

XPorter

XPorter

Free, unlimited export of X (Twitter) posts, followers, and following to CSV, JSON, XLSX, or AI-friendly TXT.
A Chrome extension — no servers or subscriptions; your exported data stays local.

Features · Installation · How It Works · Export Output · Configuration · Languages · Project Structure · License


Release status

The v1.6.4 tag matches the package released through the Chrome Web Store. The main branch now reports version 1.6.5, packaged on September 6, 2026, with verified date-range capture, completion, partial downloads and Resume fixes. This package is separate from the 1.6.4 Store release; Store publication is not implied.

Unlike the withdrawn 1.5.9, this build declares pbs.twimg.com only in optional_host_permissions. When you first select Embed photo previews, XPorter explains why the access is needed before Chrome asks for permission. Declining keeps every export working with photo links. Each download checks the grant once and falls back to the same links-only workbook when access is missing.

Warning

XPorter 1.5.9 was withdrawn and is not recommended. Its update added a required pbs.twimg.com host permission for an optional XLSX photo-embedding feature. Chrome could therefore disable the extension until users reviewed the new access. The release also introduced experimental Bookmarks and profile-feed paths that proved unsuitable for dependable large exports: real-world runs could hit X limits or exhaust the available timeline after only hundreds of rows. Do not use 1.5.9 when a large or complete export matters.

See the XPorter 1.5.9 withdrawn release notes for the complete change list, incident explanation, and safer permission design.

Features

  • Full engagement metrics — views, likes, retweets, replies, quotes, bookmarks
  • Passive seen-post dataset — stores one local row per non-reply post already loaded while you browse X, with first/latest metrics and no extra API requests
  • Multiple export modes — posts, personal bookmarks, followers, following, and verified followers
  • CSV, JSON, XLSX, and TXT output — download the AI-friendly TXT or copy it straight to your clipboard
  • Two XLSX photo modes — keep links for the smallest file, or add bounded previews to a separate Media sheet
  • Date range filtering — export posts from an inclusive calendar-date window in your browser’s timezone. For today, the cutoff is the moment you start; Resume keeps that cutoff. Keep the search tab open. Search uses extra boundary days and filters rows to the exact interval. A stalled search preserves partial results and offers Resume; it does not claim completeness from date coverage. X search availability still limits which posts can be returned. An ending without explicit confirmation remains unconfirmed, with partial rows available to download. Date exports saved by older versions must be downloaded and restarted; Resume never silently changes their date semantics.
  • Pause and resume — stop mid-export and continue later with zero data loss
  • Smart rate limiting — six Export Speed modes plus live quota-aware pauses and retries
  • 100% local processing — everything happens in your browser; your exported data is never transmitted anywhere
  • Dark and light themes — glassmorphism UI with a one-click toggle
  • 14 languages — auto-detects Chrome's UI language on first launch
  • Dynamic API discovery — extracts fresh GraphQL query IDs from X's JS bundles at runtime; gracefully falls back to hardcoded IDs
  • Crash-resilient — export progress is persisted to Chrome storage and survives browser restarts
  • Zero dependencies — no npm packages, no build step, pure vanilla JavaScript

Installation

From Source (Developer Mode)

git clone https://github.com/Lemelson/xporter-extension.git
  1. Open chrome://extensions/ in Chrome
  2. Enable Developer Mode (top-right toggle)
  3. Click Load unpacked and select the cloned xporter-extension/ directory
  4. Navigate to x.com and log in
  5. Click the XPorter icon in the toolbar

How It Works

XPorter leverages your existing authenticated X session to access X's internal GraphQL API. No API keys, no OAuth flow — it piggybacks on the cookies you already have.

Popup UI ──▶ Service Worker ──▶ X APIs ──▶ CSV / JSON / XLSX / TXT File
                  │
            Chrome Storage
            (incremental saves)
                  │
Content Script ── detects username from active tab
               └─ records posts already loaded by X into local IndexedDB

Export flow:

  1. Content script detects the currently viewed profile from the X tab URL
  2. Popup collects the target username and export settings
  3. Service worker resolves the username to a user ID via UserByScreenName, then fetches the selected data type in paginated batches
  4. Items are saved incrementally to Chrome local storage (batches of 50)
  5. On completion (or manual download), items are compiled locally into CSV, JSON, XLSX, or posts-only TXT; large exports download as safe, numbered parts, and XLSX previews show separate photo and workbook-building progress

While you browse X, the page hook also extracts non-reply posts from timeline responses X has already loaded. They are deduplicated by post ID in a local IndexedDB database; repeat sightings update metrics and exposure count instead of adding rows. The Settings tab can export this dataset as CSV/JSON or clear it. Collection is capped at the 50,000 most recently seen unique posts.

Endpoint discovery:

X periodically rotates its GraphQL queryId values. XPorter handles this by:

  1. Fetching the X main page HTML
  2. Scanning linked JS bundles for queryId + operationName patterns
  3. Caching discovered IDs for 24 hours (stale IDs self-heal on failure)
  4. Falling back to hardcoded IDs if discovery fails
  5. Automatically re-discovering on STALE_QUERY_ID (HTTP 400) errors

Rate limiting:

  • Five named speed presets target roughly 2 / 3 / 4 / 7 / 12 seconds between requests; Standard (~4 s) is the default
  • A Custom mode uses the exact request delay you enter; decimal seconds accept either . or ,
  • Optional Scheduled breaks are configured independently for Posts & Bookmarks and for User Lists; fractional minutes accept either . or ,
  • Valid x-rate-limit-* headers always take priority so XPorter stops at the live quota instead of overrunning it
  • Missing headers use conservative mode-specific fallback delays; 429s and network timeouts retry automatically
  • Stale GraphQL query IDs trigger live capture or endpoint re-discovery before the export fails

Export Output

Post exports include:

Field Description
id Tweet ID
text Full text (including long-form notes)
tweet_url Direct link
language ISO language code
type tweet · retweet · reply · quote
author_name Display name
author_username Handle (without @)
view_count Views
bookmark_count Bookmarks
favorite_count Likes
retweet_count Retweets
reply_count Replies
quote_count Quote tweets
created_at Timestamp
source Posting client
hashtags Comma-separated
urls Expanded URLs, comma-separated
media_type photo · video · animated_gif
media_urls Direct media URLs (highest quality)
media_alt_texts Author-written media descriptions, when present
article_title / article_url / article_text X Article metadata and available plain text

User-list exports include:

Field Description
id User ID
name Display name
username Handle without @
bio Profile description
location Profile location
url Profile website
followers_count Follower count
following_count Following count
tweet_count Post count
listed_count List count
verified Verification status
protected Protected/private status
created_at Account creation timestamp
profile_image_url Profile image URL
profile_url Direct X profile URL

Configuration

All settings are persisted in Chrome storage and reused across popup sessions.

Setting Default Description
Original posts On Export standalone posts written by the profile
Quotes On Export posts that add the author's comment above another post
Replies On Export the author's replies, including replies to other accounts; mixed selections use X's combined posts-and-replies feed and keep only the selected row types
Reposts On Export posts reshared without an added comment
Articles On Export X long-form Articles and the text X returns
Photos in Excel — Posts / Bookmarks Keep links / Keep links Store each mode independently: the recommended Links choice keeps source URLs only, while the other choice adds bounded previews to a separate Media sheet
Selection UI Post types, XLSX photo modes, and stopped/resumable states use official Tabler outline SVGs while keeping native keyboard-accessible controls
Export mode Posts Data type to export: posts, personal bookmarks, followers, following, or verified followers
Output format CSV File format: CSV, JSON, XLSX, or AI-friendly TXT for post-shaped exports
Quantity limit 500 Maximum posts or users per export (0 = unlimited); changing it retargets an ordinary active export
Export Speed Standard Turbo, Fast, Standard, Careful, Turtle, or Custom request pacing
Custom pacing 5 seconds Exact delay between requests; seconds may be fractional with . or ,
Scheduled breaks Off Optional independent break after every N requests; separate controls for Posts & Bookmarks and User Lists
Include “About this Account” details Off Add public account location, Premium date, connection source, affiliated account, and username-change history to user-list exports; requires up to one extra X request per user
About Details Speed Standard / 5 at once Control how many “About this Account” requests run concurrently; higher values are faster but more likely to hit X rate limits or a temporary restriction; Custom accepts 1–50
About rate-limit retries 5 / one minute apart Retry rejected “About this Account” requests; a successful request resets the counter, and the configurable range is 1–1,440 retries
Auto-clear old exports On / 4 hours Removes old downloadable payloads while keeping history metadata
Localize column titles On Translate CSV/XLSX headers; JSON keys always remain English

Preset quantity options: 100, 500, 1,000, unlimited, or a custom value.

Supported Languages

The UI auto-detects your Chrome language on first launch and can be changed at any time via the header dropdown.

Language Code Language Code
English en Français fr
Español es Deutsch de
Português pt 日本語 ja
हिन्दी hi 한국어 ko
中文 zh Türkçe tr
Русский ru Bahasa Indonesia id
العربية ar Italiano it

Project Structure

xporter/
├── manifest.json             # Manifest V3 configuration
├── agent.md / CLAUDE.md      # Developer & AI context docs
├── background/
│   ├── service-worker.js     # Export engine, message router, state machine
│   ├── downloads.js          # File serialization + Chrome download handoff
│   └── uninstall-feedback.js # Anonymous uninstall-summary URL
├── content/
│   ├── feed-parser.js        # Extracts compact non-reply post rows from page responses
│   ├── content.js            # Username detection from the active X tab
│   └── interceptor.js        # Page-context hook for GraphQL IDs and seen-post capture
├── popup/                    # Compact popup UI
│   ├── popup.html/.css/.js   # Markup, glassmorphism styles (dark + light), logic
│   ├── theme-init.js/theme.js# Theme bootstrap (anti-FOUC) + toggle
│   ├── i18n.js               # In-app translation engine
│   ├── rate-prompt.js/.css   # "Rate XPorter" prompt
│   ├── history.js            # Export-history UI
│   ├── seen-posts.js         # Passive seen-post dataset UI
│   ├── ladybug.js            # Easter-egg ladybug on the About tab
│   └── locales/*.json        # UI strings for 14 languages (en = fallback)
├── utils/
│   ├── api.js                # X GraphQL client, endpoint discovery
│   ├── api-parsers.js        # Pure X response parsers
│   ├── api-features.js       # GraphQL feature-flag constants
│   ├── config.js             # Tunable constants + logger
│   ├── rateLimit.js          # Batch rate limiter with cooldowns
│   ├── csv.js                # CSV / XLSX generation (JSON is built in the worker)
│   ├── columns-i18n.js       # Localized CSV/XLSX column headers
│   ├── storage.js            # Chrome storage abstraction + settings
│   ├── post-database.js      # Deduplicated seen-post IndexedDB store
│   ├── usage-tracker.js      # Anonymous local usage counters (opens, active time)
│   └── shared.js             # Shared popup/UI helpers
├── _locales/                 # Chrome Web Store metadata translations
├── icons/                    # icon16/48/128.png + bolt16/48/128.png (toolbar action icons)
├── docs/                     # GitHub Pages site (landing, feedback, privacy policy)
└── scripts/                  # Dev/debug scripts + CWS packaging (not shipped)

Design Decisions

  • Manifest V3 — service workers instead of persistent background pages
  • No bundler — load directly from source; no webpack, no Vite
  • No npm dependencies — the entire extension is vanilla JS
  • Incremental persistence — export items are saved in batches of 50 to prevent data loss on service worker termination
  • BOM-prefixed CSV — ensures correct Unicode rendering in Excel

Privacy

  • All exported data stays local — your X data never leaves your browser
  • The seen-post dataset is stored only in local IndexedDB and can be exported or cleared from Settings
  • No third-party analytics, advertising, or tracking SDKs
  • No extension-owned backend; normal export traffic goes only to X.com
  • Authentication uses your existing X session cookies — XPorter never stores or transmits credentials
  • One exception: when you uninstall XPorter, an anonymous usage summary (no X data, no usernames, nothing that identifies you) is sent once to help improve the extension — see the live privacy policy for exactly what it contains

Contributing

Contributions are welcome. Please open an issue for bugs or feature requests, or submit a pull request directly.

Development and verification

The extension has no build step or npm dependency installation. After changing runtime code or documentation, run the canonical deterministic gate from the repository root:

node scripts/test-all.js
git diff --check

Opening generated XLSX files in LibreOffice is checked separately with node scripts/test-xlsx-libreoffice.js. This requires LibreOffice and fails explicitly if it is missing or cannot complete the conversion. Release packaging runs both checks. CI tests four timezones and has a separate LibreOffice compatibility job. See Testing.

For focused iteration, the individual scripts/test-*.js files remain directly runnable; test-all.js is the required complete gate and the source of truth for their order.

For a real unpacked-Chromium check, run node scripts/test-extension-smoke.mjs from a normal Terminal session or an approved unsandboxed environment. Do not launch it inside CODEX_SANDBOX: macOS can abort GUI-backed Chromium during LaunchServices registration, and the script intentionally fails fast there.

Build a Chrome Web Store archive only with:

scripts/package.sh

The packager reads the version from manifest.json, runs the deterministic gates, and writes an allowlist-only ZIP beside the repository.

Passing local tests proves repository contracts such as syntax, parsers, persistence, pacing, localization structure, and file generation. It does not prove that X's current private endpoints, query IDs, cookies, feature flags, or live response shapes still work. That requires a separate authenticated live-X verification and should be reported independently from deterministic results.

Contact

Disclaimer

This project is not affiliated with, endorsed by, or connected to X Corp. It is an independent tool that uses your own authenticated browser session to export publicly available data. Use in accordance with applicable terms of service.

License

Licensed under the MIT License with Commons Clause.

You are free to use, modify, and redistribute this software at no cost. You may not sell it, offer it as a paid service, or publish paid derivatives on browser extension marketplaces.

See LICENSE for full terms.


Built by Lemelson

About

Free, unlimited export of X (Twitter) posts, followers & following to CSV/JSON/XLSX. Chrome extension, Manifest V3, zero dependencies — the exact code that ships to the Web Store.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages