How to Send an MMS With Rust

August 05, 2025
Written by
Reviewed by
Paul Kamp
Twilion

How to Send an MMS With Rust

Not so long ago, here on the Twilio blog, I stepped through how to send an SMS in Rust using Twilio's Programmable Messaging API. It was a fun tutorial, which laid the groundwork for future programming possibilities.

In this follow up tutorial, let's extend it by showing how to send an MMS instead.

While MMS is a pretty common term, it's still helpful to know a bit about it and where it came from. So, quoting the Twilio Resource Center:

MMS stands for Multimedia Messaging Service. It was built using the same technology as SMS to allow SMS users to send multimedia content. It’s most popularly used to send pictures, but can also be used to send audio, phone contacts, and video files.

As of today, Twilio doesn't yet provide a helper library (SDK) to help keep Rust code as concise as it does for PHP, Go, and Python (among other languages). However, with a little help from Crates such as Reqwest, Serde, and Serde JSON, it's not an issue.

In this tutorial, you'll learn how to use Rust to send an MMS to a single recipient, which could be yourself or a friend, along with an image — simplified by using Twilio's Programmable Messaging API.

Let's begin!

Prerequisites

To follow along with this short tutorial, you only need a few things:

  • Rust and Cargo installed on your development machine
  • A Twilio account (free or paid) with a phone number that can send MMS. Create an account now if you don't have one already.
  • A mobile/cell phone that can receive SMS and MMS
  • Your preferred Rust editor or IDE; my preference is NeoVIM
  • Some prior Rust experience would be ideal, but isn't necessary

Create a new Rust project

Let's start by creating the shell of a new Rust binary project with Cargo and changing into the new project directory, by running the following commands.

cargo new mms-in-30-seconds --bin
cd new mms-in-30-seconds

Define the required dependencies

Now, we need to specify the dependencies (crates) that we'll need. Gladly, there are only four:

  • rust-dotenv: This sets environment variables from variables defined in a .env file
  • reqwest: This simplifies making HTTP requests to Twilio's Programmable Messaging API
  • Serde and Serde JSON: These help us efficiently deserialise the responses from requests into Rust data structures, allowing us to work with them programmatically

To set them, open Cargo.toml and update the dependencies table to match the following:

[dependencies]
dotenv = "0.15.0"
reqwest = {version="0.12.22", features = ["blocking"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.141"

Set the required environment variables

The next thing we need to do is to set the environment variables that the application needs. Create a new file named .env in the project's top-level directory, and paste the code below into the file.

RECIPIENT_PHONE_NUMBER="<RECIPIENT_PHONE_NUMBER>"
MEDIA_URL="https://live.staticflickr.com/3396/3552356644_3a319c92a0_k.jpg"
TWILIO_ACCOUNT_SID="<TWILIO_ACCOUNT_SID>"
TWILIO_AUTH_TOKEN="<TWILIO_AUTH_TOKEN>"
TWILIO_PHONE_NUMBER="<TWILIO_PHONE_NUMBER>"

Now, replace <RECIPIENT_PHONE_NUMBER> with your phone number. Then, you need to retrieve your Twilio access credentials, and your Twilio phone number.

Twilio Account Info page showing Account SID, Auth Token, and phone number with options to view or manage.

To do that, log in to the Twilio Console. Then, in the Account Info panel (which you can find at the bottom of the main dashboard), copy your Account SID, Auth Token, and Twilio phone number, and paste them in place of <TWILIO_ACCOUNT_SID>, <TWILIO_AUTH_TOKEN>, and <TWILIO_PHONE_NUMBER>, respectively.

Write the Rust code

Now it's time to write code (The fun part).

Open src/main.rs and paste the code below into the file, in place of the existing code.

use dotenv::dotenv;
use dotenv_codegen::dotenv;
use reqwest::{blocking::Client, Error, StatusCode};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct MMSResponse {
    account_sid: Option<String>,
    api_version: String,
    body: String,
    date_created: String,
    date_sent: String,
    date_updated: String,
    direction: String,
    error_code: String,
    error_message: String,
    from: String,
    messaging_service_sid: String,
    num_media: String,
    num_segments: String,
    price: String,
    price_unit: String,
    sid: String,
    status: String,
    subresource_uris: SubresourceUris,
    to: String,
    uri: String,
}

#[derive(Serialize, Deserialize)]
struct SubresourceUris {
    all_time: String,
    today: String,
    yesterday: String,
    this_month: String,
    last_month: String,
    daily: String,
    monthly: String,
    yearly: String,
}

#[derive(Serialize, Deserialize)]
struct ErrorResponse {
    code: u16,
    message: String,
    more_info: String,
    status: u16
}

fn handle_error(body: String) {
    let error_response: ErrorResponse = serde_json::from_str(&body)
        .expect("Unable to deserialise JSON error response.");
    println!("SMS was not able to be sent because: {:?}.", error_response.message);
}

fn handle_success(body: String) {
    let sms_response: MMSResponse = serde_json::from_str(&body)
        .expect("Unable to deserialise JSON success response.");
    println!("Your SMS with the body \"{:?}\".", sms_response.body);
}

fn main() -> Result<(), Error> {
    dotenv().ok();
    let client = Client::new();
    let request_params = [
        ("To", dotenv!("RECIPIENT_PHONE_NUMBER")),
        ("From", dotenv!("TWILIO_PHONE_NUMBER")),
        ("Body", "Sending an MMS in Rust with Twilio"),
        ("MediaUrl", dotenv!("MEDIA_URL")),
    ];
    let response = client
        .post(format!(
            "https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json",
            dotenv!("TWILIO_ACCOUNT_SID")
        ))
        .basic_auth(dotenv!("TWILIO_ACCOUNT_SID"), Some(dotenv!("TWILIO_AUTH_TOKEN")))
        .form(&request_params)
        .send()?;
    let status = response.status();
    let body = match response.text() {
        Ok(result) => result,
        Err(error) => panic!(
            "Problem extracting the JSON body content. Reason: {:?}",
            error
        ),
    };

    match status {
        StatusCode::BAD_REQUEST => handle_error(body),
        StatusCode::OK => handle_success(body),
        _ => println!("Received status code: {}", status),
    }

    Ok(())

}

The code imports the required Crates, then defines three structs: MMSResponse, SubresourceUris, and ErrorResponse. If API requests are successful, the first two will store the deserialised success response, containing all of the details of the sent MMS. And, if the request fails, ErrorResponse will store the error details returned in the response.

Up next are two functions: handle_error() and handle_success(). These deserialise the error and success responses, respectively, into the relevant structs.

Finally, the main() function is defined. This uses Dotenv to load the environment variables from .env. Then, it initialises a new Reqwest Client object, and the following request parameters:

  • To: The phone number to send the MMS to
  • From: The phone number to send the MMS from
  • Body: The message in the body of the MMS
  • MediaUrl: A publicly accessible URL where Twilio can retrieve the attachment from

Then, it uses the Client object to make a POST request to Twilio's Programmable Messaging API, https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json, where {} is your Twilio Account SID. The request includes your Twilio Account SID and Auth Token as credentials, and the previously defined request parameters.

Following that:

  • If the message was sent, the code deserialises the MMS details in the response into an MMSResponse struct and confirms that the MMS was sent in a message to the console.
  • If the message could not be sent, it deserialises the response into an ErrorResponse struct, and confirms the message failure in a terminal message.
  • If the request itself failed — such as because the supplied credentials were invalid — the returned HTTP status code is printed to the terminal.

Test that the code works as expected

With the project built, it's time to check that it works. So, back in your terminal, run the command below to compile and run the code, sending the MMS to your phone.

cargo run

All being well, you should receive an MMS shortly afterward, which looks similar to the screenshot below.

Smartphone showing a text message about sending an MMS in Rust with Twilio with a link included.

That's how to send an MMS in Rust

While it'd be great if there was an official Twilio Helper Library for Rust, we aren’t quite there yet. However, it's pretty straightforward to make an authenticated API call with a little Rust code and some crates. Hopefully you see just how simple it is with Reqwest, Serde and Serde JSON, and are inspired to explore the documentation to find out what else you can do.

Here are some suggestions to get you started:

Happy building!

Matthew Setter is (primarily) the PHP, Go, and Rust editor on the Twilio Voices team. He’s also the author of Mezzio Essentials and Deploy with Docker Compose. You can find him at msetter[at]twilio.com. He's also on LinkedIn and GitHub.

Mms icons created by Designspace Team on Flaticon.