目次
TweetXer: Delete Undeletable Reposts on X(旧Twitter)
As often seen with old posts on X,
Sometimes the repost icon, which should be green when reposted, turns gray (grayed out), and you cannot undo the repost.
https://github.com/lucahammer/tweetXer
If you want to delete this kind of repost, you can use a UserScript called TweetXer.
Technically, it is a script that **can delete all posts including reposts**, and it has the advantage of being very easy to run for the following reasons:
- No API Key required
- On PC, it works just by using Developer Tools
It is troublesome to prepare an environment to run the API just to delete posts.
You can run it from a smartphone using Tampermonkey, but working on a PC is recommended for the following reasons:
- Need to download all X data, which has a large file size
- Need to edit `tweet-headers.js` if you want to delete specific posts
I wrote this article because searching for "cannot undo repost X" only brings up irrelevant articles.
Especially false articles claiming "you can't undo it if there is an edit history."
Posts from non-paying accounts are also grayed out, so that claim makes no sense.
How to Use
Since there are official instructions and videos, here is a brief summary:
- Download your full data from X
"Settings" -> "Your account" -> "Download an archive of your data"
- Unzip the downloaded ZIP archive
-
Wait for TweetXer to display: "Select your tweet-headers.js from your Twitter Data Export to start the deletion of all your Tweets."
- Load `tweet-headers.js` found in the unzipped archive via the "Browse" button in TweetXer
- All posts will be deleted
If the Tampermonkey version of TweetXer is enabled, it forces you to the home screen. This is by design to retrieve information necessary for deletion.
Image showing an old repost deleted.
In reality, I don't think many people want to delete *all* their posts, so you will likely need to edit `tweet-headers.js`.
Since it involves editing a JSON file, deleting specific posts or reposts can be quite troublesome.
If it's just a few, you can do it manually, but if there are many, you will likely need to use Python or similar on a PC.
As of July 2025, the latest version of TweetXer does not work
Doesn't work, bar doesn't move and not tweets deleted · Issue #38 · lucahammer/tweetXer
The above Issue has been raised.
Past versions work, so use this one until it is fixed:
It may not work no matter what
Even if you follow the exact same steps, it may not work.
The success rate seems to be about 1/5?
I don't know if it's a restriction by X, but looking at the Developer Tools on PC, an HTTP status 400 is returned most of the time.
This is not uncommon, so try again after waiting for a while.
Python script to create tweet-headers.js from a file containing only the IDs of posts you want to delete
This is almost impossible to execute without a PC, so read on only if you are interested.
The URL of a post on X is in the following format:
https://x.com/[username]/status/[1945123456789]
I have prepared a Python script that creates `tweet-headers.js` if you prepare a file with only this number part.
- Prepare a file named `delete-ids.txt` containing only the trailing numbers of the posts you want to delete.
Example:
1945123456789
1946123456789
1947123456789
...
- Place the following Python file and `delete-ids.txt` in the same directory as `tweet-headers.js`.
- Run `python ./delete.py`.
Just by doing this, it will export `tweet-headers.js` as `filtered-headers.js` based on the post IDs, so just load that into TweetXer.
import json
import re
from pathlib import Path
# Extract tweet_id from file (Supports both JSON format and plain number lines)
def load_ids_from_mixed_json(file_path):
id_set = set()
with open(file_path, encoding="utf-8") as f:
for line in f:
line = line.strip()
# Case where line contains JSON like {"tweet_id": "123456789"}
match = re.search(r'"tweet_id"s*:s*"(d+)"', line)
if match:
id_set.add(match.group(1))
# Case where line is just a number
elif re.fullmatch(r"d{5,}", line): # Minimal digit check
id_set.add(line)
return id_set
# Extract content only from JS file
def extract_json_array(file_path):
with open(file_path, encoding="utf-8") as f:
js = f.read()
match = re.search(r'=s*([s*{.*}s*]);?s*$', js, re.DOTALL)
if not match:
raise ValueError(f"Cannot parse JSON array from: {file_path}")
return json.loads(match.group(1))
# Load data
def load_tweet_headers(path):
return {item["tweet"]["tweet_id"]: item["tweet"] for item in extract_json_array(path)}
# Filter headers based on id_set
def filter_headers(headers, id_set):
return [{"tweet": tweet} for id_, tweet in headers.items() if id_ in id_set]
# Execution
def main():
input_path = Path("delete-ids.txt")
headers_path = Path("tweet-headers.js")
output_path = Path("filtered-headers.js")
id_set = load_ids_from_mixed_json(input_path)
headers = load_tweet_headers(headers_path)
filtered = filter_headers(headers, id_set)
# Add 'window.YTD.tweet_headers.part0 =' to the beginning
with open(output_path, "w", encoding="utf-8") as f:
f.write("window.YTD.tweet_headers.part0 = ")
json.dump(filtered, f, ensure_ascii=False, indent=2)
print(f"{len(filtered)} tweets have been extracted.")
if __name__ == "__main__":
main()





