How to Embed a Tweet in a Blog Post using Twitter API and a Headless CMS

Created on Jul 6, 2022
Updated on Aug 1, 2022

Social media is a great way to engage your audience and drive traffic to your website. We can’t ignore the impact of social proof when it comes to business.

But how do you get the most out of your social media efforts into your blog posts that impact your business?

In this tutorial, I’ll show you how to embed a tweet in a blog post using the Twitter API and ButterCMS to get the most out of your Twitter marketing efforts.

But first, let’s get started by knowing what Tweepy is and why is it best paired with a headless CMS .

What is Tweepy API? and Why a Headless CMS?

Tweepy is a Twitter API Python wrapper that allows you to access Twitter data easily with Python.

We use it in this tutorial and pair it with a headless CMS, more specifically ButterCMS , because it’s they both are great backend tools; the headless ButterCMS is a great tool to build a blog, and Tweepy is a great tool to get data from Twitter.

You might ask, why would you want to embed a tweet on your website?

Benefits of Embedding a Tweet on Your Website

By embedding a tweet feed on your website/blog, you can make your customers feel more connected to your brand and let them engage more on your Twitter. Embedding tweets have many benefits, some of which are:

Tweepy + ButterCMS + Flask

ButterCMS has SDKs in many different languages; Python is one of them. You can integrate it with your Python web framework like Django and Flask. In this tutorial, we will use Flask as it is a lightweight microframework and easy to set up and configure especially in the application we’re building here which is a blog that has a tweet embedded in a post. To get that tweet, we need to use Twitter API; that’s why we’ll use Tweepy which is a Twitter API wrapper that is easy to use its API methods.

Let’s now get started in our tutorial prerequisites.


To follow along with this tutorial, I assume no knowledge of whatsoever of Tweepy and ButterCMS. It’s good to have a basic knowledge of Flask and how to change routes to configure endpoints of a Flask app, and a basic knowledge of how to deal with Jinja templates to be able to customize the views of HTML.

I also assume that you have your Twitter credentials from Twitter Developer Portal ; I mean the consumer API key and secret, and access token and secret.

Without further ado, let’s jump into the installation part.

Installation Guide

We only need three packages to install: Tweepy, ButterCMS, and Flask.

Let’s include that in the requirements.txt file used in this tutorial:


and it’s preferred to use Python 3.6.9. Let’s create a virtual environment first and activate it. Also upgrade pip to be 21.3.1 version.

$ python3 -m venv venv
$ . venv/bin/activate
$ pip install --upgrade pip

Let’s now install the dependencies using the following commands:

$ pip install -r requirements.txt

Project Structure

In this tutorial, the project we’re going to build is structured as follows:

├── app
│   ├──
│   ├──
│   ├── templates
│   │   └── blog
│   │       ├── author.html
│   │       ├── blog.html
│   │       ├── category.html
│   │       ├── post.html
│   │       └── tweet.html
│   └──
├── venv/*
└── requirements.txt

Disclaimer: Don’t worry! There is a repo for this project, check it out here .

Note that venv/ directory is trimmed here because it has too many subdirectories and files that are not of help to be shown in this demonstration.

Here is a breakdown of these files:

We’re now ready to start configuring the tweet feed that we will add to our blog post.

It’s important to separate the business logic from the presentation logic. That’s why in the following two sections, we will see how to embed the tweet feed and then how to present that tweet feed in our blog post.

Exploring the Oembed Object - Business Logic

In the following two sections, we will focus on two files:

Let’s start with this file:

import tweepy
import os

def get_api(**kwargs):
    """Gets the API object after authorization
    and authentication.
    :keyword tweepy_api_key: The consumer API key.
    :keyword tweepy_api_key_secret: The consumer API key secret.
    :keyword tweepy_access_token: The access token.
    :keyword tweepy_access_token_secret: The access token secret.
    :returns: The Tweepy API object.
    auth = tweepy.OAuthHandler(
    return tweepy.API(auth)

api = get_api(
resp = api.get_oembed(

The get_api() function authorizes and authenticates the Tweepy API with the credentials you have from Twitter Developer.

Finally, the resp is the response we get from the get_oembed Tweepy method which is a wrapper method for the oembed Twitter endpoint . As indicated from Twitter doc, it returns a single Tweet, specified by either a Tweet web URL or the Tweet ID, in an oEmbed -compatible format. The returned HTML snippet will be automatically recognized as an Embedded Tweet when Twitter’s widget JavaScript is included on the page .

If you want to experiment with this resp object, you can import json library and do the following:

print(json.dumps(resp, indent=4))

When you cd into app and run this file separately with python , you will see the following:

    "url": "",
    "author_name": "ButterCMS",
    "author_url": "",
    "html": "<blockquote class=\"twitter-tweet\"><p lang=\"en\" dir=\"ltr\">In most situations, the benefits of a <a href=\";ref_src=twsrc%5Etfw\">#SaaS</a> CMS solution will vastly outweigh those of its <a href=\";ref_src=twsrc%5Etfw\">#OnPremise</a> counterparts. But why is this case? And when is it not? Check out our latest blog post to find out. <a href=\"\"></a> <a href=\";ref_src=twsrc%5Etfw\">#contentmanagement</a> <a href=\";ref_src=twsrc%5Etfw\">#headlesscms</a> <a href=\"\"></a></p>&mdash; ButterCMS (@ButterCMS) <a href=\"\">May 4, 2022</a></blockquote>\n<script async src=\"\" charset=\"utf-8\"></script>\n",
    "width": 550,
    "height": null,
    "type": "rich",
    "cache_age": "3153600000",
    "provider_name": "Twitter",
    "provider_url": "",
    "version": "1.0"

So that tweet is from ButterCMS Twitter account. The object that is returned from the get_oembed method may have too much information that we need for the embedded tweet; its URL, author name, author URL, the html blockquote tag, and more.

What we need from this object is the value of that html key which contains the html blockquote tag that we want to embed into our blog post.

Now, let’s remove the print statement that we wrote to see what that object looks like and move into the next section to configure how that tweet would be viewed in Jinja template for our Flask app.

Embedding the Tweet in a Jinja Template - Presentation Logic

Let’s now see this file: app/templates/blog/tweet.html and know what it actually does:

{% block content %}
{% include "post.html" %}
<center>{{ response.get("html")|safe }}</center>
{% endblock %}

First, it’s an HTML file. But… it has kind of Python code in it! That’s what Jinja actually is. Simply put, Jinja2 is a templating engine that shows the text of the response.

So we want to show the value of the html object that is returned from the get_oembed() method. That’s why we get this response.get("html")|safe . Note that we use the |safe filter here to avoid automatic HTML escaping and mark the coming Python object to the template as safe.

We wrap that object with double curly braces {{ ... }} because this is a variable expression in Jinja2. And then we want the tweet to be centered using the <center> HTML tag.

We include the post.html file before that tweet feed. It’s up to you where to put the tweet feed though.

This is all wrapped in a Jinja2 block code.

Now, what we need to know is where that response variable is and how to pass it from a Flask endpoint.

Pointing to the Embedded Tweet Response in Flask

You need yourself where do you want to render the tweet feed. In our case, we will render it at the end of a specific blog post. This post is the example post that you have when you first get started with ButterCMS. So our focus in the app/ file will be on the show_post() function:

from .tweepy_api import api

def show_post(slug):
    response = client.posts.get(slug)
        post = response['data']
    except KeyError:
        # Post was not found
    if slug == "example-post":
        resp = api.get_oembed(
        return render_template('tweet.html', post=post, response=resp)
    return render_template('post.html', post=post)

Note, that we import api object from the file that exists in the same level. We could have imported the resp object directly though.

We retrieve the post from this function through the ButterCMS client and we check if the slug of that post is the example-post that we firstly have in our ButterCMS portal, we render the tweet.html template and pass two variables. Note, that response variable here is what we see in the tweet.html Jinja2 template and now it’s assigned to resp variable. We also pass post to the post variable which is needed in the post.html template that we included in the tweet.html template.

The logic here is just to demonstrate how to make point to the oembed object. You can do whatever you want depending on your use case.

Is it time to run the Flask application? Yes, except for one thing we need to learn.

Running the Flask Application

The main entry point to our Flask application is the script. One of the imports of this file is this:

from app import blog

So we import the blog instance to register a blueprint . But here we import it directly from app which is somehow weird in the structure of our project. Shouldn’t it be imported from app.routes ? Actually, yes it could be but we import it directly from app because we included the routes module in the script as the following:

from .routes import blog

Now, let’s run the app. Assuming you’re at the parent directory of our repo, configure the flask application first:

$ export

and now:

$ flask run

If you open your browser and head over to localhost:5000, you’ll see the following:

which indicates the home page of our Flask app. Now, let’s turn into the blog/ endpoint:

and then click on the example post link:

and if scroll down at the bottom of that page, we’ll see our embedded tweet is shining like this:

Other Use Cases of Tweepy API

Finally, you can do a lot of things with Tweepy API. To name a few:

and you can do more and more. Just check out the documentation , you’ll see tons of values out there that can be use cases for your project.


In this tutorial, we’ve seen how to embed a tweet feed and put it in a blog post using Tweepy API and a headless CMS; ButterCMS. This blog is deployed as a Flask app. We covered what Tweepy API is and why we need a headless CMS in general.

We also covered the benefits of embedding a tweet on websites and then we dived into our tutorial in which we showed a use case of embedding a tweet and explore the oembed object and build our business logic for that task.

We’ve introduced to a basic understanding of a Jinja2 template to provide our presentation layer. We then learned how to point to our embedded tweet response in the Flask app and finally we were able to run our Flask application and explore the endpoints we’re interested in.

We’ve also touched some use cases of Tweepy API so that you can explore it more if you need it for your next project.