Skip to content

Subscribers

Reseller operations. An account that resells TextMe capacity can create sub-accounts, fund their wallets from its own credit, and read their balances.

http
POST https://my.textme.co.il/api

If 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

NameTypeDescriptionRequired
addSubobjectContains all other elements.✔️
userobjectContains the user element.✔️
user.usernamestringYour username — the reseller account.✔️
userDetailsobjectThe details of the user you want to add.✔️
userDetails.namestringDisplay name for the new user.✔️
userDetails.usernamestringLogin username for the new user.✔️
userDetails.passwordstringPassword for the new user.✔️
userDetails.sourcestringDefault sender for the new user.✔️
userDetails.amountintCredits to grant on creation, taken from your balance.✔️
userDetails.otpPhonestringPhone number for OTP authentication.

Request example

xml
<?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>
json
{
  "addSub": {
    "user": {
      "username": "xxxxxx"
    },
    "userDetails": {
      "name": "israel israeli",
      "username": "israelisraeli",
      "password": "israelisraeli",
      "source": "055xxxxxxx",
      "amount": "70000",
      "otpPhone": "5xxxxxxxx"
    }
  }
}
bash
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"
    }
  }
}'
js
// 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
<?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
<?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);
python
# 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)
go
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
// 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());
    }
}
csharp
// .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);
ruby
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
rust
// [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
<?xml version="1.0" encoding="UTF-8"?>
<addSub>
    <status>0</status>
    <message>The user was created successfully</message>
</addSub>
json
{
  "status": 0,
  "message": "The user was created successfully"
}

Errors

StatusWhen
990amount exceeds the credit you hold.
991amount contains something other than digits.
992source is too long or too short.
993password is too long or too short.
994username already exists.
995username is too long or too short.
996name is too long or too short.
511Your account is not entitled to create sub-accounts.

updateAmountSub Top up a wallet

Moves credit into a sub-account's wallet.

Parameters

NameTypeDescriptionRequired
updateAmountSubobjectContains all other elements.✔️
userobjectContains the user element.✔️
user.usernamestringYour username — the reseller account.✔️
userDetailsobjectThe details of the user you want to update.✔️
userDetails.usernamestringThe sub-account's internal username.✔️
userDetails.amountintCredits to grant.✔️
userDetails.amount_intintUse 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
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
    <user>
        <username>xxxxxx</username>
    </user>
    <userDetails>
        <username>username1</username>
        <amount>70000</amount>
    </userDetails>
</updateAmountSub>
json
{
  "updateAmountSub": {
    "user": {
      "username": "xxxxxx"
    },
    "userDetails": {
      "username": "username1",
      "amount": "70000"
    }
  }
}
bash
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"
    }
  }
}'
js
// 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
<?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
<?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);
python
# 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)
go
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
// 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());
    }
}
csharp
// .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);
ruby
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
rust
// [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
<?xml version="1.0" encoding="UTF-8"?>
<updateAmountSub>
    <status>0</status>
    <message>Wallet successfully updated</message>
</updateAmountSub>
json
{
  "status": 0,
  "message": "Wallet successfully updated"
}

Errors

StatusWhen
990The amount exceeds the credit you hold.
991The amount contains something other than digits.
503userDetails.username is not one of your sub-accounts.
511Your 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

NameTypeDescriptionRequired
getBlanceSubsobjectContains all other elements.✔️
userobjectContains the user element.✔️
usernamestringYour username — the reseller account.✔️

Request example

xml
<?xml version="1.0" encoding="UTF-8"?>
<getBlanceSubs>
    <user>
        <username>xxxxxx</username>
    </user>
</getBlanceSubs>
json
{
  "getBlanceSubs": {
    "user": {
      "username": "xxxxxx"
    }
  }
}
bash
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "getBlanceSubs": {
    "user": {
      "username": "xxxxxx"
    }
  }
}'
js
// 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
<?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
<?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);
python
# 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)
go
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
// 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());
    }
}
csharp
// .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);
ruby
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
rust
// [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
<?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>
json
{
  "status": 0,
  "message": "",
  "balances": [
    {
      "amount": "80",
      "sms_user_id": "XXXX",
      "name": "name1"
    },
    {
      "amount": "50",
      "sms_user_id": "XXXX",
      "name": "name2"
    }
  ]
}
FieldTypeDescription
balancesarrayOne entry per sub-account.
balances[].amountstringCredits remaining.
balances[].sms_user_idstringThe sub-account's internal id.
balances[].namestringIts 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

StatusWhen
3, 10, 11Token invalid, expired, or belonging to another username.
511Your 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.