Skip to content

Balance

Reports how much credit the account has left.

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

balance Check remaining credit

Parameters

NameTypeDescriptionRequired
balanceobjectContains all other elements.✔️
userobjectContains the user element.✔️
usernamestringThe username of the account by which you are recognized in the system.✔️
typestringSend mail to read the mail balance instead. Omit it for the SMS balance.

Request example

xml
<?xml version="1.0" encoding="UTF-8"?>
<balance>
    <user>
        <username>Leeroy</username>
    </user>
    <type>mail</type>
</balance>
json
{
  "balance": {
    "user": {
      "username": "Leeroy"
    },
    "type": "mail"
  }
}
bash
curl --location 'https://my.textme.co.il/api' \
--header "Authorization: Bearer $TEXTME_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
  "balance": {
    "user": {
      "username": "Leeroy"
    },
    "type": "mail"
  }
}'
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({
    balance: {
      user: {
        username: 'Leeroy',
      },
      type: 'mail',
    },
  }),
})

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' => [
        'balance' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'type' => 'mail',
        ],
    ],
]);

$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', [
        'balance' => [
            'user' => [
                'username' => 'Leeroy',
            ],
            'type' => 'mail',
        ],
    ])
    ->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={
        "balance": {
            "user": {
                "username": "Leeroy",
            },
            "type": "mail",
        },
    },
)
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{
		"balance": map[string]any{
			"user": map[string]any{
				"username": "Leeroy",
			},
			"type": "mail",
		},
	})

	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 TextMeBalance {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "balance": {
                "user": {
                  "username": "Leeroy"
                },
                "type": "mail"
              }
            }
            """;

        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 = """
    {
      "balance": {
        "user": {
          "username": "Leeroy"
        },
        "type": "mail"
      }
    }
    """;

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({
  "balance" => {
    "user" => {
      "username" => "Leeroy",
    },
    "type" => "mail",
  },
})

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!({
          "balance": {
            "user": {
              "username": "Leeroy"
            },
            "type": "mail"
          }
        }))
        .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"?>
<balance>
    <status>0</status>
    <message>Your balance is : 42 and international balance: 40.5 nis</message>
    <balance>42</balance>
    <interantional_balance>40.5</interantional_balance>
</balance>
json
{
  "status": 0,
  "message": "Your balance is : 42 and international balance: 40.5 nis",
  "balance": "42",
  "international_balance": "40.5"
}
FieldTypeDescription
statusint0 on success.
messagestringThe balance as a sentence, e.g. Your balance is : 42 and international balance: 40.5 nis.
balancestringDomestic credit remaining.
international_balancestringInternational credit remaining, in shekels.

The XML spells one field differently

In the XML response the international figure comes back as <interantional_balance> — a typo baked into the wire format. The JSON response spells it international_balance. Match whichever encoding you are parsing rather than the one you expect.

Errors

StatusWhen
3, 10, 11Token invalid, expired, or belonging to another username.
503username is not an account you may act for.
511The account is not entitled to this operation.

Field notes

Two balances, two currencies

balance counts domestic message credit. international_balance is money, in shekels, drawn down when you send to destinations outside Israel with includes_international set to 1. Running out of either produces status 4 (Not enough credit) or 12 (Not enough money) on the next send — which one depends on the destination.

Checking before a large run

A cheap pre-flight before a big bulk send, since a batch that outruns the balance fails as a whole rather than partway:

js
const balance = await textme({ balance: { user: { username: 'Leeroy' } } })

if (Number(balance.balance) < recipients.length) {
  throw new Error(
    `Need ${recipients.length} credits, have ${balance.balance}`,
  )
}

await textme({ bulk: { /* … */ } })
python
balance = textme({"balance": {"user": {"username": "Leeroy"}}})

if int(balance["balance"]) < len(recipients):
    raise RuntimeError(
        f"Need {len(recipients)} credits, have {balance['balance']}"
    )

textme({"bulk": {...}})

Treat it as a guard rather than a guarantee: another process on the same account can spend the credit between the two calls. The authoritative answer is still the status on the send.

Sub-account balances

balance reports the calling account only. Resellers read every sub-account at once with getBlanceSubs, and move credit into them with updateAmountSub.