All handlers usually return a response object, and middleware may optionally return a response object.

To clarify that statement:

  • unless the handler is a streaming endpoint handling its own pattern for sending bytes to the client, the return value must be an instance of sanic.response.HTTPResponse (to learn more about this exception see streaming responses). In most use cases, you will need to return a response.
  • if a middleware does return a response object, that will be used instead of whatever the handler would do (see middleware to learn more).

A most basic handler would look like the following. The sanic.response.HTTPResponse object will allow you to set the status, body, and headers to be returned to the client.

from sanic import HTTPResponse, Sanic

app = Sanic("TestApp")

def handler(_):
    return HTTPResponse()

However, usually it is easier to use one of the convenience methods discussed below.


The easiest way to generate a response object is to use one of the convenience functions.


Default Content-Type: text/plain; charset=utf-8
Description: Returns plain text

from sanic import text

async def handler(request):
    return text("Hi 😎")


Default Content-Type: text/html; charset=utf-8
Description: Returns an HTML document

from sanic import html

async def handler(request):
    return html('<!DOCTYPE html><html lang="en"><meta charset="UTF-8"><div>Hi 😎</div>')


Default Content-Type: application/json
Description: Returns a JSON document

from sanic import json

async def handler(request):
    return json({"foo": "bar"})

By default, Sanic ships with ujson as its JSON encoder of choice. If ujson is not installed, it will fall back to the standard library json module.

It is super simple to change this if you want.

from sanic import json
from orjson import dumps

json({"foo": "bar"}, dumps=dumps)

You may additionally declare which implementation to use globally across your application at initialization:

from orjson import dumps

app = Sanic(..., dumps=dumps)


Default Content-Type: N/A
Description: Returns a file

from sanic import file

async def handler(request):
    return await file("/path/to/whatever.png")

Sanic will examine the file, and try and guess its mime type and use an appropriate value for the content type. You could be explicit, if you would like:

file("/path/to/whatever.png", mime_type="image/png")

You can also choose to override the file name:

file("/path/to/whatever.png", filename="super-awesome-incredible.png")

File Streaming#

Default Content-Type: N/A
Description: Streams a file to a client, useful when streaming large files, like a video

from sanic.response import file_stream

async def handler(request):
    return await file_stream("/path/to/whatever.mp4")

Like the file() method, file_stream() will attempt to determine the mime type of the file.


Default Content-Type: application/octet-stream
Description: Send raw bytes without encoding the body

from sanic import raw

async def handler(request):
    return raw(b"raw bytes")


Default Content-Type: text/html; charset=utf-8
Description: Send a 302 response to redirect the client to a different path

from sanic import redirect

async def handler(request):
    return redirect("/login")


Default Content-Type: N/A
Description: For responding with an empty message as defined by RFC 2616

from sanic import empty

async def handler(request):
    return empty()

Defaults to a 204 status.

Default status#

The default HTTP status code for the response is 200. If you need to change it, it can be done by the response method."/")
async def create_new(request):
    new_thing = await do_create(request)
    return json({"created": True, "id": new_thing.thing_id}, status=201)

Returning JSON data#

Starting in v22.12, When you use the sanic.json convenience method, it will return a subclass of HTTPResponse called sanic.response.types.JSONResponse. This object will have several convenient methods available to modify common JSON body.

from sanic import json

resp = json(...)
  • resp.set_body(<raw_body>) - Set the body of the JSON object to the value passed
  • resp.append(<value>) - Append a value to the body like list.append (only works if the root JSON is an array)
  • resp.extend(<value>) - Extend a value to the body like list.extend (only works if the root JSON is an array)
  • resp.update(<value>) - Update the body with a value like dict.update (only works if the root JSON is an object)
  • resp.pop() - Pop a value like list.pop or dict.pop (only works if the root JSON is an array or an object)


The raw Python object is stored on the JSONResponse object as raw_body. While it is safe to overwrite this value with a new one, you should not attempt to mutate it. You should instead use the methods listed above.

resp = json({"foo": "bar"})

# This is OKAY
resp.raw_body = {"foo": "bar", "something": "else"}

# This is better
resp.set_body({"foo": "bar", "something": "else"})

# This is also works well
resp.update({"something": "else"})

# This is NOT OKAY
resp.raw_body.update({"something": "else"})
# Or, even treat it like a list
resp = json(["foo", "bar"])

# This is OKAY
resp.raw_body = ["foo", "bar", "something", "else"]

# This is better
resp.extend(["something", "else"])

# This is also works well

# This is NOT OKAY

Added in v22.9