An API that has been in production for years
Two interfaces, one account. A JSON API for the common operations, and a SOAP API that exposes the full campaign surface. Both authenticate with an API key from your account settings, and both can be restricted to specific IP addresses.
What you can do
Contacts
Create a contact, add it to a list, change its status. This is what most WordPress and CRM integrations call.
Transactional email
Send a single message to a single recipient, immediately, outside any campaign. Order confirmations, password resets, receipts.
Transactional SMS and OTP
Send a single SMS in real time. The usual case is a verification code your system generated and needs delivered in seconds. See the OTP guide for working code in four languages.
Campaigns
Create and send a campaign programmatically through the SOAP interface, for systems that generate their own content.
JSON reference
Endpoint, function list, request and response shapes for the five JSON functions.
Security
API key in a header, plus an optional IP allowlist so the key only works from your servers.
Error codes worth knowing
0 success 1 the API key is wrong, missing or revoked 3 a payload the function could not process, or an error on our side 4 a parameter is missing or invalid, often a list that does not exist 6 unknown function name
Codes 3 and 4 are the ones that confuse people: the key is fine, the request is not. Check that every required field is present, including the ones older documentation shows as empty strings. Code 6 has a use of its own, since a key that reaches function lookup is a key that was accepted, which makes an unknown function name a connection test with no side effects.
Send your first message
Two requests, no SDK. The first one proves the key works without creating anything or spending anything, 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 a one-time password. 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}Read ErrorCode, not the status code. Every call answers HTTP 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. And treat code 1 as fatal: repeated authentication 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 retrying makes it worse. Use the probe above instead of retrying.
FromName has to be a sender identity already approved on the account: either a number, or an alphanumeric name of at most 11 characters, Latin letters, digits and spaces only, containing at least one letter. That is a GSM constraint on alphanumeric sender IDs rather than ours, and it varies by destination country, since alphanumeric sender IDs are not available in the United States or Canada.
Set a User-Agent header
Worth knowing before you debug the wrong thing. The API hosts sit behind Cloudflare with Browser Integrity Check enabled, and that check reads the User-Agent header. Default HTTP library signatures are refused:
Java/1.8.0_241 403 Python-urllib/3.x Cloudflare error 1010 curl, requests, a browser string, any custom value, no header at all pass
The value itself does not matter, only that it is not a default library signature. The symptom is intermittent, because a system often has two code paths to the same endpoint and only one of them sets the header, which is the usual reason an integration works in one place and fails in another.
Machine-readable contract
The JSON API is published as a specification as well as prose, so you can generate a client, import it into your tooling, or hand it to an AI agent that will write the integration:
- OpenAPI 3.1, JSON. The URL to give automated tooling.
- The same description in YAML, easier to read.
- Postman collection, six requests with tests that normalise
ErrorCodefor you.
In the Postman collection, run Verify the key first: it creates nothing and sends nothing. The others act on the live account, two of them deliver a real SMS and a real email, and one unsubscribes an address, so run them one at a time rather than through the Collection Runner. The full field-by-field reference is in the JSON API reference.
Get an API key
Open a free account, then find the key under Advanced settings. If you need the IP allowlist configured, tell us the addresses.
Sign up freeContact usFrequently asked questions
Where do I get an API key?
In the account, under Advanced settings, in the API settings card. The same screen holds the IP allowlist.
JSON or SOAP?
JSON for contacts, single email and single SMS, which covers most integrations. SOAP when you need the full campaign surface.
Is there a rate limit?
Nothing published for normal use. If you are planning high-volume transactional sending, tell us the expected rate so we can size the account.
Can I restrict the key to my server?
Yes. The IP allowlist means a leaked key is useless from anywhere else.