Python web applications that combine the Bottle web framework with the Twilio Voice API can easily make and receive phone calls. Our calls will read a snippet of text then play an MP3 file. The call instructions can be modified to provide other useful actions such as gathering input from the number pad or putting all callers together in a conference call.
Our Project Tools
Our Bottle application will need either Python 2 or 3 to be installed. Python 3 is recommended for new applications. We will also need the following tools throughout this walkthrough:
- Bottle web framework for serving HTTP requests
- Ngrok to create a localhost tunnel to our local development environment
- A free Twilio account to use the Voice web API
- Twilio Python helper library
You can snag all the code for this tutorial in the python-twilio-example-apps GitHub repository. Copy and modify the code however you want – it is all open sourced under the MIT license.
Before we write our Python code we will create a new virtual environment to isolate our application dependencies. Open your terminal and execute the
virtualenv command to create a new virtualenv:
activate script within the virtualenv, which activates this Python installation for the shell we are working in. Be aware that you need to explicitly activate the virtualenv in each terminal window where you want to execute the Bottle application.
The command prompt will change when the virtualenv is activated. You should see
(bottlephone) $. The following screenshot is what my environment looks like after I ran the
Next use the
pip command to install the Bottle and Twilio Python packages into your virtualenv.
pip install bottle twilio
Our two required dependencies will be locally installed once the installation script finishes. Time to write some Python code!
Python Coding with Bottle
The Bottle web application will have three endpoints:
/– returns a simple message to let us know our Bottle app is running
/twiml– responds with TwiML (a set of instructions written in XML) that instructs Twilio what to do with phone calls
/dial-phone/, where “outbound_phone_number” is a phone number in the format ” 12025551234′′
With that overview out of the way, we can proceed to writing the code for our initial Bottle application. Create a new file named
app.py with the following lines of code:
import os import bottle from bottle import route, run, post, Response app = bottle.default_app() @route('/') def index(): """Returns standard text response to show app is working.""" return Response("Bottle app up and running!") if __name__ == '__main__': run(host='127.0.0.1', port=5000, debug=False, reloader=True)
Save the file. Make sure your virtualenv is still activated so our code can rely on the Bottle code library. Run the app via the Bottle development server with the following command:
Our development server should boot up successfully:
(bottlephone) matt@ubuntu:~/bottlephone$ python app.py Bottle v0.12.9 server starting up (using WSGIRefServer())... Listening on http://127.0.0.1:5000/ Hit Ctrl-C to quit.
Test out the app by going to localhost:5000 in a web browser. We should get a simple success message that the app is running and responding to requests.
Next we need to obtain a phone number that our Bottle app can use to call other phone numbers.
Getting Our Twilio Number
Our basic Bottle web app runs but what we really want to do is make some phone calls.
In your web browser go to the Twilio website and sign up for a free account. You can also sign into your existing Twilio account if you already have one.
The Twilio trial account allows you to dial and receive phone calls to your own validated phone number. To dial and receive calls from any phone number then you need to upgrade your account. Trial accounts are great for initial development before your application goes live but upgraded accounts are where the real power of Twilio is revealed.
Once you are signed into your Twilio account, go to the manage phone numbers screen. On this screen you can buy one or more phone numbers or click on an existing phone number in your account to configure it.
There is nothing for us to configure right now on the phone number configuration page but we will come back to it shortly. Now that we have a phone number in hand, let’s add the final bit of code to our Bottle app to get this app working.
Filling in Our Bottle App
We need to add two new routes to our Bottle app so it can dial outbound phone calls. Modify your existing
app.py file with the two new functions below,
import os import bottle from bottle import route, run, post, Response from twilio import twiml from twilio.rest import TwilioRestClient app = bottle.default_app() # copy the account SID and auth token from the Twilio Console and paste below twilio_client = TwilioRestClient('paste account SID here' , 'auth token here') # input your Twilio number in the second string on the next line TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', '+12023350173') NGROK_BASE_URL = os.environ.get('NGROK_BASE_URL', '') @route('/') def index(): """Returns standard text response to show app is working.""" return Response("Bottle app up and running!") @post('/twiml') def twiml_response(): """Provides TwiML instructions in response to a Twilio POST webhook event so that Twilio knows how to handle the outbound phone call when someone picks up the phone. """ response = twiml.Response() response.say("Hello, this call is from a Bottle web application.") response.play("https://api.twilio.com/cowbell.mp3", loop=10) return Response(str(response)) @route('/dial-phone/') def outbound_call(outbound_phone_number): """Uses the Twilio Python helper library to send a POST request to Twilio telling it to dial an outbound phone call from our specific Twilio phone number (that phone number must be owned by our Twilio account). """ # the url must match the Ngrok Forwarding URL plus the route defined in # the previous function that responds with TwiML instructions twilio_client.calls.create(to=outbound_phone_number, from_=TWILIO_NUMBER, url=NGROK_BASE_URL + '/twiml') return Response('phone call placed to ' + outbound_phone_number + '!') if __name__ == '__main__': run(host='127.0.0.1', port=5000, debug=False, reloader=True)
Make sure to replace the phone number listed above with your Twilio phone number. Also modify the placeholders for the Twilio credentials to properly instantiate the TwilioRestClient.
There is one issue with our local development environment configuration: Twilio won’t be able to reach that
/twiml route. We need to deploy our app to a reachable server or use a localhost tunneling tool like Ngrok. Ngrok provides an external URL that connects to a port running on your machine. Download and install the Ngrok application for your operating system.
Run the following command to make our Bottle app on port 5000 available publicly on the Internet.
./ngrok http 5000
Ngrok will start up and provide us with a Forwarding URL, with both HTTP and HTTPS versions.
The Forwarding URL plus the route will instruct Twilio to handle the phone call when someone answers. Insert the Ngrok forwarding URL into the
app.py file where
NGROK_BASE_URL is specified:
One bit to be aware of when using Ngrok is that your Forwarding URL will change whenever you restart the application, so you’ll need to update your code with the latest Forwarding URL.
To get even more out of Ngrok after finishing your Bottle project, make sure to read this 6 awesome reasons to use Ngrok when testing webhooks post.
Make sure your Bottle development server is still running or re-run it with the
python app.py command in a shell where your virtualenv is still activated.
Bring up your application in the web browser. Go to “localhost:5000/dial-phone/my-phone-number”, where “my-phone-number” is a number in the ” 12023350173′′ E.164 format. For example, here is what happens when I get the call on my iPhone:
When we pick up the phone call we also see the
/twiml route get called via Ngrok.
With only a couple of Bottle routes plus Twilio’s Voice API we were able to make phones ring!
Dialing Our Application
Outbound calls are handy, but what about if someone dials our application’s phone number? We have done all the hard work to make inbound calls possible. We just need to copy our Ngrok forwarding URL plus TwiML endpoint into the phone number configuration screen for the phone number we want to dial.
Click “Save” and dial your application’s number. You should get the same message and MP3 played as when you dialed the outbound phone call.
We can make and receive phone calls from our Bottle web application using our Twilio phone number. Next you could try to add even more features to your app by going through one of these tutorials:
- Create a phone-calling bot for Slack
- Upgrade your Bottle app to also send and respond to text messages
- Implement call tracking for calls made through your app
Questions? Contact me via
- Twitter: @mattmakai
- GitHub: mattmakai
- Email: firstname.lastname@example.org