How to delete all of your Tweets - using Go
8min read"Software needs to be built such that it will allow us to forget." - Alex Schroeder
When a former colleague mentioned in passing that he stumbled upon my Twitter, I thought to myself, “what else about me and my thoughts is accessible to the Internet?”. I’m not necessarily concerned about things that I’ve said publicly on Twitter, but I did feel to a certain degree that my stream of thought shouldn’t be so indexable and searchable, especially in the age of Internet-content-trained Large Language Models.
Since Twitter has announced that it will end free access to its API on the 9th of February 2023, this finally gave me the impulse to do a Twitter purge.
There are many tools out there that can help with deleting old tweets, like Twitter Archive Eraser or TweetDelete, though they are either paid for, or have a deletion limit of 100 Tweets per month. Additionally, I didn’t want to give permissions to yet another third party application to access my Twitter data.
Before we start, I highly recommend that you ask Twitter for an archive of your data, info available at this link.
However, I’m aware that this might take up to 24 hours, so I’ve added some code snippets to allow you to save your tweet text locally.
Building a Golang purge script
Fortunately, with the Twitter API still very much free, and my limited Golang knowledge, I set out to build a purge script, and I’ll show you how I did it here.
The Twitter API
Firstly, we’re going to have to generate some API keys to talk to Twitter programmatically.
Open up a web browser and navigate to:
https://developer.twitter.com
You should see “Developer Portal” right at the top, on the right-hand side next to your profile picture. Click that to navigate to your Twitter API Dashboard.
Now let’s generate some API keys: Click on “Get Access” and follow the flow to create an App (you can name it whatever, and provide whatever reasons for using Twitter API, this App is just for your eyes only).
Make sure to save the API Key and Secret generated from this step, as this will be your Consumer Key
and Consumer Secret
in later stages.
After you’ve created your app, we are going to give it write permission, so that it can delete your Tweets for you.
Navigate to your App
-> Settings
-> User authentication settings
-> Set up
.
Under
App Permissions
, make sure thatRead and Write
is selected from the list.Ignore
Type of App
andApp Info
, filling in whatever passes validation, as we are not going to be using theOAuth2.0
authentication flow.
After you’re done with this, the last step is to generate your Personal API Access Token and Secret.
On the tab bar, below your App name, navigate to Keys and tokens
.
Under Authentication Tokens
-> Access Token and Secret
, hit Generate
.
In the pop-up screen, save the Access Token
and Access Token Secret
.
Writing the Golang app
In a new folder, initialise a new Golang module
mkdir -p twitter_purge/
cd twitter_purge/
go mod init example.com/twitter_purge/
We are going to use the following libraries:
$ go get -u github.com/dghubble/oauth1 \
github.com/Xcod3bughunt3r/Go-Twitter/twitter \
github.com/joho/godotenv
go: added github.com/Xcod3bughunt3r/Go-Twitter v0.0.0-20220806141616-f9566d8efbe0
go: added github.com/cenkalti/backoff/v4 v4.2.0
go: added github.com/dghubble/oauth1 v0.7.2
go: added github.com/dghubble/sling v1.4.1
go: added github.com/google/go-querystring v1.1.0
go: added github.com/joho/godotenv v1.5.1
After installing all the libraries, store your tokens and secrets so that your script can access them.
In the root of your module, create a .env
file, and populate it as such:
TWITTER_CONSUMER_KEY=<Consumer Key saved when we generated our app>
TWITTER_CONSUMER_SECRET=<Consumer Secret saved when we generated our app>
TWITTER_ACCESS_KEY=<Personal Access Token>
TWITTER_ACCESS_SECRET=<Personal Access Token Secret>
TWITTER_USER_HANDLE=<Your Personal User Handle on Twitter>
Now that we’re all set up, let’s get to purging.
In the main
function of your script, let’s load up all our keys into our environment, and create a Twitter client using those keys:
func main() {
err := godotenv.Load()
if err != nil {
panic(err)
}
config := oauth1.NewConfig(
os.Getenv("TWITTER_CONSUMER_KEY"),
os.Getenv("TWITTER_CONSUMER_SECRET"),
)
token := oauth1.NewToken(
os.Getenv("TWITTER_ACCESS_KEY"),
os.Getenv("TWITTER_ACCESS_SECRET"),
)
httpClient := config.Client(oauth1.NoContext, token)
client := twitter.NewClient(httpClient)
}
Now let’s fetch all of our Tweets. We want to be thorough, so let’s grab both Retweets and Replies. Also, let’s save a copy of our tweets before we delete them.
Note: you can include/exclude retweets and replies using the IncludeRetweets and ExcludeReplies parameters below
func BoolPointer(b bool) *bool {
return &b
}
// Let's save our Tweets locally as we delete them.
f, err := os.Create("tweets.csv")
if err != nil {
panic(err)
}
defer f.Close()
userTimelineParams := &twitter.UserTimelineParams{
ScreenName: os.Getenv("TWITTER_USER_HANDLE"),
IncludeRetweets: BoolPointer(true), // Params require a pointer
ExcludeReplies: BoolPointer(false), // Params require a pointer
Count: 200, // Max allowed by API
}
// Initialise our first batch of Tweets
tweets, _, err := client.Timelines.UserTimeline(userTimelineParams)
Fantastic! We now have our first batch of Tweets in scope for deletion. However, since these contain a mix of Retweets and our own Tweets, we will have to deal with them accordingly:
- for our own Tweets, we will use the
/destroy
endpoint - for Retweets, we will call the
/unretweet
endpoint
Also, since the Twitter v1.1 API only lets us fetch 200 Tweets at a time, we’re going to have to batch up our requests.
// Iterate until there are no tweets left, fetching new tweets
for ; len(tweets) > 0; tweets, _, _ = client.Timelines.UserTimeline(userTimelineParams) {
for _, tweet := range tweets {
fmt.Println("ID=", tweet.IDStr, " Text=", tweet.Text, " StatusesCount=", statusesCount, "Retweet=", tweet.Retweeted)
if tweet.Retweeted {
_, _, err = client.Statuses.Unretweet(tweet.ID, &twitter.StatusUnretweetParams{
ID: tweet.ID,
TrimUser: BoolPointer(true),
})
} else {
f.WriteString(tweet.IDStr + "," + tweet.Text + "\n")
_, _, err = client.Statuses.Destroy(tweet.ID, &twitter.StatusDestroyParams{
ID: tweet.ID,
TrimUser: BoolPointer(true),
})
}
time.Sleep(1 * time.Second) // prevents rate limiting
}
}
And that’s it! The full code is available here on Github - bruvduroiu/tweet_purge
We can now run our app and watch as our tweets get deleted or unretweeted one by one! Clean bliss!
IMPORTANT: This is a destructive operation, make sure you back up everything you don’t want to lose before proceeding!
go run .
And after approximately 2-3 mins, the result is…
Hope you found this article useful, and you enjoy a clean Twitter slate.