Send SMS from PHP: No Twilio, Just Your Android Phone
Send real SMS from PHP using textbee and your Android phone as the gateway. Native cURL, Guzzle, and Laravel Http examples — zero per-message fees.
TL;DR
- textbee's REST API works from any PHP version that can make HTTP requests — no SDK needed.
- Three options: native cURL (zero dependencies), Guzzle (
composer require guzzlehttp/guzzle), or Laravel'sHttpfacade (one line). - Store credentials in environment variables, not hardcoded strings.
- The
recipientsfield is an array — one request can reach multiple numbers. - No per-message API fee: messages go through the SIM in your Android phone.
You don't need a Twilio account or a virtual phone number to send SMS from PHP. If you have an Android phone with a working SIM, textbee turns it into an SMS gateway your PHP code can call over a simple REST API. The message goes out from your real phone number.
Before you start
- textbee app installed on your Android device and linked to your account: download and quickstart
- Device ID and API key from the dashboard at textbee.dev
- PHP 7.4+ (the examples use modern syntax, but the cURL approach works on older versions too)
- On Android 15 or 16? You may need to manually enable SMS permissions once
Option 1: Native cURL (no dependencies)
This works on any PHP installation. No Composer, no packages.
<?php
function sendSms(string $to, string $message): array
{
$deviceId = getenv('TEXTBEE_DEVICE_ID');
$apiKey = getenv('TEXTBEE_API_KEY');
$url = "https://api.textbee.dev/api/v1/gateway/devices/{$deviceId}/send-sms";
$body = json_encode(['recipients' => [$to], 'message' => $message]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"x-api-key: {$apiKey}",
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status < 200 || $status >= 300) {
throw new RuntimeException("SMS send failed with HTTP {$status}: {$response}");
}
return json_decode($response, true);
}
// Usage
$result = sendSms('+15551234567', 'Hello from PHP!');
print_r($result);
Export your credentials before running:
export TEXTBEE_DEVICE_ID="your-device-id"
export TEXTBEE_API_KEY="your-api-key"
php send.php
Option 2: Guzzle HTTP
If your project already uses Guzzle (or you prefer a more expressive HTTP client):
composer require guzzlehttp/guzzle
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
function sendSms(string $to, string $message): array
{
$deviceId = getenv('TEXTBEE_DEVICE_ID');
$apiKey = getenv('TEXTBEE_API_KEY');
$client = new Client(['base_uri' => 'https://api.textbee.dev']);
try {
$response = $client->post("/api/v1/gateway/devices/{$deviceId}/send-sms", [
'headers' => [
'x-api-key' => $apiKey,
'Content-Type' => 'application/json',
],
'json' => [
'recipients' => [$to],
'message' => $message,
],
]);
return json_decode($response->getBody()->getContents(), true);
} catch (RequestException $e) {
$body = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '';
throw new RuntimeException("SMS send failed: {$body}", 0, $e);
}
}
$result = sendSms('+15551234567', 'Hello from Guzzle!');
print_r($result);
Option 3: Laravel Http facade
If you're in a Laravel project, this is the cleanest path:
use Illuminate\Support\Facades\Http;
function sendSms(string $to, string $message): array
{
$response = Http::withHeaders([
'x-api-key' => config('services.textbee.api_key'),
])->post(
'https://api.textbee.dev/api/v1/gateway/devices/' . config('services.textbee.device_id') . '/send-sms',
[
'recipients' => [$to],
'message' => $message,
]
);
$response->throw(); // throws on 4xx/5xx
return $response->json();
}
Add your credentials to config/services.php:
'textbee' => [
'api_key' => env('TEXTBEE_API_KEY'),
'device_id' => env('TEXTBEE_DEVICE_ID'),
],
And in .env:
TEXTBEE_API_KEY=your-api-key
TEXTBEE_DEVICE_ID=your-device-id
What the API returns
On success, you get a JSON response wrapped in data:
{
"data": {
"success": true,
"message": "SMS added to queue for processing",
"smsBatchId": "abc123xyz",
"recipientCount": 1
}
}
success: true means the message was accepted and queued on your device — not yet necessarily delivered to the recipient's handset. The device sends it asynchronously over the cellular network. Track delivery status in the textbee dashboard, or configure webhooks to get notified when status changes.
On failure, you get a non-2xx status code and a body like:
{
"success": false,
"error": "Device not found or not active"
}
Common causes: wrong device ID, API key mismatch, device offline, or SMS permissions revoked on the Android device.
Sending to multiple recipients
The recipients field is an array. Send one request to reach multiple numbers:
$result = sendSms(['+15551234567', '+15559876543'], 'Appointment reminder: tomorrow at 10am.');
// Adjust the function signature:
function sendSms(array|string $to, string $message): array
{
$recipients = is_array($to) ? $to : [$to];
// ... rest of the function, use $recipients in the JSON body
}
Things worth knowing
Phone number format: Use E.164 format (+ followed by country code and number, no spaces or dashes). Example: +15551234567 for a US number. Some carriers accept local formats for same-country sends, but E.164 is unambiguous and always correct.
No per-message API fee: Because messages route through your own Android SIM, you pay your carrier's SMS rate — which is often included in an unlimited plan. The textbee pricing is a flat monthly subscription, not per-message.
Opt-in matters: Messages come from your real phone number. Unsolicited texts can violate regulations regardless of which SMS technology you use. Read the compliance checklist before sending to lists.
Rate limits: The practical constraint is your SIM and carrier throughput (~hundreds of messages per hour on a consumer SIM), not an API rate limit. For higher volume, connect multiple Android devices to the same account.
Frequently asked questions
Does this work with Laravel?
Yes — Option 3 above uses Laravel's Http facade directly. For a full Laravel integration with queued jobs, see the dedicated Laravel SMS guide.
Do I need to install any packages?
No. Option 1 uses PHP's built-in curl extension, which is enabled by default on virtually every PHP installation. Options 2 and 3 require Guzzle/Laravel, but only if you prefer their interface.
Can I send to multiple numbers in one call?
Yes. Pass an array to recipients. One API call, one batch — each number gets the same message body.
What PHP version does this require?
Option 1 works on PHP 5.6+ (drop the type hints for older versions). Options 2 and 3 require PHP 7.4+ and their respective dependencies.
Is there a textbee PHP SDK?
Not yet. The REST API is simple enough that a raw HTTP call is all you need — typically 10–15 lines. The API docs cover all endpoints if you need inbound webhooks, device status, or batch operations beyond what's shown here.
Keep going
- Full API reference — send, receive, webhooks, device management
- How to send SMS programmatically — curl, Python, and JavaScript side by side
- SMS compliance checklist — before you send to a list
- Pricing — free tier available, paid plans start small
Download textbee and send your first PHP SMS in under 5 minutes.
You may also like
Cost-Effective SMS Solutions: Why textbee Beats Traditional SMS APIs in 2026
Traditional SMS APIs charge per segment plus carrier fees plus number rental plus 10DLC. An Android SMS gateway like textbee replaces all of that with a flat subscription. Real 2026 math, side-by-side examples, and a migration guide.
What Is an Android SMS Gateway? How It Works, Pros, Cons & When to Use One (2026)
An Android SMS gateway turns a phone you already own into a programmable SMS sender. Learn how it works, when it beats Twilio, real costs, and how to set one up in 5 minutes.

Developers in Emerging Markets: Twilio Doesn't Support Your Country? textbee Works Anywhere Android Works
When CPaaS onboarding, pricing, or coverage blocks you, a local Android phone on a real SIM is often the fastest path to programmatic SMS — same REST patterns, different constraints. A practical guide for developers underserved by Twilio, Plivo, and friends.