03-7440020 +1 917-277-5362 +44 203 146-8524
Log in עברית
Meser10
Sign up free
OTP

Send a one-time password to an Israeli phone

One HTTP call, no SDK, no queue. An Israeli sender with local carrier routing, Hebrew and English in the same message body, and an API that has been in production for years.

A verification code is the simplest message there is: one recipient, one line of text, no list. It is also the one that matters most when it is late, because somebody is sitting in front of a login screen waiting for it. So it goes through its own function on the JSON API, with no campaign, no schedule and no mailing list in the way.

Two commands, nothing installed

The first proves the key works. It creates nothing, sends nothing and costs nothing, because the address it asks about is on no account:

curl -sS "https://heb.mesereser.com/Services/JsonServices.aspx?f=GetContactStatus&email=nobody.probe@example.invalid" \
  -H "ApiKey: $MESER10_API_KEY" \
  -H "User-Agent: my-app/1.0"

{"ErrorCode":0,"Result":"Call successful","StatusID":0}

The second sends the code. It reaches a real handset and spends credit:

curl -sS -X POST "https://heb.mesereser.com/Services/JsonServices.aspx?f=SendSingleSmsMessage" \
  -H "ApiKey: $MESER10_API_KEY" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "User-Agent: my-app/1.0" \
  -d '{"ToPhone":"0501234567","MessageBody":"Your code is 481902.","FromName":"MyShop"}'

{"ErrorCode":0,"Result":"","MessageID":0}

The same call in four languages

No library, no dependency. Note that every one of them sends a User-Agent, and that is not cosmetic.

// PHP
$ch = curl_init('https://heb.mesereser.com/Services/JsonServices.aspx?f=SendSingleSmsMessage');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'ApiKey: ' . getenv('MESER10_API_KEY'),
        'Content-Type: application/json; charset=utf-8',
        'User-Agent: my-app/1.0',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'ToPhone'     => $phone,
        'MessageBody' => "Your code is {$code}.",
        'FromName'    => 'MyShop',
    ], JSON_UNESCAPED_UNICODE),
]);
$body = json_decode(curl_exec($ch), true);
$ok   = (string) $body['ErrorCode'] === '0';
// Node 18+
const res = await fetch(
  'https://heb.mesereser.com/Services/JsonServices.aspx?f=SendSingleSmsMessage',
  {
    method: 'POST',
    headers: {
      ApiKey: process.env.MESER10_API_KEY,
      'Content-Type': 'application/json; charset=utf-8',
      'User-Agent': 'my-app/1.0',
    },
    body: JSON.stringify({
      ToPhone: phone,
      MessageBody: `Your code is ${code}.`,
      FromName: 'MyShop',
    }),
  },
);
const body = await res.json();
const ok = String(body.ErrorCode) === '0';
# Python
import os, requests

res = requests.post(
    "https://heb.mesereser.com/Services/JsonServices.aspx",
    params={"f": "SendSingleSmsMessage"},
    headers={
        "ApiKey": os.environ["MESER10_API_KEY"],
        # urllib's default signature is refused. requests passes, but set
        # something of your own anyway.
        "User-Agent": "my-app/1.0",
    },
    json={"ToPhone": phone, "MessageBody": f"Your code is {code}.", "FromName": "MyShop"},
    timeout=20,
)
ok = str(res.json().get("ErrorCode")) == "0"
// C# - one HttpClient for the lifetime of the application, not one per call
var payload = JsonSerializer.Serialize(new
{
    ToPhone     = phone,
    MessageBody = $"Your code is {code}.",
    FromName    = "MyShop",
});

using var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://heb.mesereser.com/Services/JsonServices.aspx?f=SendSingleSmsMessage")
{
    Content = new StringContent(payload, Encoding.UTF8, "application/json"),
};
request.Headers.Add("ApiKey", apiKey);
request.Headers.Add("User-Agent", "my-app/1.0");

using var response = await http.SendAsync(request);
var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());

The API is also published as an OpenAPI 3.1 description and a Postman collection, so you can generate a client instead of copying the code above.

Three things to handle before you build a login on this

Read ErrorCode, not the HTTP status. Every call answers 200, including a rejected key, and ErrorCode is serialised as a number on some functions and as a string on others, so normalise before comparing.

Stop after an authentication failure; do not retry. ErrorCode 1 is final. Repeated failures block the calling IP address for several hours, and the block is on the address rather than the key, so reissuing the key and trying again makes it worse. On shared hosting it takes down every other integration sending from that machine. The probe at the top of this page exists for exactly this moment.

Send a User-Agent header. The API hosts sit behind Cloudflare with Browser Integrity Check enabled, and it reads that header. Default HTTP library signatures are refused: Java/1.8.0_241 answers 403 and Python-urllib/3.x answers Cloudflare error 1010. Any custom string passes, and the value itself does not matter. The symptom is intermittent, because a system often has two code paths to the same endpoint and only one of them sets it.

The sender identity, which stops most first integrations

FromName has to be a sender identity already approved on the account. Until it is, the call answers ErrorCode 4, and that is by far the most common reason a perfectly correct call does not go through. Two forms are accepted:

FromName
  alphanumeric   up to 11 characters, A-Z a-z 0-9 and space,
                 at least one letter
  numeric        a full phone number, local or E.164

  MyShop           ok
  Meser10 Ltd      ok, exactly 11
  Meser10 Israel   rejected, 14 characters
  Hebrew text      rejected, not a valid sender name
  0501234567       ok

A name longer than 11 characters is not truncated, the call is simply rejected. The limit is a GSM constraint on alphanumeric sender IDs rather than ours, and support varies by destination country: alphanumeric sender IDs are not available in the United States or Canada, where a number is used instead. In Israel the identity is approved against the Ministry of Communications, and approval is granted the same day.

Hebrew doubles what a message costs

A single Hebrew character anywhere in the body moves the whole message to Unicode, and a single part then holds 70 characters instead of 160. Anything longer is sent as several parts and billed per part. Worth measuring before you settle the wording, especially when the code itself varies in length.

Both alphabets work in the same message, so a bilingual product can send the same template to everyone. Pricing is published in full on the pricing page, and SMS bundles are a one-off purchase with no monthly fee and no expiry on the balance.

A code by email, from the same gateway

The same key sends a single transactional email through SendSingleEMailMessage. Worth knowing what that function will not take before you route a message to it: no From address, only a display name; no CC and no BCC; no attachments; and one recipient per call. ReplyToEMail is required, whatever older documentation shows. The JSON API reference has the field list, and an API overview covers the SOAP interface and the IP allowlist.

Send your first code

Open a free account, no credit card, and the key is waiting in your settings.

Sign up freeJSON API referenceContact us

Frequently asked questions

What do I need to send the first code?

An account, an API key and an approved sender identity. The key is issued in the interface under account settings, advanced settings, API settings. There is no plugin to install and no server in the middle, and authentication is a single HTTP header.

Why does a correct call answer ErrorCode 4?

Almost always because the sender identity is not approved on the account yet, or is not valid for the mobile networks: more than 11 characters, Hebrew text, or digits only. The message field in the response names what was refused.

Is there a sandbox?

There is no separate sandbox. The closest thing is a GetContactStatus call for an address that is not on the account, which proves the key was read without creating anything and without sending anything. A real send always reaches a real recipient and spends credit.

When is it safe to retry a failed call?

It depends on the code. ErrorCode 3 is transient and one more attempt is reasonable. ErrorCode 1, an authentication failure, must never be retried: repeated failures block the calling IP address for several hours, and the block is on the address rather than the key. ErrorCode 4 will not change on a retry, because it means a parameter is wrong.

Can the same code go out by email as well as SMS?

Yes. The same key and the same gateway send a single transactional email through SendSingleEMailMessage, so one integration covers both channels. The email function takes a display name rather than a From address, one recipient, no CC or BCC and no attachments.