Adding an MP3 to Voice Calls using Twilio and Python

June 16, 2025
Written by
Reviewed by

Adding an MP3 to Voice Calls using Twilio and Python

MP3 audio in voice calls can serve many use cases whether it’s for branded greetings, informational announcements, music on hold, or interactive voice response (IVR) menus. Using prerecorded MP3s gives you control over tone, language, and clarity while reducing dependence on real-time text-to-speech.

In this post, you'll learn how to play MP3 audio in a Twilio voice call using Python and FastAPI. Whether you want to play a recorded message, music, or any other MP3 file, Twilio Programmable Voice provides the flexibility to do so. We'll cover three different ways to serve your MP3: using a public URL, hosting it with Twilio Assets, or serving it directly from your FastAPI app.

Prerequisites

To follow along with this tutorial, you’ll need:

  • A free Twilio account
  • A Twilio phone number with voice capabilities
  • Python 3.8 or higher
  • ngrok for local testing

Building the application

Setting up the project

Open your terminal and run the following commands to create your project structure:

mkdir twilio-fastapi-mp3
cd twilio-fastapi-mp3
mkdir assets # optional

Then create the following files inside the folder:

  • main.py: your FastAPI app
  • requirements.txt: dependencies

If you have an MP3 ready to go that you want to serve directly from your application, put that MP3 in the assets folder you (optionally) created. Next, start building the code for your application.

For best practices in Python application creation, it's best to create a virtual environment. This will allow you to install all the dependencies you need for your application.

python -m venv venv
source venv/bin/activate  # on Windows, use `venv\Scripts\activate`

Add these lines to requirements.txt:

fastapi
uvicorn
twilio
python-dotenv

Then install all dependencies from the file:

pip install -r requirements.txt

Setting environment variables

Create a .env file in your root directory and populate it with the following values:

TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=your_twilio_phone_number

You can find these credentials in your Twilio Console. After logging in, scroll down to the Account Info section on the main dashboard. Here, you’ll see your Account SID, Auth Token, and Twilio phone number. Copy and paste these into your .env file.

An image showing the Twilio Account Dashboard showcasing the locations of the users Account SID, Auth Token, and Twilio Phone Number.

Writing the FastAPI application

Now let’s build the FastAPI application that will serve the TwiML. TwiML, or Twilio Markup Language, is a set of XML tags used to instruct Twilio on what to do during a call or SMS interaction. In this case, your FastAPI endpoint will return TwiML to tell Twilio to play an MP3 file when someone calls your number.

# main.py
import os
from fastapi import FastAPI, Request
from fastapi.responses import Response
from twilio.twiml.voice_response import VoiceResponse
from dotenv import load_dotenv

load_dotenv()

app = FastAPI()

@app.get("/voice")
def voice():
    response = VoiceResponse()
    response.play("https://example.com/sample.mp3")
    return Response(content=str(response), media_type="application/xml")

This route returns TwiML that plays the MP3. Replace the URL with one of the following methods:

Option 1: Use a hosted MP3 file (public URL)

You can use any public URL. Twilio will fetch the MP3 directly.

We have a demo MP3 file hosted on our server that you can use. You can copy and use the following URL in your response.play() method like so:

response.play("https://demo.twilio.com/docs/classic.mp3")

Option 2: Upload to Twilio Assets

Twilio Assets lets you upload and host static files, like MP3s, directly within your Twilio account. Once uploaded and marked public, you can use the asset’s URL in your TwiML to serve audio files reliably, without needing external hosting. This is especially useful for production-ready call flows where uptime and simplicity are key.

If you don’t have your own server to host files, Twilio Assets is a great free alternative.

To upload an MP3 file to Twilio Assets:

  • Log in to the Twilio Console
  • In the left-hand sidebar, click Functions & Assets
  • Under that section, select Assets (Classic)
  • Click the plus (+) icon in the top-left corner to upload a new asset
  • Select your MP3 file from your computer
  • Make sure to mark the asset as public so Twilio can access it

Once uploaded, copy the asset's URL and use it in your response.play() method like so:

response.play("https://<your-asset-url>. twil.io/path/to/file.mp3")

If you don’t see the Functions and Assets section, you can visit the Explore Products section and select Functions and Assets.

Option 3: Serve from Project

This option allows you to host the MP3 file directly within your FastAPI project. It’s a great approach for testing or small-scale deployments when you don’t want to rely on external hosting. Using FastAPI’s FileResponse, your app serves the MP3 file over HTTP so Twilio can fetch and play it during a call. Update your code to include the following:

from fastapi.responses import FileResponse

@app.get("/<your-mp3-file>.mp3")
def serve_mp3():
    return FileResponse("assets/<your-mp3-file>.mp3", media_type="audio/mpeg")

@app.get("/voice")
def voice():
    response = VoiceResponse()
    response.play("https://<your-ngrok-url>/<your-mp3-file>.mp3")
    return Response(content=str(response), media_type="application/xml")
When using ngrok, be sure your public MP3 URL is accessible and ends in .mp3. Twilio cannot play audio from a non-MP3 URL or content type.

You’ll need to run ngrok to generate a publicly accessible URL that Twilio can reach. Open a new terminal tab or window and run:

ngrok http 8000

Copy the generated https://<your-ngrok-subdomain>.ngrok.io URL and use it in your Twilio Console to point incoming calls to your FastAPI endpoint. You'll find out how to do this in the next section. 

Deploying your application

Now you can verify your setup and make sure Twilio can reach your FastAPI app and serve the MP3 file successfully during a phone call. You'll use uvicorn to run your FastAPI server locally and ngrok to expose it to the internet for Twilio to access.

  • Start your server:

uvicorn main:app --port 8000 --reload

  • Run ngrok, if you haven’t already, and copy your forwarding URL from its output:

ngrok http 8000

  • In your Twilio Console, navigate to Develop > Phone Numbers > Active Numbers, select your active Twilio phone number, set the Voice Configuration > A call comes in webhook to:

https://<your-ngrok-subdomain>.ngrok.io/voice

  • Call your Twilio number and listen for the MP3.

Troubleshooting

Why isn’t my audio playing?

If your call connects but the MP3 doesn’t play, here are a few common culprits:

  • Ensure your MP3 URL is correct and publicly accessible.
  • Make sure the file ends in .mp3.
  • Ensure that your .mp3 file is within Twilio Asset’s limitations.

Why can’t I access my application?

If you can’t connect to your application:

  • Make sure ngrok is forwarding the correct port being used by your python project.

Next steps

You could build on this application by exploring other TwiML verbs:

  • Create a playlist by chaining multiple <Play> verbs
  • Use <Gather> to collect user input
  • Use <Say> for dynamic text-to-speech
  • Use <Record> to capture voicemail or input

Conclusion

In this post, you learned how to use Twilio Programmable Voice with FastAPI to play MP3 audio files during voice calls. You saw how to serve audio via a public URL, Twilio Assets, and even directly from your Python app. For more ideas, check out the TwiML Voice documentation.

Dylan Frankcom is a Python Software Engineer on Twilio’s Developer Voices team. He’s passionate about building tools that help developers learn, create, and ship faster. You can reach him at dfrankcom [at] twilio.com, find him on LinkedIn , or follow him on Github .