Blueprints are objects that can be used for sub-routing within an application. Instead of adding routes to the application instance, blueprints define similar methods for adding routes, which are then registered with the application in a flexible and pluggable manner.

Blueprints are especially useful for larger applications, where your application logic can be broken down into several groups or areas of responsibility.

Creating and registering#

First, you must create a blueprint. It has a very similar API as the Sanic() app instance with many of the same decorators.

# ./
from sanic.response import json
from sanic import Blueprint

bp = Blueprint("my_blueprint")

async def bp_root(request):
    return json({"my": "blueprint"})

Next, you register it with the app instance.

from sanic import Sanic
from my_blueprint import bp

app = Sanic(__name__)

Blueprints also have the same websocket() decorator and add_websocket_route method for implementing websockets.

Beginning in v21.12, a Blueprint may be registered before or after adding objects to it. Previously, only objects attached to the Blueprint at the time of registration would be loaded into application instance.


async def bp_root(request):


Blueprints along with everything that is attached to them can be copied to new instances using the copy() method. The only required argument is to pass it a new name. However, you could also use this to override any of the values from the old blueprint.

v1 = Blueprint("Version1", version=1)

def something(request):

v2 = v1.copy("Version2", version=2)

Available routes:

Added in v21.9

Blueprint groups#

Blueprints may also be registered as part of a list or tuple, where the registrar will recursively cycle through any sub-sequences of blueprints and register them accordingly. The method is provided to simplify this process, allowing a ‘mock’ backend directory structure mimicking what’s seen from the front end. Consider this (quite contrived) example:

│ ├──
│ ├──
│ └──

First blueprint#

# api/content/
from sanic import Blueprint

authors = Blueprint("content_authors", url_prefix="/authors")

Second blueprint#

# api/content/
from sanic import Blueprint

static = Blueprint("content_static", url_prefix="/static")

Blueprint group#

# api/content/
from sanic import Blueprint
from .static import static
from .authors import authors

content =, authors, url_prefix="/content")

Third blueprint#

# api/
from sanic import Blueprint

info = Blueprint("info", url_prefix="/info")

Another blueprint group#

# api/
from sanic import Blueprint
from .content import content
from .info import info

api =, info, url_prefix="/api")

Main server#

All blueprints are now registered

from sanic import Sanic
from .api import api

app = Sanic(__name__)

Blueprint group prefixes and composability#

As shown in the code above, when you create a group of blueprints you can extend the URL prefix of all the blueprints in the group by passing the url_prefix argument to the method. This is useful for creating a mock directory structure for your API.

In addition, there is a name_prefix argument that can be used to make blueprints reusable and composable. The is specifically necessary when applying a single blueprint to multiple groups. By doing this, the blueprint will be registered with a unique name for each group, which allows the blueprint to be registered multiple times and have its routes each properly named with a unique identifier.

Consider this example. The routes built will be named as follows:

bp1 = Blueprint("bp1", url_prefix="/bp1")
bp2 = Blueprint("bp2", url_prefix="/bp2")

bp1.add_route(lambda _: ..., "/", name="route1")
bp2.add_route(lambda _: ..., "/", name="route2")

group_a =
    bp1, bp2, url_prefix="/group-a", name_prefix="group-a"
group_b =
    bp1, bp2, url_prefix="/group-b", name_prefix="group-b"

app = Sanic("TestApp")

Name prefixing added in v23.6


Blueprints can also have middleware that is specifically registered for its endpoints only.

async def print_on_request(request):
    print("I am a spy")

async def halt_request(request):
    return text("I halted the request")

async def halt_response(request, response):
    return text("I halted the response")

Similarly, using blueprint groups, it is possible to apply middleware to an entire group of nested blueprints.

bp1 = Blueprint("bp1", url_prefix="/bp1")
bp2 = Blueprint("bp2", url_prefix="/bp2")

async def bp1_only_middleware(request):
    print("applied on Blueprint : bp1 Only")

async def bp1_route(request):
    return text("bp1")

async def bp2_route(request, param):
    return text(param)

group =, bp2)

async def group_middleware(request):
    print("common middleware applied for both bp1 and bp2")

# Register Blueprint group under the app


Just like other exception handling, you can define blueprint specific handlers.

def ignore_404s(request, exception):
    return text("Yep, I totally found the page: {}".format(request.url))

Static files#

Blueprints can also have their own static handlers

bp = Blueprint("bp", url_prefix="/bp")
bp.static("/web/path", "/folder/to/serve")
bp.static("/web/path", "/folder/to/server", name="uploads")

Which can then be retrieved using url_for(). See routing for more information.

>>> print(app.url_for("static", name="bp.uploads", filename="file.txt"))


Blueprints can also implement listeners.

async def before_server_start(app, loop):

async def after_server_stop(app, loop):


As discussed in the versioning section, blueprints can be used to implement different versions of a web API.

The version will be prepended to the routes as /v1 or /v2, etc.

auth1 = Blueprint("auth", url_prefix="/auth", version=1)
auth2 = Blueprint("auth", url_prefix="/auth", version=2)

When we register our blueprints on the app, the routes /v1/auth and /v2/auth will now point to the individual blueprints, which allows the creation of sub-sites for each API version.

from auth_blueprints import auth1, auth2

app = Sanic(__name__)

It is also possible to group the blueprints under a BlueprintGroup entity and version multiple of them together at the same time.

auth = Blueprint("auth", url_prefix="/auth")
metrics = Blueprint("metrics", url_prefix="/metrics")

group =, metrics, version="v1")

# This will provide APIs prefixed with the following URL path
# /v1/auth/ and /v1/metrics


A Blueprint may be registered to multiple groups, and each of BlueprintGroup itself could be registered and nested further. This creates a limitless possibility Blueprint composition.

Added in v21.6

Take a look at this example and see how the two handlers are actually mounted as five (5) distinct routes.

app = Sanic(__name__)
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
group =
primary =, url_prefix="/primary")

def blueprint_1_default_route(request):
    return text("BP1_OK")

def blueprint_2_default_route(request):
    return text("BP2_OK")


# The mounted paths:
# /api/v1/grouped/bp1/
# /api/v1/grouped/bp2/
# /api/v1/primary/grouped/bp1
# /api/v1/primary/grouped/bp2
# /bp1

Generating a URL#

When generating a url with url_for(), the endpoint name will be in the form: