Skip to content

Instantly share code, notes, and snippets.

@ShawnaRStaff
Created December 31, 2025 13:13
Show Gist options
  • Select an option

  • Save ShawnaRStaff/2841cce99928bd5436ed908cc7633d33 to your computer and use it in GitHub Desktop.

Select an option

Save ShawnaRStaff/2841cce99928bd5436ed908cc7633d33 to your computer and use it in GitHub Desktop.
delete x media
// Standalone script to delete all media tweets
// Run this on x.com (any page while logged in)
var authorization = "Bearer ";
var client_tid = "";
var random_resource_media = "";
var username = "";
var ua = navigator.userAgentData.brands.map(brand => `"${brand.brand}";v="${brand.version}"`).join(', ');
var client_uuid = crypto.randomUUID();
var csrf_token = document.cookie.split('; ').find(row => row.startsWith('ct0=')).split('=')[1];
var user_id = decodeURIComponent(document.cookie.split('; ').find(row => row.startsWith('twid=')).split('=')[1]).substring(2);
var language_code = navigator.language.split("-")[0];
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function buildAcceptLanguageString() {
const languages = navigator.languages;
if (!languages || languages.length === 0) return "en-US,en;q=0.9";
let q = 1;
return languages.map(lang => {
if (q < 1) {
const result = `${lang};q=${q.toFixed(1)}`;
q -= 0.1;
return result;
}
q -= 0.1;
return lang;
}).join(',');
}
async function fetch_media(cursor, retry = 0) {
let count = "20";
let final_cursor = cursor ? `%22cursor%22%3A%22${cursor}%22%2C` : "";
var base_url = `https://x.com/i/api/graphql/${random_resource_media}/UserMedia`;
var variable = `?variables=%7B%22userId%22%3A%22${user_id}%22%2C%22count%22%3A${count}%2C${final_cursor}%22includePromotedContent%22%3Afalse%2C%22withClientEventToken%22%3Afalse%2C%22withBirdwatchNotes%22%3Afalse%2C%22withVoice%22%3Atrue%7D`;
var feature = `&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticlePlainText%22%3Afalse%7D`;
const response = await fetch(base_url + variable + feature, {
headers: {
"accept": "*/*",
"accept-language": buildAcceptLanguageString(),
"authorization": authorization,
"content-type": "application/json",
"x-client-transaction-id": client_tid,
"x-client-uuid": client_uuid,
"x-csrf-token": csrf_token,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": language_code
},
credentials: "include"
});
if (!response.ok) {
if (response.status === 429) {
console.log("Rate limit reached. Waiting 1 minute...");
await sleep(60000);
return fetch_media(cursor, retry + 1);
}
if (retry >= 5) throw new Error("Max retries reached fetching media");
console.log(`Fetch error (${response.status}), retrying in ${10 * (1 + retry)}s...`);
await sleep(10000 * (1 + retry));
return fetch_media(cursor, retry + 1);
}
const data = await response.json();
let entries = data?.data?.user?.result?.timeline_v2?.timeline?.instructions
|| data?.data?.user?.result?.timeline?.timeline?.instructions;
if (!entries) {
console.log("Response:", data);
throw new Error("Could not parse media response");
}
for (let item of entries) {
if (item.type === "TimelineAddEntries") {
console.log("Raw entries:", item.entries.map(e => e.entryId));
return item.entries;
}
}
console.log("No TimelineAddEntries found. Instructions:", entries.map(e => e.type));
return [];
}
async function delete_tweet(tweet_id, retry = 0) {
const response = await fetch("https://x.com/i/api/graphql/VaenaVgh5q5ih7kvyVjgtg/DeleteTweet", {
method: "POST",
headers: {
"accept": "*/*",
"accept-language": buildAcceptLanguageString(),
"authorization": authorization,
"content-type": "application/json",
"x-client-transaction-id": client_tid,
"x-client-uuid": client_uuid,
"x-csrf-token": csrf_token,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": language_code
},
body: JSON.stringify({
variables: { tweet_id: tweet_id, dark_request: false },
queryId: "VaenaVgh5q5ih7kvyVjgtg"
}),
credentials: "include"
});
if (!response.ok) {
if (response.status === 429) {
console.log("Rate limit on delete. Waiting 1 minute...");
await sleep(60000);
return delete_tweet(tweet_id, retry + 1);
}
if (retry >= 3) {
console.log(`Failed to delete ${tweet_id} after retries`);
return false;
}
await sleep(5000);
return delete_tweet(tweet_id, retry + 1);
}
return true;
}
// Main execution
(async function() {
console.log("🚀 Starting to delete all media tweets...");
console.log("User ID:", user_id);
let totalDeleted = 0;
let rounds = 0;
let emptyRounds = 0;
const maxEmptyRounds = 3; // Stop after 3 consecutive empty fetches
while (emptyRounds < maxEmptyRounds) {
rounds++;
console.log(`\n=== Round ${rounds} ===`);
// Always start fresh (no cursor) to get current media
console.log("Fetching media tweets...");
const entries = await fetch_media(null);
// Find tweet IDs
let tweetIds = [];
for (const entry of entries) {
if (entry.entryId.startsWith("tweet-")) {
tweetIds.push(entry.entryId.replace("tweet-", ""));
} else if (entry.entryId.startsWith("profile-grid-")) {
const items = entry.content?.items || [];
console.log(`Found ${items.length} items in profile-grid`);
for (const item of items) {
const itemId = item.item?.itemContent?.tweet_results?.result?.rest_id
|| item.item?.itemContent?.tweet_results?.result?.tweet?.rest_id
|| item.item?.itemContent?.tweet_results?.result?.legacy?.id_str;
if (itemId) {
tweetIds.push(itemId);
}
}
}
}
console.log(`Found ${tweetIds.length} media tweets to delete`);
if (tweetIds.length === 0) {
emptyRounds++;
console.log(`No media found (empty round ${emptyRounds}/${maxEmptyRounds})`);
await sleep(3000);
continue;
}
emptyRounds = 0; // Reset empty counter when we find tweets
// Delete each tweet
for (let i = 0; i < tweetIds.length; i++) {
const success = await delete_tweet(tweetIds[i]);
if (success) {
totalDeleted++;
console.log(`Deleted ${totalDeleted}: ${tweetIds[i]}`);
} else {
console.log(`Failed to delete: ${tweetIds[i]}`);
}
await sleep(150);
}
console.log(`Round ${rounds} complete. Total deleted so far: ${totalDeleted}`);
await sleep(2000);
}
console.log(`\n✅ ALL DONE! Deleted ${totalDeleted} media tweets in ${rounds} rounds.`);
})();
// Standalone script to delete all media tweets
// Run this on x.com (any page while logged in)
var authorization = "Bearer ";
var client_tid = "";
var random_resource_media = "";
var username = "";
var ua = navigator.userAgentData.brands.map(brand => `"${brand.brand}";v="${brand.version}"`).join(', ');
var client_uuid = crypto.randomUUID();
var csrf_token = document.cookie.split('; ').find(row => row.startsWith('ct0=')).split('=')[1];
var user_id = decodeURIComponent(document.cookie.split('; ').find(row => row.startsWith('twid=')).split('=')[1]).substring(2);
var language_code = navigator.language.split("-")[0];
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function buildAcceptLanguageString() {
const languages = navigator.languages;
if (!languages || languages.length === 0) return "en-US,en;q=0.9";
let q = 1;
return languages.map(lang => {
if (q < 1) {
const result = `${lang};q=${q.toFixed(1)}`;
q -= 0.1;
return result;
}
q -= 0.1;
return lang;
}).join(',');
}
async function fetch_media(cursor, retry = 0) {
let count = "20";
let final_cursor = cursor ? `%22cursor%22%3A%22${cursor}%22%2C` : "";
var base_url = `https://x.com/i/api/graphql/${random_resource_media}/UserMedia`;
var variable = `?variables=%7B%22userId%22%3A%22${user_id}%22%2C%22count%22%3A${count}%2C${final_cursor}%22includePromotedContent%22%3Afalse%2C%22withClientEventToken%22%3Afalse%2C%22withBirdwatchNotes%22%3Afalse%2C%22withVoice%22%3Atrue%7D`;
var feature = `&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticlePlainText%22%3Afalse%7D`;
const response = await fetch(base_url + variable + feature, {
headers: {
"accept": "*/*",
"accept-language": buildAcceptLanguageString(),
"authorization": authorization,
"content-type": "application/json",
"x-client-transaction-id": client_tid,
"x-client-uuid": client_uuid,
"x-csrf-token": csrf_token,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": language_code
},
credentials: "include"
});
if (!response.ok) {
if (response.status === 429) {
console.log("Rate limit reached. Waiting 1 minute...");
await sleep(60000);
return fetch_media(cursor, retry + 1);
}
if (retry >= 5) throw new Error("Max retries reached fetching media");
console.log(`Fetch error (${response.status}), retrying in ${10 * (1 + retry)}s...`);
await sleep(10000 * (1 + retry));
return fetch_media(cursor, retry + 1);
}
const data = await response.json();
let entries = data?.data?.user?.result?.timeline_v2?.timeline?.instructions
|| data?.data?.user?.result?.timeline?.timeline?.instructions;
if (!entries) {
console.log("Response:", data);
throw new Error("Could not parse media response");
}
for (let item of entries) {
if (item.type === "TimelineAddEntries") {
console.log("Raw entries:", item.entries.map(e => e.entryId));
return item.entries;
}
}
console.log("No TimelineAddEntries found. Instructions:", entries.map(e => e.type));
return [];
}
async function delete_tweet(tweet_id, retry = 0) {
const response = await fetch("https://x.com/i/api/graphql/VaenaVgh5q5ih7kvyVjgtg/DeleteTweet", {
method: "POST",
headers: {
"accept": "*/*",
"accept-language": buildAcceptLanguageString(),
"authorization": authorization,
"content-type": "application/json",
"x-client-transaction-id": client_tid,
"x-client-uuid": client_uuid,
"x-csrf-token": csrf_token,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"x-twitter-client-language": language_code
},
body: JSON.stringify({
variables: { tweet_id: tweet_id, dark_request: false },
queryId: "VaenaVgh5q5ih7kvyVjgtg"
}),
credentials: "include"
});
if (!response.ok) {
if (response.status === 429) {
console.log("Rate limit on delete. Waiting 1 minute...");
await sleep(60000);
return delete_tweet(tweet_id, retry + 1);
}
if (retry >= 3) {
console.log(`Failed to delete ${tweet_id} after retries`);
return false;
}
await sleep(5000);
return delete_tweet(tweet_id, retry + 1);
}
return true;
}
// Main execution
(async function() {
console.log("🚀 Starting to delete all media tweets...");
console.log("User ID:", user_id);
let totalDeleted = 0;
let rounds = 0;
let emptyRounds = 0;
const maxEmptyRounds = 3; // Stop after 3 consecutive empty fetches
while (emptyRounds < maxEmptyRounds) {
rounds++;
console.log(`\n=== Round ${rounds} ===`);
// Always start fresh (no cursor) to get current media
console.log("Fetching media tweets...");
const entries = await fetch_media(null);
// Find tweet IDs
let tweetIds = [];
for (const entry of entries) {
if (entry.entryId.startsWith("tweet-")) {
tweetIds.push(entry.entryId.replace("tweet-", ""));
} else if (entry.entryId.startsWith("profile-grid-")) {
const items = entry.content?.items || [];
console.log(`Found ${items.length} items in profile-grid`);
for (const item of items) {
const itemId = item.item?.itemContent?.tweet_results?.result?.rest_id
|| item.item?.itemContent?.tweet_results?.result?.tweet?.rest_id
|| item.item?.itemContent?.tweet_results?.result?.legacy?.id_str;
if (itemId) {
tweetIds.push(itemId);
}
}
}
}
console.log(`Found ${tweetIds.length} media tweets to delete`);
if (tweetIds.length === 0) {
emptyRounds++;
console.log(`No media found (empty round ${emptyRounds}/${maxEmptyRounds})`);
await sleep(3000);
continue;
}
emptyRounds = 0; // Reset empty counter when we find tweets
// Delete each tweet
for (let i = 0; i < tweetIds.length; i++) {
const success = await delete_tweet(tweetIds[i]);
if (success) {
totalDeleted++;
console.log(`Deleted ${totalDeleted}: ${tweetIds[i]}`);
} else {
console.log(`Failed to delete: ${tweetIds[i]}`);
}
await sleep(150);
}
console.log(`Round ${rounds} complete. Total deleted so far: ${totalDeleted}`);
await sleep(2000);
}
console.log(`\n✅ ALL DONE! Deleted ${totalDeleted} media tweets in ${rounds} rounds.`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment