To send a picture by text from your app, call an MMS API with the recipient's number, a message body and a public URL pointing at the image. On Ready that is one request, POST /v1/messages with a media_url field, and the carrier delivers the image inline instead of as a link.
This post covers the request shape, what changes in billing when a text carries an image instead of plain SMS segments, general guidance on file size and format, and the cases where a plain link does the job better and costs less. It is written for developers and product leads at platforms that text on behalf of their own customers, and it applies just as well to anyone sending from a single account.
What an MMS API actually does
SMS carries text and nothing else. A single segment holds 160 GSM-7 characters, and longer messages split into 153-character parts. MMS (Multimedia Messaging Service) is a separate carrier path that carries a media file alongside the text. The recipient's phone downloads the file and shows it in the thread.
An MMS API wraps that path in an HTTP call. You don't upload bytes to the carrier yourself. You host the image somewhere public, pass the URL, and the platform fetches it and hands it to carrier infrastructure.
Three things follow from that design:
- The image has to be reachable. If your URL needs a session cookie, a signed header or a VPN, the fetch fails. A public HTTPS URL, or a signed URL that stays valid long enough to be fetched, is the safe default.
- The URL is not what the recipient sees. They get the picture. If you want them to click through to something, put that link in the body text.
- Registration still applies. MMS from a 10-digit long code goes over the same A2P 10DLC campaign as your SMS. A picture doesn't get around carrier filtering. Unregistered traffic gets filtered whether it carries an image or not. Our 10DLC explainer covers the background.
Sending a picture with the Ready mms api
Here is the full request. The base URL is https://api.tryready.com, auth goes in the X-API-Key header, and you should always send an Idempotency-Key (any UUID). If your HTTP client retries after a timeout, the key stops the same picture from going out twice.
curl -X POST https://api.tryready.com/v1/messages \
-H "X-API-Key: YOUR_API_KEY" \
-H "Idempotency-Key: 7f3c2a90-4d1e-4b8a-9c55-2e6f1a0b7d43" \
-H "Content-Type: application/json" \
-d '{
"to": "+12125550199",
"body": "Your order #1042 has shipped. Here is a photo of your package at our dock.",
"media_url": "https://cdn.example.com/shipments/1042.jpg",
"consent_attested": true
}' Field by field:
| Field | Required | What it does |
|---|---|---|
to | Yes | Recipient in E.164. A 10-digit US number is treated as +1. |
body | Yes | The text that goes with the image. |
media_url | For MMS | Public URL of the image. Including it makes the message MMS. |
consent_attested | Yes | Your promise that the recipient agreed to receive texts. |
from | No | One of your numbers. If you leave it out, the platform picks one. |
from_strategy | No | local, even or random for choosing among your numbers. |
These are the only fields we'll show here. POST /v1/messages/bulk accepts the same media_url (one URL for every recipient, up to 100 recipients per call). For anything else, check the messages API docs and the bulk messages docs rather than guessing at payloads.
What consent_attested does and doesn't record
Setting consent_attested: true records your attestation for the send: who sent it, to how many recipients, and when. That builds an audit trail on our side. It is not a per-contact consent record. We don't store the recipient's opt-in time, the wording they agreed to, or the page URL where they signed up. Your app should keep that. If a consent dispute ever comes up, your signup log is the evidence that matters. (A contact who signs up on a Ready-hosted opt-in page is the exception: Ready stores that sign-up, with the time, IP address and exact wording, and support@tryready.com can send you a copy.)
What happens before the picture goes out
Some sends never reach the carrier, and that's by design:
- Opt-outs: a number that replied STOP is refused up front and not charged. You can check status with
GET /v1/opt-outs/:phone. - Numbers on your Ban list, and numbers our own sends have already seen as landlines, are refused up front and not charged, on every account. With landline validation on, a number the lookup marks as a landline is refused too, and that lookup bills the $0.003 check.
- Quiet hours: an API text addressed to someone outside permitted local hours is held and sent later if their window opens within 6 hours; otherwise it's refused. It's charged only when it actually sends.
If you want to check a number before you build an image for it (a personalized receipt image, say), GET /v1/validate/:phone tells you whether it's worth rendering. Optional compliance scrubbing is covered in the compliance docs.
For the wider integration (retries and idempotency strategy), see our SMS API integration guide.
How MMS billing compares with SMS segments
This is where most teams get surprised. They budget for MMS as if it were SMS, or they treat every picture as unaffordable.
SMS is billed per segment. On Standard that's $0.0075/segment plus a $0.0045 carrier fee per segment. The all-in figure excludes the optional compliance surcharge: scrubbing and landline validation each add $0.003 per segment when switched on for an account, and both are off by default.
MMS is billed as a picture message, not by counting 153-character segments. On Ready, an MMS is a flat $0.021 plus a $0.0095 MMS carrier fee, $0.0305 all-in, whatever its length. The practical point: a picture message generally costs more than a short SMS on every carrier, which is why the "should this be a link?" question below matters.
Worked example: the link version of the same campaign
To compare, cost out the text-only version and set it against the MMS price above ($0.0305 a message all-in).
Assumptions: 5,000 opted-in contacts and a 220-character message with a short link, no emoji.
- 220 GSM-7 characters is over 160, so it splits into 153-character parts: 220 ÷ 153 = 1.44, rounded up to 2 segments.
- 5,000 contacts × 2 segments = 10,000 segments.
- Rate portion: 10,000 × $0.0075 = $75.
- Carrier portion: 10,000 × $0.0045.
- Total: $200 plus 10,000 × $0.0045, before any compliance surcharge you've switched on.
Now add one emoji to that same 220-character body. Unicode messages cap at 70 characters, or 67 per part when split. 220 ÷ 67 = 3.28, rounded up to 4 segments. That doubles the job to 20,000 segments and $150 on the rate portion alone. A single emoji can do more to your SMS bill than switching to a picture would. (Ready swaps curly quotes, long dashes and ellipses for plain ones before sending, so those don't trigger the unicode limit. Emoji still do.)
Multiply your contact count by the MMS price ($0.0305 a message all-in) and compare it with the number above. That's the real decision. "MMS is expensive" isn't.
Replies cost money too
Inbound replies are billed like a sent text: the per-segment rate plus the carrier fee. A picture that gets a lot of "love it!" replies costs a bit more than the send alone. That's a good problem to have, but put it in the budget.
Size and format guidance
Carriers don't publish one universal spec, and the limits vary by carrier and by handset. So treat the following as general good practice, not a guarantee:
- Stick to common formats. JPEG for photos, PNG for graphics with text or sharp edges, GIF for short animations. Exotic formats (HEIC, WebP, SVG) are the most likely to arrive broken or be converted badly.
- Keep files small. Carriers resize or recompress large images, and very large files can fail outright. Keep each file under 1 MB, which is the size the Ready app holds a picture to. A 1080px-wide JPEG at moderate quality usually looks fine on a phone and stays well under that.
- Design for a small screen. Text baked into an image should be large and high contrast. Fine print in a picture gets mangled by recompression.
- Serve with the right content type. If your CDN serves a JPEG as
application/octet-stream, the fetch can fail or the phone may not render it. - Keep the URL alive. Short-lived signed URLs should stay valid for at least several minutes, longer if the send could be held for quiet hours. A held message is fetched when it sends, not when you called the API.
That last point catches people. If you queue a 3 AM promo with a 5-minute signed URL and the recipient's quiet hours hold it until the window opens, the URL has expired by the time it's fetched. Use a longer-lived URL for anything that might be held. Campaigns, drip campaigns and automations wait up to 24 hours for the window; a one-to-one or API text is held only if the window opens within 6 hours, otherwise it is refused with 409 QUIET_HOURS_BLOCK and must be resent; a one-to-one reply to someone who texted you in the last 24 hours goes out at any hour.
When a link beats a picture
An image is persuasive in the thread, but it isn't always the right tool. Use this rough rule:
Send the picture when:
- The image is the message: a proof photo, a delivery confirmation, a finished-job photo, a QR code someone will scan at a counter.
- The recipient needs to see it without tapping, for example when they're on the go or about to walk into a store.
- You're sending one-to-one or to a small list where the cost difference barely registers.
Send a link when:
- The content is long, changes, or needs interaction: a full receipt, a booking page, a product catalog.
- You're texting a large list and the MMS price multiplied by your contact count is more than the result justifies.
- You need to track clicks. The image itself gives you no tap data. A link to your own domain does.
Two caveats about links. Many iPhones won't show the rich preview card you expect, because carriers often strip the unfurl. We explain what survives in why your branded link preview never shows up on iPhone. And public URL shorteners shared by thousands of senders are a common filtering trigger, so use your own domain. Our SMS vs MMS breakdown goes further into which use cases earn the picture.
Getting set up without wasting free sends
The order matters if you want to test MMS on real phones before you commit:
- Sign up and add a card. To send, you add a card and get a number, which is $2 a month with the first month charged when you get it.
- Test one-to-one first. Your first 20 texts are free, and they're one-to-one sends. A campaign is paid from its first text. Send your test picture to yourself and a couple of teammates on different carriers and handsets before you register, because the free texts end the moment a registration is paid for.
- Register. Brand plus campaign registration runs in the app. The charge at submit is the first month, $25. After that the monthly is $25 for registration and $2 per number. Most approvals take 2 to 7 days. Before registering, an account can send up to 500 texts a month at its normal per-text rate. If you bought a number before registering, you send on our shared registration at the same rate while yours is in review.
- Check status with
GET /v1/registrationso your app can gate sending until it's approved.
If you're a platform onboarding your own customers, each one gets a sub-account (POST /v1/sub-accounts) with its own numbers and registration, so one customer's image campaigns don't affect another's sender reputation.
A note on the blue line
If you're also using Ready's blue line (texts that arrive as iMessage on iPhones), know that photos can only go out after the contact has replied. Campaigns and drip campaigns send from regular numbers, not the blue line. For a first-touch picture to a new contact, the regular-number MMS path above is the one to use.
Where a raw CPaaS might suit you better
We'll say it plainly: if all you need is the cheapest possible pipe for pictures and you're happy to build registration, opt-out handling, quiet hours and an inbox yourself, a raw CPaaS like Twilio prices its per-message rate below ours. What Ready includes on top is self-serve 10DLC registration, STOP handling that carries across campaigns, quiet-hours holds, optional scrubbing and validation, sub-accounts for your customers, and an inbox where replies to your pictures land. Whether that's worth it depends on how much of it you'd otherwise build and maintain. Our Ready vs Twilio comparison works through the numbers.
The practical takeaway
Sending a picture is one field: add media_url to POST /v1/messages, host the image publicly, keep it small and in a common format, and send an Idempotency-Key so retries are safe. The harder decision is whether a picture beats a link for that message. Take the MMS price ($0.0305 a message all-in), multiply by your list size, and compare it with the segment math for the text-and-link version. Use your free one-to-one texts to test on real handsets before you register.
When you're ready, the quickstart gets you an API key, and you can create an account here.
FAQ
How do I send a picture by text from my app? Call an MMS API with the recipient's number, a message body and a public URL for the image. On Ready that's POST /v1/messages with a media_url field. The platform fetches the image and delivers it inline through carrier infrastructure.
Does MMS cost more than SMS? Generally yes. A picture message costs more than a short single-segment SMS with most providers. On Ready an MMS is $0.0305 all-in; compare it with your SMS segment math, because a long text with emoji can cost several segments too.
What image size and format should I use for MMS? Use JPEG for photos or PNG for graphics, and keep files to a few hundred kilobytes. Carriers vary in what they accept and often recompress large images, so small, common-format files are the most reliable.
Do I need 10DLC registration to send MMS? Yes, if you're sending from a standard 10-digit business number. MMS goes over the same A2P 10DLC campaign as your SMS, and unregistered traffic gets filtered whether or not it carries an image.
Should I send an image or a link in my text? Send the image when the picture itself is the message and the recipient needs to see it without tapping. Send a link to your own domain when the content is long, needs interaction or click tracking, or when the list is large enough that the per-message difference adds up.