
Send SMS from Java: No Twilio, Just Your Android Phone
Send real SMS from Java using textbee and your Android phone as the gateway. Native HttpClient, OkHttp, and Spring Boot examples, zero per-message fees.
TL;DR
- textbee's REST API works from any Java version with HTTP capability: no SDK or Twilio account needed.
- Three options: native
HttpClient(Java 11+, zero dependencies), OkHttp (Maven/Gradle), or Spring BootRestTemplate. - Store credentials in environment variables, not hardcoded strings.
- The
recipientsfield is a JSON array: one request sends to multiple numbers. - No per-message API fee: messages route through the SIM in your Android phone.
You don't need a Twilio account or a rented virtual number to send SMS from Java. If you have an Android phone with a working SIM, textbee turns it into an SMS gateway your Java code can call over a plain 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
- Java 11+ for the native
HttpClientexample; Java 8+ for OkHttp - On Android 15 or 16? You may need to manually enable SMS permissions once
Option 1: Native HttpClient (Java 11+, no dependencies)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class TextbeeSender {
private static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
public static String sendSms(String to, String message) throws Exception {
String apiKey = System.getenv("TEXTBEE_API_KEY");
String url = "https://api.textbee.dev/api/v1/gateway/send-sms";
String body = String.format(
"{\"recipients\": [\"%s\"], \"message\": \"%s\"}",
to, message.replace("\"", "\\\"")
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.header("Content-Type", "application/json")
.header("x-api-key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("SMS send failed (HTTP " + response.statusCode() + "): " + response.body());
}
return response.body();
}
public static void main(String[] args) throws Exception {
String result = sendSms("+15551234567", "Hello from Java!");
System.out.println(result);
}
}Run with environment variables set:
export TEXTBEE_DEVICE_ID="your-device-id"
export TEXTBEE_API_KEY="your-api-key"
javac TextbeeSender.java && java TextbeeSenderNote on string formatting: The String.format approach above works for simple messages. For production, use a JSON library (Gson, Jackson, or org.json) to avoid escaping bugs with special characters in message bodies.
Option 1b: Native HttpClient with Jackson (recommended for production)
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.Map;
public class TextbeeSender {
private static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)).build();
private static final ObjectMapper MAPPER = new ObjectMapper();
public static Map<?, ?> sendSms(String to, String message) throws Exception {
String apiKey = System.getenv("TEXTBEE_API_KEY");
String url = "https://api.textbee.dev/api/v1/gateway/send-sms";
String body = MAPPER.writeValueAsString(Map.of(
"recipients", List.of(to),
"message", message
));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.header("Content-Type", "application/json")
.header("x-api-key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new RuntimeException("SMS send failed (HTTP " + response.statusCode() + "): " + response.body());
}
return MAPPER.readValue(response.body(), Map.class);
}
}Add Jackson to your pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>Or build.gradle:
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'Option 2: OkHttp (Java 8+)
OkHttp works on Java 8 and is popular in Android and backend Java projects:
import okhttp3.*;
import com.google.gson.Gson;
import java.util.List;
import java.util.Map;
public class TextbeeSender {
private static final OkHttpClient CLIENT = new OkHttpClient();
private static final Gson GSON = new Gson();
private static final MediaType JSON = MediaType.get("application/json");
public static String sendSms(String to, String message) throws Exception {
String apiKey = System.getenv("TEXTBEE_API_KEY");
String url = "https://api.textbee.dev/api/v1/gateway/send-sms";
String body = GSON.toJson(Map.of("recipients", List.of(to), "message", message));
Request request = new Request.Builder()
.url(url)
.addHeader("x-api-key", apiKey)
.post(RequestBody.create(body, JSON))
.build();
try (Response response = CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new RuntimeException("SMS send failed (HTTP " + response.code() + "): " + response.body().string());
}
return response.body().string();
}
}
}Add OkHttp and Gson to pom.xml:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>Option 3: Spring Boot RestTemplate
In a Spring Boot application, RestTemplate integrates cleanly with your existing configuration:
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.*;
import org.springframework.beans.factory.annotation.Value;
import java.util.List;
import java.util.Map;
@Service
public class SmsService {
private final RestTemplate restTemplate;
@Value("${textbee.api-key}")
private String apiKey;
public SmsService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Map<?, ?> sendSms(String to, String message) {
String url = "https://api.textbee.dev/api/v1/gateway/send-sms";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("x-api-key", apiKey);
Map<String, Object> body = Map.of(
"recipients", List.of(to),
"message", message
);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.exchange(
url, HttpMethod.POST, entity, Map.class
);
return response.getBody();
}
}Add to application.properties (reference environment variables):
textbee.device-id=${TEXTBEE_DEVICE_ID}
textbee.api-key=${TEXTBEE_API_KEY}Register a RestTemplate bean in your configuration:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}What the API returns
On success:
{
"data": {
"success": true,
"message": "SMS added to queue for processing",
"smsBatchId": "abc123xyz",
"recipientCount": 1
}
}On failure, you get a non-2xx HTTP status. The response body contains an error message. All three examples above throw a RuntimeException with the status code and body: catch it in your calling code and handle accordingly (retry logic, alerting, fallback).
Sending to multiple recipients
Pass multiple numbers in the recipients list:
String body = MAPPER.writeValueAsString(Map.of(
"recipients", List.of("+15551111111", "+15552222222", "+15553333333"),
"message", "Reminder: team meeting at 3pm today."
));All recipients get the same message body in one API call and one batch ID.
Things worth knowing
Phone number format: Use E.164 (+15551234567). Local formats may work for same-country sends but E.164 is unambiguous and always correct.
Thread safety: HttpClient (Java 11+) and OkHttpClient are thread-safe and should be shared as singletons: create once, reuse everywhere. Don't instantiate a new client per request.
No per-message fee: Messages route through your own Android SIM. See pricing for the flat subscription tiers.
Opt-in compliance: Read the SMS compliance checklist before sending to lists.
Frequently asked questions
Is there a Java SDK for textbee?
Not yet. The REST API is simple enough: the examples above cover all common use cases. The API docs cover inbound webhooks, device status, and batch operations for anything beyond single sends.
What Java version is required?
Option 1 (native HttpClient) requires Java 11+. Options 2 (OkHttp) and 3 (Spring Boot) work on Java 8+. For new projects, Java 17 LTS or Java 21 LTS is recommended.
Can I use this in a Spring Boot application?
Yes. Option 3 above integrates directly into the Spring dependency injection model. For async sends (non-blocking), wrap the RestTemplate call in a @Async method or use WebClient from Spring WebFlux instead.
How do I handle async sending in Java?
Wrap the send call in a CompletableFuture:
CompletableFuture.runAsync(() -> {
try {
sendSms("+15551234567", "Your order has shipped!");
} catch (Exception e) {
log.error("SMS send failed", e);
}
});Or use Spring's @Async annotation on the service method for cleaner integration.
Keep going
- Full API reference: send, receive, webhooks, device management
- Receive SMS with webhooks: inbound SMS in a Java server
- Send SMS from Go, Python, PHP: same API, other languages
- Pricing: free tier available
Download textbee and send your first Java SMS in under 5 minutes.
You may also like

Send SMS from Make.com, Zapier, and n8n with textbee
Automate SMS without a dev team: HTTP module config, phone normalization, retry logic, and workflow patterns for Make.com, Zapier, and n8n. No code needed.

Send SMS from Node.js: No Twilio, Just Your Android Phone
Send real SMS messages from Node.js using your Android phone as the gateway. One async function, your own phone number, zero per-message fees.

Best Free SMS Gateway in 2026 (No Monthly Fee, No Credit Card Trap)
Honest comparison of free SMS gateway options in 2026: textbee, Twilio trial, Vonage sandbox, TextLocal, and more. What's actually free vs what's a trial disguised as free.