===========================
Bogdan Buduroiu
===========================

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.

Navigate to the Twitter Developer Portal

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.

Create a new app to get access keys

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 that Read and Write is selected from the list.

  • Ignore Type of App and App Info, filling in whatever passes validation, as we are not going to be using the OAuth2.0 authentication flow.

Grant Read and Write permissions to your app

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.

Generate an Access Token and Secret

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…

Clean result afterwards

Hope you found this article useful, and you enjoy a clean Twitter slate.