Subscribers
Reseller operations. An account that resells TextMe capacity can create sub-accounts, fund their wallets from its own credit, and read their balances.
POST https://my.textme.co.il/apiIf your account does not resell, these return status 511 and you can skip the page.
Two usernames, two roles
Throughout this page, user.username is your account — the reseller doing the work — while userDetails.username is the sub-account being acted on. Getting them the wrong way round is the most common mistake here.
addSub Create a sub-account
Creates a sub-account with login credentials, a default sender and an opening credit balance drawn from yours.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
addSub | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
user.username | string | Your username — the reseller account. | ✔️ |
userDetails | object | The details of the user you want to add. | ✔️ |
userDetails.name | string | Display name for the new user. | ✔️ |
userDetails.username | string | Login username for the new user. | ✔️ |
userDetails.password | string | Password for the new user. | ✔️ |
userDetails.source | string | Default sender for the new user. | ✔️ |
userDetails.amount | int | Credits to grant on creation, taken from your balance. | ✔️ |
userDetails.otpPhone | string | Phone number for OTP authentication. | ➖ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<addSub>
<user>
<username>xxxxxx</username>
</user>
<userDetails>
<name>israel israeli</name>
<username>israelisraeli</username>
<password>israelisraeli</password>
<source>055xxxxxxx</source>
<amount>70000</amount>
<otpPhone>5xxxxxxxx</otpPhone>
</userDetails>
</addSub>{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}'// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://my.textme.co.il/api', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
addSub: {
user: {
username: 'xxxxxx',
},
userDetails: {
name: 'israel israeli',
username: 'israelisraeli',
password: 'israelisraeli',
source: '055xxxxxxx',
amount: '70000',
otpPhone: '5xxxxxxxx',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too — the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'addSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'name' => 'israel israeli',
'username' => 'israelisraeli',
'password' => 'israelisraeli',
'source' => '055xxxxxxx',
'amount' => '70000',
'otpPhone' => '5xxxxxxxx',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too — the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'addSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'name' => 'israel israeli',
'username' => 'israelisraeli',
'password' => 'israelisraeli',
'source' => '055xxxxxxx',
'amount' => '70000',
'otpPhone' => '5xxxxxxxx',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"addSub": {
"user": {
"username": "xxxxxx",
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too — the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"addSub": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
"userDetails": map[string]any{
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too — the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberAdd {
public static void main(String[] args) throws Exception {
String body = """
{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too — the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too — the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"addSub" => {
"user" => {
"username" => "xxxxxx",
},
"userDetails" => {
"name" => "israel israeli",
"username" => "israelisraeli",
"password" => "israelisraeli",
"source" => "055xxxxxxx",
"amount" => "70000",
"otpPhone" => "5xxxxxxxx",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"addSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"name": "israel israeli",
"username": "israelisraeli",
"password": "israelisraeli",
"source": "055xxxxxxx",
"amount": "70000",
"otpPhone": "5xxxxxxxx"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too — the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<addSub>
<status>0</status>
<message>The user was created successfully</message>
</addSub>{
"status": 0,
"message": "The user was created successfully"
}Errors
| Status | When |
|---|---|
990 | amount exceeds the credit you hold. |
991 | amount contains something other than digits. |
992 | source is too long or too short. |
993 | password is too long or too short. |
994 | username already exists. |
995 | username is too long or too short. |
996 | name is too long or too short. |
511 | Your account is not entitled to create sub-accounts. |
updateAmountSub Top up a wallet
Moves credit into a sub-account's wallet.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
updateAmountSub | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
user.username | string | Your username — the reseller account. | ✔️ |
userDetails | object | The details of the user you want to update. | ✔️ |
userDetails.username | string | The sub-account's internal username. | ✔️ |
userDetails.amount | int | Credits to grant. | ✔️ |
userDetails.amount_int | int | Use this instead of amount to update the sub-account's money balance rather than its message credits. Send it without an amount element. | ➖ |
amount and amount_int are alternatives
amount moves message credits; amount_int moves money. Send one or the other, never both — amount_int is documented as replacing amount, not supplementing it.
Request example
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
<user>
<username>xxxxxx</username>
</user>
<userDetails>
<username>username1</username>
<amount>70000</amount>
</userDetails>
</updateAmountSub>{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}'// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://my.textme.co.il/api', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
updateAmountSub: {
user: {
username: 'xxxxxx',
},
userDetails: {
username: 'username1',
amount: '70000',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too — the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'updateAmountSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'username' => 'username1',
'amount' => '70000',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too — the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'updateAmountSub' => [
'user' => [
'username' => 'xxxxxx',
],
'userDetails' => [
'username' => 'username1',
'amount' => '70000',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"updateAmountSub": {
"user": {
"username": "xxxxxx",
},
"userDetails": {
"username": "username1",
"amount": "70000",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too — the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"updateAmountSub": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
"userDetails": map[string]any{
"username": "username1",
"amount": "70000",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too — the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberWallet {
public static void main(String[] args) throws Exception {
String body = """
{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too — the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too — the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"updateAmountSub" => {
"user" => {
"username" => "xxxxxx",
},
"userDetails" => {
"username" => "username1",
"amount" => "70000",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"updateAmountSub": {
"user": {
"username": "xxxxxx"
},
"userDetails": {
"username": "username1",
"amount": "70000"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too — the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
<status>0</status>
<message>Wallet successfully updated</message>
</updateAmountSub>{
"status": 0,
"message": "Wallet successfully updated"
}Errors
| Status | When |
|---|---|
990 | The amount exceeds the credit you hold. |
991 | The amount contains something other than digits. |
503 | userDetails.username is not one of your sub-accounts. |
511 | Your account is not entitled to this operation. |
Not idempotent
There is no request id and no deduplication. Calling this twice grants the credit twice. If a call times out, read the balance with getBlanceSubs before retrying — never blind-retry a top-up.
getBlanceSubs Read every balance
Returns the balance of every sub-account in one call.
The name is misspelled on the wire
The root element really is getBlanceSubs — "Blance", not "Balance". Spelling it correctly returns status 997, Not a valid command sent.
Parameters
| Name | Type | Description | Required |
|---|---|---|---|
getBlanceSubs | object | Contains all other elements. | ✔️ |
user | object | Contains the user element. | ✔️ |
username | string | Your username — the reseller account. | ✔️ |
Request example
<?xml version="1.0" encoding="UTF-8"?>
<getBlanceSubs>
<user>
<username>xxxxxx</username>
</user>
</getBlanceSubs>{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}'// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://my.textme.co.il/api', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
getBlanceSubs: {
user: {
username: 'xxxxxx',
},
},
}),
})
const result = await response.json()
// Errors arrive as HTTP 200 too — the payload status is what counts
if (Number(result.status) !== 0) {
throw new Error(`TextMe ${result.status}: ${result.message}`)
}
console.log(result)<?php
// composer require guzzlehttp/guzzle
$client = new \GuzzleHttp\Client([
'headers' => [
'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->post('https://my.textme.co.il/api', [
'json' => [
'getBlanceSubs' => [
'user' => [
'username' => 'xxxxxx',
],
],
],
]);
$result = json_decode($response->getBody()->getContents(), true);
// Errors arrive as HTTP 200 too — the payload status is what counts
if ((int) $result['status'] !== 0) {
throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
}
print_r($result);<?php
use Illuminate\Support\Facades\Http;
$result = Http::withToken(config('services.textme.token'))
->acceptJson()
->post('https://my.textme.co.il/api', [
'getBlanceSubs' => [
'user' => [
'username' => 'xxxxxx',
],
],
])
->throw()
->json();
// Errors arrive as HTTP 200 too — the payload status is what counts
throw_if((int) $result['status'] !== 0, RuntimeException::class,
"TextMe {$result['status']}: {$result['message']}");
logger()->info('TextMe', $result);# pip install httpx
import os
import httpx
response = httpx.post(
"https://my.textme.co.il/api",
headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
json={
"getBlanceSubs": {
"user": {
"username": "xxxxxx",
},
},
},
)
response.raise_for_status()
result = response.json()
# Errors arrive as HTTP 200 too — the payload status is what counts
if int(result["status"]) != 0:
raise RuntimeError(f"TextMe {result['status']}: {result['message']}")
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"getBlanceSubs": map[string]any{
"user": map[string]any{
"username": "xxxxxx",
},
},
})
req, _ := http.NewRequest("POST", "https://my.textme.co.il/api", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TEXTME_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var result struct {
Status json.Number `json:"status"`
Message string `json:"message"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
panic(err)
}
// Errors arrive as HTTP 200 too — the payload status is what counts
if result.Status.String() != "0" {
panic(fmt.Sprintf("TextMe %s: %s", result.Status, result.Message))
}
fmt.Println(result.Message)
}// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class TextMeSubscriberBalances {
public static void main(String[] args) throws Exception {
String body = """
{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://my.textme.co.il/api"))
.header("Authorization", "Bearer " + System.getenv("TEXTME_API_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// Errors arrive as HTTP 200 too — the payload status is what counts
System.out.println(response.body());
}
}// .NET 8+ — System.Net.Http
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var payload = """
{
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}
""";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("TEXTME_API_TOKEN"));
var response = await http.PostAsync("https://my.textme.co.il/api",
new StringContent(payload, Encoding.UTF8, "application/json"));
var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
var status = result.GetProperty("status").ToString();
// Errors arrive as HTTP 200 too — the payload status is what counts
if (status != "0")
{
var message = result.GetProperty("message").ToString();
throw new Exception($"TextMe {status}: {message}");
}
Console.WriteLine(result);require "net/http"
require "json"
uri = URI("https://my.textme.co.il/api")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('TEXTME_API_TOKEN')}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"getBlanceSubs" => {
"user" => {
"username" => "xxxxxx",
},
},
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
# Errors arrive as HTTP 200 too — the payload status is what counts
raise "TextMe #{result['status']}: #{result['message']}" unless result["status"].to_i.zero?
pp result// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result: Value = reqwest::Client::new()
.post("https://my.textme.co.il/api")
.bearer_auth(std::env::var("TEXTME_API_TOKEN")?)
.json(&json!({
"getBlanceSubs": {
"user": {
"username": "xxxxxx"
}
}
}))
.send()
.await?
.json()
.await?;
// Errors arrive as HTTP 200 too — the payload status is what counts
if result["status"] != 0 {
return Err(format!("TextMe {}: {}", result["status"], result["message"]).into());
}
println!("{result}");
Ok(())
}Response
<?xml version="1.0" encoding="UTF-8"?>
<getBlanceSubs>
<status>0</status>
<message></message>
<balances>
<balance>
<amount>80</amount>
<sms_user_id>xxx</sms_user_id>
<name>name1</name>
</balance>
<balance>
<amount>50</amount>
<sms_user_id>xxx</sms_user_id>
<name>name2</name>
</balance>
</balances>
</getBlanceSubs>{
"status": 0,
"message": "",
"balances": [
{
"amount": "80",
"sms_user_id": "XXXX",
"name": "name1"
},
{
"amount": "50",
"sms_user_id": "XXXX",
"name": "name2"
}
]
}| Field | Type | Description |
|---|---|---|
balances | array | One entry per sub-account. |
balances[].amount | string | Credits remaining. |
balances[].sms_user_id | string | The sub-account's internal id. |
balances[].name | string | Its display name. |
The response has no username
Entries carry name and sms_user_id, but not the login username you used in addSub. If you need to map balances back to your own records, store sms_user_id — or keep the name unique and meaningful.
Errors
| Status | When |
|---|---|
3, 10, 11 | Token invalid, expired, or belonging to another username. |
511 | Your account is not entitled to this operation. |
Field notes
Credit flows one way through the API
addSub and updateAmountSub move credit from you to a sub-account. There is no operation that pulls it back. Grant in the amounts you actually intend to hand over, and top up more often rather than in large blocks.
Acting on a sub-account's behalf
Your token authenticates; user.username chooses who the operation is for. Set it to a sub-account's username and you can send, read reports and manage lists on their behalf — see Authentication. The account must be one of yours, or the call fails with 503.
Sub-accounts verify their own senders; getVerifiedPhones with is_subs: 1 gives you a view across all of them.
Passwords go over the wire
addSub carries a plaintext password. It is TLS-protected in transit, but it will end up in any request log you keep — redact userDetails.password before logging, and generate the value rather than reusing one a human picked.

