By Jessica Garson
With the launch of the manage Tweets endpoint, we included the ability to attach previously uploaded media to a Tweet. In addition, based on feedback we’ve heard from you, we recently added access to the v1 media endpoints for all users.
This tutorial will walk you through how to Tweet images of cats using the manage Tweets endpoint of v2 and the v1.1 media endpoints in Python using Flask. Using OAuth 2.0. Authorization Code Flow with PKCE to request the Manage Tweets endpoint requires a user to log in. Therefore, you can use Flask as a web framework to parse the information dynamically.
By the end of this tutorial, you will be able to Tweet a cat picture from your Twitter handle. This code sample is similar to the one I used to add image support to Factual Cat, my bot that Tweets cat pictures and facts about cats. Check out the full code on GitHub.
Endpoints
Resources

Getting started with the Twitter API
If you don’t already have access to the Twitter API, you can sign up for a developer account. Once you have a developer account, you must create a Project in the developer portal. Each Project contains an App with which you can generate the credentials required to use the Twitter API. You will also need to have OAuth 2.0 turned on in your App’s settings. In our platform overview, you can learn more about getting started with the Twitter API.
Installing packages
Before you start, you must install the required packages for this script, including requests
for making HTTP requests to various endpoints, tweepy
for working with OAuth 1.0a, requests_oauthlib
for working with OAuth 2.0, and flask
as a web framework.
pip install requests tweepy requests_oauthlib flask
You will also want to save a file as app.py
in your chosen code editor.
Importing packages
We will import these packages plus hashlib
, base64
, re
for creating a code verifier and challenge, and os
for parsing environment variables.
import base64
import hashlib
import os
import re
import requests
import tweepy
from requests_oauthlib import OAuth2Session
from flask import Flask, request, redirect, session, url_for, render_template
Setting up your Flask app
To start, you will need to set up your application as a Flask app. After that, you will need to set a secret key for your app.
app = Flask(__name__)
app.secret_key = os.urandom(50)
Variables for authenticating with OAuth 2.0
You will also want to set up environment variables to ensure you aren’t passing in your secrets directly. Since we’ll be using environment variables, you will want to ensure you have no other configuration files that might cause an override.
In your terminal, you will want to define the following environment variables:
export CLIENT_ID=’xxxxxxxxxxxxxx’
export CLIENT_SECRET=’xxxxxxxxxxx’
export REDIRECT_URI=’http://127.0.0.1:5000/oauth/callback’
You can obtain your own OAuth 2.0 Client ID and Secret inside of the developer settings in the developer portal.
You will want to use your Client ID and Secret instead of the value xxxxxxxxxxxxxx
.
I am currently using the type of App as a public client, a single-page App. Inside your App’s OAuth settings in the Developer Portal, you will want to ensure that you also have the above redirect URI defined for locally testing your application.
In your Python file, you’ll want to set up variables to get your environment variables for your client_id
and client_secret
. You will also want to define variables for the authorization URL as auth_url and the URL for obtaining your OAuth 2.0 token as token_url. You will also want to get the environment variable you set for your redirect URI and pass that into a new variable called redirect_uri
.
client_id = os.environ.get("CLIENT_ID")
client_secret = os.environ.get("CLIENT_SECRET")
auth_url = "https://twitter.com/i/oauth2/authorize"
token_url = "https://api.x.com/2/oauth2/token"
redirect_uri = os.environ.get("REDIRECT_URI")
To define your App's permissions, set your application's scopes. Check out the authentication mapping guide to determine what scopes you will need based on the endpoints you are using. The scopes for this demo are tweet.read
, users.read
, tweet.write
.
tweet.read
allows you to read Tweetsusers.read
allows you to obtain information about userstweet.write
allows you to create Tweets
scopes = ["tweet.read", "users.read", "tweet.write"]
Since Twitter’s implementation of OAuth 2.0 is PKCE-compliant, you will need to set a code verifier, a secure random string. The code verifier is used to create the code challenge. The code challenge is a base64 encoded string of the SHA256 hash of the code verifier.
code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8")
code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier)
code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest()
code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")
code_challenge = code_challenge.replace("=", "")
Uploading media
Now that you have your variables for connecting to OAuth 2.0 set up, you can now define a function for working with the media.
We will be using OAuth 1.0a to get access to the v1.1 media endpoint. The following code will work if you will be authenticating on behalf of yourself. If you want to authenticate on behalf of another user, you would need to log in with Twitter flow.
In your terminal, you will want to define the following environment variables for working with OAuth 1.0a. You can get these credentials from the “keys and tokens” section of your App’s settings.
export API_KEY=’xxxxxxxxxxxxxx’
export API_SECRET=’xxxxxxxxxxx’
export ACCESS_TOKEN=’xxxxxxxxxxx’
export ACCESS_TOKEN_SECRET=’xxxxxxxxxxx’
Back in your code editor, you can use Tweepy, a Python library for working with the Twitter API, to handle the OAuth 1.0a flow and authenticate to the Twitter API. To get cat pictures you can use the cat API to get a random picture and save the picture locally as catpic.jpg
. You can use the simple_upload function from Tweepy to upload your image to the Twitter API. You can also turn the response into a string and use regex to find the media ID. You can then pass that into a dictionary for attaching the image to the Tweet you want to post. Finally, you will want to remove the catpic.jpg
file you saved locally.
def upload_media():
tweepy_auth = tweepy.OAuth1UserHandler(
"{}".format(os.environ.get("API_KEY")),
"{}".format(os.environ.get("API_SECRET")),
"{}".format(os.environ.get("ACCESS_TOKEN")),
"{}".format(os.environ.get("ACCESS_TOKEN_SECRET")),
)
tweepy_api = tweepy.API(tweepy_auth)
url = "https://api.thecatapi.com/v1/images/search"
cats = requests.request("GET", url).json()
cat_pic = cats[0]["url"]
img_data = requests.get(cat_pic).content
with open("catpic.jpg", "wb") as handler:
handler.write(img_data)
post = tweepy_api.simple_upload("catpic.jpg")
text = str(post)
media_id = re.search("media_id=(.+?),", text).group(1)
payload = {"media": {"media_ids": ["{}".format(media_id)]}}
os.remove("catpic.jpg")
return payload
Posting the Tweet
You can create another function for posting on behalf of the authenticated user that logs in. This function prints out that it’s Tweeting for debugging purposes and makes a request to the Twitter API.
def post_tweet(payload, new_token):
print("Tweeting!")
return requests.request(
"POST",
"https://api.x.com/2/tweets",
json=payload,
headers={
"Authorization": "Bearer {}".format(new_token["access_token"]),
"Content-Type": "application/json",
},
)
Authenticating on behalf of the user
You will want your application login page to be the first page you will visit when you run your application. @app.route
("/") indicates it’s the first landing page. This will be the page that an authenticated user logs into, and will be the start of the OAuth 2.0 consent flow. Here the user gives permission to an App to perform actions on their behalf.
@app.route("/")
def demo():
global twitter
twitter = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes)
authorization_url, state = twitter.authorization_url(
auth_url, code_challenge=code_challenge, code_challenge_method="S256"
)
session["oauth_state"] = state
return redirect(authorization_url)
Tying it together
After the user logs in, you can now use the code
variable from the URL response to obtain a token object that contains the access token. This allows us to connect to the manage Tweets endpoint. You can save the response of the upload_media
function into a variable called payload and call the post_tweet
function to Tweet the cat picture.
@app.route("/oauth/callback", methods=["GET"])
def callback():
code = request.args.get("code")
token = twitter.fetch_token(
token_url=token_url,
client_secret=client_secret,
code_verifier=code_verifier,
code=code,
)
payload = upload_media()
response = post_tweet(payload, token).json()
return response
if __name__ == "__main__":
app.run()
To run the file locally, you can run the following line:
python app.py
While testing, you will want to use an account that is tied to the keys and tokens you used for your media uploads. If you run into any errors or if you extend this code sample you may want to consider turning on Flask’s debug mode.