Flask integration
AIVisibility from ai_visibility.middleware.flask is a standard Flask extension — construct it with app directly, or defer with init_app() — that hooks an after_request handler to detect AI crawlers and optimize the HTML response served to them.
Install
pip install ai-visibility[flask]Adds Flask (≥2.0) as a dependency. The core package remains dependency-free otherwise.
Extension setup
from flask import Flask
from ai_visibility.middleware.flask import AIVisibility
app = Flask(__name__)
ai_vis = AIVisibility(app, optimize=True, inject_schemas=True)Or the deferred application-factory pattern, if your project constructs the app lazily:
# extensions.py
from ai_visibility.middleware.flask import AIVisibility
ai_vis = AIVisibility()
# app.py
from flask import Flask
from extensions import ai_vis
def create_app():
app = Flask(__name__)
ai_vis.init_app(app)
return appEvery request runs through AIVisibility's after_request hook, reading request.headers.get("User-Agent") to detect a crawler and response.content_type to skip anything that isn't text/html — non-HTML responses (a JSON endpoint, a static file) have zero added overhead.
Configuration options
AIVisibility(
app: Flask | None = None, *,
optimize: bool = True,
inject_schemas: bool = False,
schemas: list[dict[str, Any]] | None = None,
log_visits: bool = True,
on_detect: Callable[[CrawlerInfo, str], None] | None = None,
optimize_options: OptimizeOptions | None = None,
analytics: CrawlerAnalytics | None = None,
)| Parameter | Type | Default | Description |
|---|---|---|---|
app | Flask | None | None | Attach immediately, or pass None and call init_app(app) later. |
optimize | bool | True | Serve an optimized HTML snapshot to detected crawlers. |
inject_schemas | bool | False | Inject schemas as JSON-LD for detected crawlers. |
schemas | list[dict] | None | None | JSON-LD schema dicts to inject when inject_schemas is set. Static, set once at construction — see per-route schemas below for page-specific data. |
log_visits | bool | True | Log detected crawler visits. |
on_detect | Callable[[CrawlerInfo, str], None] | None | None | Called with (crawler, path) on every detected visit. |
optimize_options | OptimizeOptions | None | None | Fine-grained stripping control — see the optimizer reference. |
analytics | CrawlerAnalytics | None | None | Pass a CrawlerAnalytics built with your own AnalyticsStore to persist visits — unlike Django, Flask's constructor accepts this directly. Defaults to a fresh in-memory tracker. |
Using schema builders in Flask routes
Build page-specific schema in the route handler and inject it directly with inject_schemas(), independent of the extension's own static schemas config:
from flask import render_template, url_for
from ai_visibility import product_schema, inject_schemas
@app.route("/products/<slug>")
def product_detail(slug):
product = get_product_or_404(slug)
html = render_template("product_detail.html", product=product)
schema = product_schema(
name=product.name,
description=product.description,
price=product.price,
price_currency="USD",
availability="InStock" if product.in_stock else "OutOfStock",
url=url_for("product_detail", slug=slug, _external=True),
)
return inject_schemas(html, [schema])Serving llms.txt and robots.txt
from flask import Response
from ai_visibility import generate_llms_txt, generate_robots_txt
from ai_visibility.types import LlmsTxtConfig, LlmsTxtSection, LlmsTxtLink, RobotsTxtConfig
@app.route("/llms.txt")
def llms_txt():
body = generate_llms_txt(LlmsTxtConfig(
title="Acme",
summary="Acme makes widgets.",
sections=[LlmsTxtSection(name="Docs", links=[
LlmsTxtLink(title="Getting started", url="https://acme.com/docs"),
])],
))
return Response(body, mimetype="text/plain")
@app.route("/robots.txt")
def robots_txt():
body = generate_robots_txt(RobotsTxtConfig(
block_training_bots=True,
sitemap_urls=["https://acme.com/sitemap.xml"],
))
return Response(body, mimetype="text/plain")Blueprint usage
AIVisibility attaches its after_request hook to the whole app, not per-blueprint — so routes registered on any blueprint are already covered once the extension is initialized on the app. Generator routes above work identically inside a blueprint:
from flask import Blueprint, Response
from ai_visibility import generate_robots_txt
seo_bp = Blueprint("seo", __name__)
@seo_bp.route("/robots.txt")
def robots_txt():
return Response(generate_robots_txt(), mimetype="text/plain")If a blueprint's own routes need to opt out of optimization entirely (a webhook receiver, an internal admin blueprint that happens to return HTML), there's no built-in per-route bypass in 0.5.0 — route those under a distinct path and gate on request.path inside on_detect/a custom after_request instead.
Flask-RESTful / Flask-RESTX integration
Same story as Django REST Framework: AIVisibility only touches text/html responses, so Flask-RESTful/Flask-RESTX API endpoints (which return JSON) are already unaffected — no exclusion config needed. It only matters for the HTML-serving parts of an app that also mounts a REST API alongside server-rendered pages.
Testing with AI crawler User-Agents
GPTBOT_UA = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.2"
def test_strips_script_tags_for_gptbot(client):
response = client.get("/", headers={"User-Agent": GPTBOT_UA})
assert b"<script>" not in response.data
def test_regular_visitor_untouched(client):
response = client.get("/", headers={"User-Agent": "Mozilla/5.0 (normal browser)"})
assert b"<script>" in response.dataclient is Flask's standard app.test_client() fixture (or the pytest-flask client fixture, if used) — nothing ai-visibility-specific is needed beyond setting the header.
See the middleware API reference for the shared AIVisibilityCore both Django and FastAPI adapters also wrap, and the Django guide / the FastAPI guide for the other two frameworks.