How to Return PDF Files in FastAPI (3 Examples)

Updated: September 2, 2023 By: Khue Post a comment

This succinct, straight-to-the-point article will walk you through a couple of different ways to return PDF files in FastAPI. In the upcoming examples, we’ll use this sample PDF file:

https://api.slingacademy.com/v1/sample-data/files/just-text.pdf

Save it in the same folder as your Python script (and rename it to sample.pdf).

Using FileResponse with a file path

This solution uses the FileResponse class from FastAPI to return the PDF file from a file path on the disk. The steps to get the job done are listed below:

  1. Import FileResponse from fastapi.responses.
  2. Create a FileResponse object with the file path as the content, the media type as application/pdf, and optionally, the headers as a dictionary with the Content-Disposition key and a value that specifies the filename and whether to view or download the file (inline for viewing, attachment for downloading).
  3. Return the FileResponse object from the endpoint function.

Complete example:

from fastapi import FastAPI
from fastapi.responses import FileResponse

app = FastAPI()


@app.get("/pdf")
async def get_pdf():
    # To view the file in the browser, use "inline" for the media_type
    headers = {
        "Content-Disposition": "inline; filename=sample.pdf"
    }  
    
    # Create a FileResponse object with the file path, media type and headers
    response = FileResponse("sample.pdf", media_type="application/pdf", headers=headers)

    # Return the FileResponse object
    return response

The result (at http://localhost:8000/pdf):

If you want to download the file immediately instead of viewing it on the web browser, use these headers:

headers = {"Content-Disposition": "attachment; filename=sample.pdf"} 

This approach is simple, neat, and works well for both small and large files. It reads the file asynchronously and sends it as a stream.

Using StreamingResponse with a file-like object

The main idea here is to use the StreamingResponse class from FastAPI to return the PDF file as a stream of data from a file-like object, such as a file handle or a BytesIO buffer.

The steps:

  1. Import StreamingResponse from fastapi.responses.
  2. Open or create a file-like object that contains the PDF data, using open or io.BytesIO.
  3. Create a StreamingResponse object with the file-like object as the content, the media type as application/pdf, and optionally, the headers as a dictionary with the Content-Disposition key and a value that specifies the filename and whether to view or download the file.
  4. Return the StreamingResponse object from the endpoint function.

Code example:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import io

app = FastAPI()


@app.get("/pdf")
def get_pdf():
    # Open the PDF file from the disk as a file-like object
    f = open("sample.pdf", "rb")

    # To view the file in the browser, use "inline" for the media_type
    headers = {"Content-Disposition": "inline; filename=sample.pdf"}

    # Create a StreamingResponse object with the file-like object, media type and headers
    response = StreamingResponse(f, media_type="application/pdf", headers=headers)

    # Return the StreamingResponse object
    return response

This approach is less efficient and more verbose than the preceding one, but it is more flexible and customizable for sending files from other sources, such as remote URLs or databases. It also allows you to manipulate or transform the data before sending it, such as applying compression or encryption.

Using Response with file bytes and headers

In this approach, we’ll use the Response class from FastAPI to return a PDF file as a byte stream, along with the appropriate headers and media type. The steps to implement this solution are:

  1. Import Response from fastapi.
  2. Read the PDF file from the disk or memory as a byte stream, using open or io.BytesIO.
  3. Create a Response object with the file bytes as the content, the media type as application/pdf, and the headers as a dictionary with the Content-Disposition key and a value that specifies the filename and whether to view or download the file.
  4. Return the Response object from the endpoint function.

Code example:

from fastapi import FastAPI, Response

app = FastAPI()


@app.get("/pdf")
def get_pdf():
    # Read the PDF file from the disk as a byte stream
    with open("sample.pdf", "rb") as f:
        pdf_bytes = f.read()

    # To view the file in the browser, use "inline" for the media_type
    headers = {"Content-Disposition": "inline; filename=sample.pdf"}

    # Create a Response object with the file bytes, media type and headers
    response = Response(pdf_bytes, media_type="application/pdf", headers=headers)

    # Return the Response object
    return response

This approach isn’t efficient for large PDF files, since it loads the entire file into memory before returning it. Another downside is that it does not support streaming or chunking of the file data.

Note: If you want to download more sample PDF files for practice, check this article: Free Sample PDF Files for Learning & Practice.