Tweeting Media with v2 of the Twitter API in Python

Python
Media

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.

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 Tweets 
  • users.read allows you to obtain information about users
  • tweet.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.

true

Next steps

Hopefully, this can be a starting point for you to get started with adding media using v2 of the Twitter API. We are hoping to add v2 media support to the Twitter API shortly to make this process easier in the future.

Be sure to inform us on the forums if you run into any troubles along the way, or Tweet us at @TwitterDev if this tutorial inspires you to create anything.