Headless, Python, and Flask: How to enjoy massive flexibility in a microframework

Project requirements evolve and successful websites change over time. How can we manage website growth with Python?

Michael Berry

Published on Jun 3, 2021

In this article, I’ll show you how to feed Flask application data from a headless CMS and discuss why pairing the two results in a more flexible website architecture. If you’re new to Flask, take a look at the user’s guide in the official Flask documentation. It has a wonderful foreword, quick-start introduction, and full-length tutorial.

Grow like a snake

Weird opener, I know, but bear with me. Snakes grow until they die. Their growth slows down a lot as they mature, but it doesn’t stop if they are being fed. I’d like to think I can apply this to my own life and skills. Learn something everyday, practice your skills frequently, keep advancing and refining whatever you’re working on, feed yourself new information. 

I enjoy writing code in Python. It’s explicit, it has a low entry-learning curve, it’s human readable, and it has a wide variety of uses. Python projects range from simple file system CRUD scripts to robust machine learning libraries. With Python, I feel as I grow in skill, it always has a place for me. 

Using Python has become a “labor of love” for me, and I wanted to extend that same welcoming feeling to the web development projects I regularly work on. I needed a web framework that starts small but can scale in complexity as a project advances. Flask fits the bill perfectly.

What is Flask?

Flask is a minimal Python web development framework that bridges your application code to a Web Server Gateway Interface (WSGI) and templates. It doesn’t come with a database layer, form layer, or any other features.  The official documentation says it best:

Flask can be everything you need and nothing you don’t...As your codebase grows, you are free to make the design decisions appropriate for your project. Flask will continue to provide a very simple glue layer to the best that Python has to offer.

If your application was a meal, Flask isn’t even the “meat and potatoes,” it’s the plate and the fork.

Flask’s flexibility is hard to summarize succinctly because of all the extensibility options it has, but in the interest of brevity, I’ve focused on scalability and ease of development:


  • Flask does not dictate architecture design decisions and acts more as a conduit linking your services together. Essentially, it comes with out-of-the-box templating via Jinja and a Web Server Gateway Interface via Werkzeug (for routing and connecting applications). So you can start with the bare minimum and incrementally increase complexity.
  • Extensions, API hooks, middleware, and subclassing can be used as requirements arise or when a project is ready to “become big” per the Flask documentation.
  • Blueprints and running sites as packages are available for larger applications.

For example, in the diagram, you can see three different types of client requests that your site may service in the future:


If you only need to serve up simple pages, then you may only need your Flask application on a server. However, as time passes, you might find that you need to add “login” support with extensions, a media server with middleware, or blueprints for dynamic routing. Flask can scale to meet these scenarios. 

Ease of development

  • Routing is extremely straightforward—in one line you bind a decorator to a function. Whenever that route it hit, the function is executed.
def index():
    return 'Index Page Text'

def hello():
    return 'Hello, World!'
  • Using variables in templates doesn’t require boilerplate or setup—pass a variable to your render_template function in a view, and you are able to access it in the template:
 #  in file: home/view.py:
return render_template("home/index.html", hero=hero, articles=articles)

# in file: templates/home/index.html
<h2>{{ hero.elements.title.value }}</h2>

Headless and Flask: why it’s a perfect match

There’s a lot to consider when it comes to selecting a framework and CMS, but cost, complexity, and future-proof design are always tied to whether a project is flexible. Here’s why using a headless CMS and Flask meet these requirements head-on:

Both use the microservices model

Designing your project using a microservices architecture translates to a smaller code footprint. Using a microframework means there isn’t any “solution bloat” with features. You’ll use only the best-of-breed integrations, and you can incrementally add those tools as your project matures. 

Similar to the scalability diagram earlier, here’s what integrating with external services may look like to handle different client scenarios:


Using a SaaS headless CMS means your management system’s code is hosted by the vendor, resulting in zero overhead for you. Additionally, the content stays isolated from the project’s code, and you typically don’t need code changes when new CMS features are released.

You’re not tied to a specific channel

Headless CMSs provide content in an omnichannel capable format, meaning the content is reusable across different types of applications throughout your organization. With Python being a prime language for machine learning, AI, and data engineering, this could prove extremely useful for the future of your content!

Even the budgeting is more flexible

With a headless CMS subscription payment model, you only pay for what you need and can typically adjust the subscription plan to fit your budget and requirements. 

Flask is open-source and free. I’d say that’s the most flexible charge your company credit card has ever seen.

How to connect Flask to a headless CMS

Warmups and exercise are the best way to maintain flexibility, so in this section, let’s run through a sample site setup to see Flask and a headless CMS in action.

We’ll use Kontent.ai as our headless CMS, a Kontent.ai Python SDK, the larger application structure advised by Flask, and a shared Kontent.ai project.  

Full disclosure: I built this “unofficial Kontent Python SDK” and connecting Flask to Kontent.ai can be done with just an HTTP request library. However, it did save me a lot of code in my views:


We’ll set up the site in five steps: 

    1. Perform installations
    2. Create the project structure
    3. Add the application logic
    4. Add the template logic
    5. Run the site

Step: One: Perform Installations

    • Set up a virtual environment such as virtualenv: py -m venv env
    • Install Flask: pip install flask 
    • Install the Python SDK: pip install kontent_delivery

Step Two: Create the project structure

    • For now, create empty python and HTML files in the structure below. You’ll add the respective code to each file in the next step.  Note: backslashes ("/") denote directories.

Step Three: Add the application logic

from setuptools import setup

project_id ="975bf280-fd91-488c-994c-2f04416e5ee3"
delivery_options = {   
    "preview": False,
    "preview_api_key": "enter_key_here",
    "secured": False,
    "secured_api_key": "enter_key_here",
    "timeout": (4,7)
    • __init__.py - creates the Flask application object, initializes the Kontent delivery client, and imports views.
from flask import Flask
import config
from kontent_delivery.client import DeliveryClient

app = Flask(__name__)

client = DeliveryClient(config.project_id, options=config.delivery_options)

import sample.home.views
    • home/views.py - grabs the content from the CMS, then passes it as variables to the home template when the "/" route is hit.
from flask.templating import render_template
from sample import app, client

def index():
    resp = client.get_content_item("home")
    if resp:
        hero_units = resp.get_linked_items("hero_unit")
        hero = hero_units[0]

        articles = resp.get_linked_items("articles")

        return render_template("home/index.html", hero=hero, articles=articles)

# custom 404 page information available at: 
# https://flask.palletsprojects.com/en/2.0.x/errorhandling/?highlight=404#custom-error-pages
    return render_template("error_pages/404.html"), 404

Step Four: Add the template logic

    • templates/base.html - template wrapper that contains layout HTML for the site
    <head id="head">
        {% block head %}
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="UTF-8" />
        <title>Flask Sample</title>
        {% endblock %}
        <div class="container">
            {% block content %}
            {% endblock %}
    • templates/home/index.html - template on the "/" route that uses variables passed from home/views.py.
{% extends "base.html" %}
{% block content %}
<h1>Flask Simple Example</h1>
<h2>{{ hero.elements.title.value }}</h2>
{% for article in articles %}
                <a href="/articles/{{article.elements.url_pattern.value}}">
                    {{ article.elements.title.value }}
                {{ article.elements.summary.value }}
                {{ article.elements.post_date.value }}
{% endfor %}
{% endblock %}

Step Five: Run the site

  • In your terminal, set the FLASK_APP environment variable: set FLASK_APP=sample
  • Install the application package using pip install -e . (including the period)
  • Run the application: flask run

Following the Flask application link from the terminal ( by default) will bring you to your Kontent + Flask site. If you have a Kontent account and want to test this out with your own sample Kontent project, follow these instructions

The full code for this sample can be seen in this GitHub repository.


In this article, we discussed the benefits of using Flask with a headless CMS and how to create a Flask + Kontent.ai sample project. Now I’m sure you’ll agree that using a microframework with a headless CMS affords major flexibility!

If this article got you excited about the possibilities of using Flask and Kontent.ai, a more robust Flask + Kontent.ai sample site can be seen here.

Are you having issues with the Flask samples from the article? Get in touch on our Discord.

Subscribe to the Kontent.ai newsletter

Stay in the loop. Get the hottest updates while they’re fresh!