EarlySEO LogoEarlySEO Docs

Flask SDK Guide

Add an EarlySEO blog to your Flask application with the earlyseo-blog Python package. Blueprint setup, Jinja templates, and configuration.

Open .mdx

Quick Start with CLI

The fastest way to add EarlySEO to your Flask project:

pip install "earlyseo-blog[flask]"
earlyseo-blog

The CLI will:

  1. Detect your Flask project
  2. Ask for your Site ID (from Dashboard → Integrations → SDK)
  3. Generate starter templates and a .env file

Prefer manual setup? Follow the steps below.


Prerequisites

  • Python ≥ 3.9
  • Flask ≥ 2.0
  • SDK integration enabled in EarlySEO (Dashboard → Integrations → SDK)
  • Site ID from the SDK integration card
  • At least one published article

Installation

pip install "earlyseo-blog[flask]"

This installs the core package plus Flask-specific extras (blueprint, Jinja helpers).


Blueprint Setup

Register the EarlySEO blog blueprint in your Flask app:

from flask import Flask
from earlyseo_blog.flask.views import create_blog_blueprint

app = Flask(__name__)
app.config["EARLYSEO_SITE_ID"] = "your-site-id"

blog_bp = create_blog_blueprint()
app.register_blueprint(blog_bp, url_prefix="/blog")

Or load the Site ID from an environment variable:

import os

app.config["EARLYSEO_SITE_ID"] = os.environ.get("EARLYSEO_SITE_ID", "")

Optional: Custom CDN URL

app.config["EARLYSEO_CDN_BASE_URL"] = "https://media.earlyseo.com"  # default

Routes

The blueprint registers two routes:

URL PatternNameDescription
/blog/earlyseo_blog.blog_listPaginated article list (?page=N)
/blog/<slug>/earlyseo_blog.blog_detailSingle article view

You can change the prefix when registering the blueprint:

app.register_blueprint(blog_bp, url_prefix="/articles")

Built-in Templates

The package ships with built-in HTML that renders a clean blog layout out of the box. No template files are required to get started — just register the blueprint and visit /blog/.


Custom Jinja Templates

To customize the look of your blog, create template files in your project's templates/earlyseo/ directory. Flask will use your templates instead of the built-in ones.

Blog list template

Create templates/earlyseo/blog_list.html:

{% raw %}
{% extends "base.html" %}

{% block head %}
  <style>{{ article_css }}{{ blog_css }}</style>
{% endblock %}

{% block content %}
  <h1>Blog</h1>

  {% for article in articles %}
    <article>
      {% if article.featured_image_url %}
        <img src="{{ article.featured_image_url }}" alt="{{ article.title }}" />
      {% endif %}
      <h2>
        <a href="{{ url_for('earlyseo_blog.blog_detail', slug=article.slug) }}">
          {{ article.title }}
        </a>
      </h2>
      <p>{{ article.meta_description }}</p>
    </article>
  {% endfor %}

  <!-- Pagination -->
  <nav>
    {% if current_page > 1 %}
      <a href="?page={{ current_page - 1 }}">← Previous</a>
    {% endif %}
    <span>Page {{ current_page }} of {{ total_pages }}</span>
    {% if current_page < total_pages %}
      <a href="?page={{ current_page + 1 }}">Next →</a>
    {% endif %}
  </nav>
{% endblock %}
{% endraw %}

Context variables available:

VariableTypeDescription
articleslistList of ArticleListItem objects
current_pageintCurrent page number
total_pagesintTotal number of pages
total_articlesintTotal article count
article_cssstrArticle-specific CSS
blog_cssstrBlog layout CSS

Blog detail template

Create templates/earlyseo/blog_detail.html:

{% raw %}
{% extends "base.html" %}

{% block title %}{{ article.title }}{% endblock %}

{% block head %}
  <style>{{ article_css }}{{ blog_css }}</style>
  <meta name="description" content="{{ article.meta_description }}" />
{% endblock %}

{% block content %}
  <article>
    <h1>{{ article.title }}</h1>
    {% if article.featured_image_url %}
      <img src="{{ article.featured_image_url }}" alt="{{ article.title }}" />
    {% endif %}
    {{ content_html | safe }}
  </article>
{% endblock %}
{% endraw %}

Context variables available:

VariableTypeDescription
articleArticleFull article object
content_htmlstrRendered HTML of the article body
article_cssstrArticle-specific CSS
blog_cssstrBlog layout CSS

Full App Example

A minimal Flask app with EarlySEO blog:

import os
from flask import Flask
from earlyseo_blog.flask.views import create_blog_blueprint

app = Flask(__name__)
app.config["EARLYSEO_SITE_ID"] = os.environ["EARLYSEO_SITE_ID"]

app.register_blueprint(create_blog_blueprint(), url_prefix="/blog")

@app.route("/")
def home():
    return '<h1>My Site</h1><p><a href="/blog">Read the blog</a></p>'

if __name__ == "__main__":
    app.run(debug=True)

Run it:

export EARLYSEO_SITE_ID=your-site-id
python app.py

Visit http://localhost:5000/blog to see your articles.


Using the Client Directly

For API endpoints or custom logic, use the client directly:

from flask import jsonify
from earlyseo_blog import EarlySeoClient

@app.route("/api/articles")
def api_articles():
    client = EarlySeoClient(site_id=app.config["EARLYSEO_SITE_ID"])
    page = client.get_list_page(1)
    return jsonify({
        "articles": [{"title": a.title, "slug": a.slug} for a in page.articles]
    })

Async client (with Quart or async Flask)

from earlyseo_blog import AsyncEarlySeoClient

async with AsyncEarlySeoClient(site_id="your-site-id") as client:
    page = await client.get_list_page(1)

Install the async extra:

pip install "earlyseo-blog[async]"

How Publishing Works

  1. Write and publish an article in the EarlySEO dashboard
  2. EarlySEO writes JSON files to the global CDN
  3. Your Flask app fetches JSON at request time via the earlyseo-blog package
  4. Articles include SEO metadata automatically

Need Help?

On this page