NewHope SMS API
A complete REST API to send SMS, OTP, and delivery reports in Tanzania. Works with every programming language — if it can make an HTTP request, it works with NewHope SMS.
⚡ Quick Start — Send Your First SMS in 5 Minutes
Follow these 3 steps. No SDK installation required — just HTTP.
Create an account and generate API keys
Sign up at sms.newhope.co.tz/accounts/register/ → go to API Keys → click Generate New Key → choose a sender ID → save both keys shown to you. The Secret Key (nhs_…) is shown only once.
Send your first SMS
Replace YOUR_API_KEY and YOUR_SECRET_KEY in the cURL command below, then run it in any terminal. You should receive an SMS on the phone number you specified.
curl -X POST https://sms.newhope.co.tz/v1/sms/send/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"recipient":"+255712345678","message":"Hello from NewHope SMS!","sender_id":"NewHope"}'
Check the response
A successful response looks like this. The id is your message ID — use it to check delivery status.
{
"id": "9f3a1c2e-4b5d-…",
"status": "sent",
"recipient": "+255712345678",
"sender_id_name": "NewHope",
"gateway_message_id": "ATXid_xxxxxxxxxxxxx",
"cost": "20.00",
"segments": 1
}
Any language. Any framework. cURL above is just a raw HTTP POST. In your code, use whatever HTTP client your language/framework provides — requests (Python), axios/fetch (JS), HttpClient (.NET), OkHttp (Java/Android), http package (Dart/Flutter), curl_exec (PHP). They all send the same HTTP request.
Introduction
The NewHope SMS API is a RESTful HTTP API targeting businesses, schools, hospitals, NGOs, churches, and developers in Tanzania.
| Property | Value |
|---|---|
| Base URL | https://sms.newhope.co.tz/v1 |
| Protocol | HTTPS only — plain HTTP is rejected |
| Format | JSON — set Content-Type: application/json on all POST/PUT requests |
| Authentication | Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY |
| Rate limit | 200 requests per minute per API key (429 if exceeded) |
| Phone format | E.164 international — +255XXXXXXXXX for Tanzania |
| Timestamps | UTC, ISO 8601 format — e.g. 2026-06-07T10:00:00Z |
| Pagination | Paginated lists return count, next, previous, results |
| Support | support@newhope.co.tz |
Authentication
Every request must include both your API Key and your Secret Key in the Authorization header, joined by a colon, using the ApiKey scheme.
Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY
nhk_…nhs_…Do not use Authorization: Bearer nhk_…. That format is JWT and will return 401 Token is invalid or expired. External API clients must use ApiKey key:secret.
API Key types
| Type | Access |
|---|---|
| General | Full access — all endpoints |
| SMS Sender | Send SMS using one specific sender ID only |
| OTP | Only /otp/send/ and /otp/verify/ |
Generate keys: sms.newhope.co.tz/api-keys
Code examples — Authentication setup
curl https://sms.newhope.co.tz/v1/sender-ids/ \
-H "Authorization: ApiKey nhk_YOUR_API_KEY:nhs_YOUR_SECRET_KEY"
🐍 Python (requests)
import requests API_KEY = "nhk_YOUR_API_KEY" # dashboard → API Keys SECRET_KEY = "nhs_YOUR_SECRET_KEY" # save once, never shown again BASE = "https://sms.newhope.co.tz/v1" session = requests.Session() session.headers.update({ "Authorization": f"ApiKey {API_KEY}:{SECRET_KEY}", "Content-Type": "application/json", }) r = session.get(f"{BASE}/sender-ids/") print(r.json())
🟨 JavaScript / Node.js (axios)
const axios = require('axios'); const API_KEY = 'nhk_YOUR_API_KEY'; // dashboard → API Keys const SECRET_KEY = 'nhs_YOUR_SECRET_KEY'; // save once, never shown again const api = axios.create({ baseURL: 'https://sms.newhope.co.tz/v1', headers: { 'Authorization': `ApiKey ${API_KEY}:${SECRET_KEY}`, 'Content-Type': 'application/json', }, }); const { data } = await api.get('/sender-ids/'); console.log(data);
🟦 JavaScript / Browser (fetch)
const API_KEY = 'nhk_YOUR_API_KEY'; const SECRET_KEY = 'nhs_YOUR_SECRET_KEY'; const headers = { 'Authorization': `ApiKey ${API_KEY}:${SECRET_KEY}`, 'Content-Type': 'application/json', }; const res = await fetch('https://sms.newhope.co.tz/v1/sender-ids/', { headers }); const data = await res.json(); console.log(data);
🐘 PHP (cURL / GuzzleHTTP)
<?php $apiKey = 'nhk_YOUR_API_KEY'; // dashboard → API Keys $secretKey = 'nhs_YOUR_SECRET_KEY'; // save once, never shown again $ch = curl_init('https://sms.newhope.co.tz/v1/sender-ids/'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . $apiKey . ':' . $secretKey, ], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch); print_r($data);
<?php
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://sms.newhope.co.tz/v1/',
'headers' => [
'Authorization' => 'ApiKey ' . $apiKey . ':' . $secretKey,
'Content-Type' => 'application/json',
],
]);
$res = $client->get('sender-ids/');
$data = json_decode($res->getBody(), true);
☕ Java (OkHttp / HttpClient)
import okhttp3.*; String apiKey = "nhk_YOUR_API_KEY"; String secretKey = "nhs_YOUR_SECRET_KEY"; OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://sms.newhope.co.tz/v1/sender-ids/") .addHeader("Authorization", "ApiKey " + apiKey + ":" + secretKey) .build(); try (Response response = client.newCall(request).execute()) { System.out.println(response.body().string()); }
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://sms.newhope.co.tz/v1/sender-ids/"))
.header("Authorization", "ApiKey " + apiKey + ":" + secretKey)
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
🔷 C# / .NET (HttpClient)
using System.Net.Http; using System.Net.Http.Json; var apiKey = "nhk_YOUR_API_KEY"; var secretKey = "nhs_YOUR_SECRET_KEY"; var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", $"ApiKey {apiKey}:{secretKey}"); var senderIds = await client.GetFromJsonAsync<List<object>>( "https://sms.newhope.co.tz/v1/sender-ids/"); Console.WriteLine(senderIds);
🎯 Dart / Flutter (http package)
import 'package:http/http.dart' as http; import 'dart:convert'; // Add to pubspec.yaml: http: ^1.2.0 const apiKey = 'nhk_YOUR_API_KEY'; const secretKey = 'nhs_YOUR_SECRET_KEY'; final headers = { 'Authorization': 'ApiKey $apiKey:$secretKey', 'Content-Type': 'application/json', }; final res = await http.get( Uri.parse('https://sms.newhope.co.tz/v1/sender-ids/'), headers: headers, ); final data = jsonDecode(res.body); print(data);
🦀 Go (net/http)
package main
import (
"fmt"; "io"; "net/http"
)
func main() {
apiKey := "nhk_YOUR_API_KEY"
secretKey := "nhs_YOUR_SECRET_KEY"
req, _ := http.NewRequest("GET",
"https://sms.newhope.co.tz/v1/sender-ids/", nil)
req.Header.Set("Authorization", "ApiKey "+apiKey+":"+secretKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
💎 Ruby (Net::HTTP / Faraday)
require 'net/http' require 'json' api_key = 'nhk_YOUR_API_KEY' secret_key = 'nhs_YOUR_SECRET_KEY' uri = URI('https://sms.newhope.co.tz/v1/sender-ids/') req = Net::HTTP::Get.new(uri) req['Authorization'] = "ApiKey #{api_key}:#{secret_key}" res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } data = JSON.parse(res.body) puts data
Send SMS
Send a single SMS to one recipient. Consumes 1 credit per segment.
Request body (JSON)
| Field | Type | Status | Description |
|---|---|---|---|
| recipient | string | Required | E.164 phone — +255712345678 |
| message | string | Required | SMS body. Max 918 chars (6 segments). GSM-7: 160 chars/segment. Unicode/Swahili: 70 chars/segment. |
| sender_id | string | Optional | Approved sender name (max 11 chars). Uses account default if omitted. |
| webhook_url | string URL | Optional | URL to receive delivery receipt POSTs. Must respond 200 within 10 seconds. |
| schedule_at | datetime | Optional | ISO 8601 future datetime — e.g. 2026-06-10T09:00:00Z |
Response — 201 Created
{
"id": "9f3a1c2e-4b5d-…",
"status": "sent",
"recipient": "+255712345678",
"sender_id_name": "NewHope",
"gateway_message_id": "ATXid_xxxxxxxxxxxxx",
"cost": "20.00",
"segments": 1
}
Code examples
curl -X POST https://sms.newhope.co.tz/v1/sms/send/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"recipient":"+255712345678","message":"Hello from NewHope SMS!","sender_id":"NewHope"}'
🐍 Python
r = session.post("/sms/send/", json={ "recipient": "+255712345678", "message": "Hello from NewHope SMS!", "sender_id": "NewHope", "webhook_url": "https://yourapp.com/dlr", }) print(r.json()) # {'id': '...', 'status': 'sent', 'cost': '20.00'}
🟨 Node.js
const { data } = await api.post('/sms/send/', {
recipient: '+255712345678',
message: 'Hello from NewHope SMS!',
sender_id: 'NewHope',
webhook_url: 'https://yourapp.com/dlr',
});
🐘 PHP
$ch = curl_init('https://sms.newhope.co.tz/v1/sms/send/'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Authorization: ApiKey '.$apiKey.':'.$secretKey, 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode(['recipient'=>'+255712345678','message'=>'Hello!','sender_id'=>'NewHope']), ]); print_r(json_decode(curl_exec($ch), true)); curl_close($ch);
☕ Java
String body = """{"recipient":"+255712345678","message":"Hello!","sender_id":"NewHope"}"""; RequestBody rb = RequestBody.create(body, MediaType.get("application/json")); Request req = new Request.Builder() .url("https://sms.newhope.co.tz/v1/sms/send/") .addHeader("Authorization", "ApiKey " + apiKey + ":" + secretKey) .post(rb).build(); try (Response r = client.newCall(req).execute()) { System.out.println(r.body().string()); }
🔷 C#
var payload = new { recipient = "+255712345678", message = "Hello!", sender_id = "NewHope" };
var res = await client.PostAsJsonAsync("https://sms.newhope.co.tz/v1/sms/send/", payload);
var data = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(data);
🎯 Dart / Flutter
final res = await http.post( Uri.parse('https://sms.newhope.co.tz/v1/sms/send/'), headers: headers, body: jsonEncode({'recipient': '+255712345678', 'message': 'Hello!', 'sender_id': 'NewHope'}), ); print(jsonDecode(res.body));
Bulk SMS
Send the same message to an entire contact list in one request.
| Field | Type | Status | Description |
|---|---|---|---|
| contact_list_id | uuid | Required | UUID of the contact list. Get it from GET /contacts/ |
| message | string | Required | SMS message body. |
| sender_id | string | Optional | Approved sender name. Uses default if omitted. |
| schedule_at | datetime | Optional | ISO 8601 future send time. |
Response — 200 OK
{ "queued_count": 250, "status": "queued" }
Credits consumed = contacts × message segments. Ensure sufficient balance before sending. Returns 402 if insufficient.
curl -X POST https://sms.newhope.co.tz/v1/sms/send-batch/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"contact_list_id":"YOUR_LIST_UUID","message":"Hello customers!","sender_id":"NewHope"}'
SMS Status
Check the current delivery status of a specific message by its ID.
Response — 200 OK
{
"id": "9f3a1c2e-…",
"status": "delivered",
"sent_at": "2026-06-07T10:00:00Z",
"delivered_at": "2026-06-07T10:00:05Z"
}
curl https://sms.newhope.co.tz/v1/sms/9f3a1c2e-4b5d-.../status/ \
-H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY"
If you prefer real-time delivery updates instead of polling, use the webhook_url field in your send request. NewHope SMS will POST to your URL the moment status changes.
SMS Reports
Retrieve a paginated list of your sent messages and delivery statuses.
| Query Param | Type | Status | Description |
|---|---|---|---|
| status | string | Optional | pending · sent · delivered · failed |
| page | integer | Optional | Page number. Default: 1. |
| page_size | integer | Optional | Results per page. Max 100. Default: 20. |
curl "https://sms.newhope.co.tz/v1/sms/reports/?status=delivered&page=1" \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY"
Contact Lists
Manage contact lists used for bulk SMS campaigns. A contact list is a named group of phone numbers.
List all contact lists
[
{
"id": "a1b2c3d4-…",
"name": "Customers Q1 2026",
"description": "VIP customers who purchased in Q1",
"contacts_count": 1240,
"created_at": "2026-01-01T09:00:00Z"
}
]
curl https://sms.newhope.co.tz/v1/contacts/ \
-H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY"
Create a contact list
| Field | Type | Status | Description |
|---|---|---|---|
| name | string | Required | Name of the contact list. |
| description | string | Optional | Optional description. |
curl -X POST https://sms.newhope.co.tz/v1/contacts/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"My Customers","description":"Q1 2026 customers"}'
Delete a contact list
Returns 204 No Content on success.
List contacts in a list
| Query Param | Type | Description |
|---|---|---|
| page | integer | Page number. Default: 1. |
| page_size | integer | Results per page. |
| q | string | Search by phone, first name, or last name. |
{
"count": 1240, "next": "…?page=2", "previous": null,
"results": [
{ "id": "…", "phone_number": "+255712345678", "first_name": "Ali", "last_name": "Hassan", "created_at": "…" }
]
}
Add a single contact
| Field | Type | Status | Description |
|---|---|---|---|
| phone_number | string | Required | E.164 phone number — +255712345678 |
| first_name | string | Optional | Contact first name. |
| last_name | string | Optional | Contact last name. |
curl -X POST https://sms.newhope.co.tz/v1/contacts/LIST_UUID/contacts/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"phone_number":"+255712345678","first_name":"Ali","last_name":"Hassan"}'
Remove a contact
Returns 204 No Content on success.
Bulk import contacts from CSV
Upload a CSV file to import many contacts at once. Use multipart/form-data, not JSON.
| Field | Type | Description |
|---|---|---|
| file | file (CSV) | CSV with column headers: phone_number, first_name (optional), last_name (optional) |
phone_number,first_name,last_name +255712345678,Ali,Hassan +255754321098,Fatuma,Mwangi +255765432109,John,Doe
curl -X POST https://sms.newhope.co.tz/v1/contacts/LIST_UUID/import/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -F "file=@contacts.csv"
{
"imported": 1200,
"skipped": 15,
"errors": ["Row 5: invalid phone number"],
"total_in_list": 2440
}
🐍 Python — CSV import
with open("contacts.csv", "rb") as f: r = requests.post( f"{BASE}/contacts/LIST_UUID/import/", files={"file": ("contacts.csv", f, "text/csv")}, headers={"Authorization": f"ApiKey {API_KEY}:{SECRET_KEY}"}, ) print(r.json()) # {'imported': 1200, 'skipped': 15, ...}
🟨 Node.js — CSV import
const FormData = require('form-data'); const fs = require('fs'); const form = new FormData(); form.append('file', fs.createReadStream('contacts.csv')); const { data } = await axios.post('/contacts/LIST_UUID/import/', form, { headers: { ...form.getHeaders() }, }); console.log(data);
🎯 Dart / Flutter — CSV import
var request = http.MultipartRequest( 'POST', Uri.parse('https://sms.newhope.co.tz/v1/contacts/LIST_UUID/import/'), ); request.headers['Authorization'] = 'ApiKey $apiKey:$secretKey'; request.files.add(await http.MultipartFile.fromPath('file', 'contacts.csv')); var response = await request.send(); print(await response.stream.bytesToString());
Sender IDs
List sender IDs on your account. Only approved sender IDs can be used. The default "NewHope" sender ID is pre-approved on every account.
[
{
"id": "a1b2c3d4-…",
"sender_name": "NewHope",
"status": "approved",
"is_default": true,
"approved_at": "2026-01-10T08:00:00Z",
"rejection_reason": null
}
]
| Status | Meaning |
|---|---|
| awaiting_payment | Application fee (TZS 5,000) not yet paid |
| pending | Fee paid — compliance review in 1–2 business days |
| under_review | Under review by the NewHope compliance team |
| approved | Active — can be used for sending |
| rejected | Denied — reason in rejection_reason field |
To apply for a custom sender ID: log in → Sender IDs → Request New. Upload Brela / TIN / Business Licence (PDF), pay TZS 5,000 via mobile money. Sender ID names: max 11 characters, letters and numbers only, no spaces.
OTP — Send
Send a 6-digit one-time password via SMS. Expires after 5 minutes (300 seconds). Requires OTP or General API key.
| Field | Type | Status | Description |
|---|---|---|---|
| phone | string | Required | Recipient phone in E.164 format |
| sender_id | string | Optional | Sender name. Uses account default if omitted. |
{ "request_id": "uuid-…", "expires_in": 300 }
curl -X POST https://sms.newhope.co.tz/v1/otp/send/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"phone":"+255712345678","sender_id":"NewHope"}'
OTP — Verify
Verify the 6-digit code submitted by the user against the request_id from /otp/send/.
| Field | Type | Status | Description |
|---|---|---|---|
| request_id | uuid | Required | The request_id returned by /otp/send/ |
| otp | string | Required | 6-digit code entered by the user |
{ "valid": true } // or { "valid": false } if wrong or expired
curl -X POST https://sms.newhope.co.tz/v1/otp/verify/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"request_id":"uuid-…","otp":"847291"}'
🐍 Python — Full OTP flow
# Step 1 — send OTP send = session.post(f"{BASE}/otp/send/", json={"phone": "+255712345678", "sender_id": "NewHope"}) request_id = send.json()["request_id"] print(f"OTP sent — expires in {send.json()['expires_in']}s") # Step 2 — user reads code; then verify verify = session.post(f"{BASE}/otp/verify/", json={"request_id": request_id, "otp": user_input}) print("Valid:", verify.json()["valid"])
🎯 Dart / Flutter — Full OTP flow
// Step 1 — send OTP final sendRes = await http.post(Uri.parse('$BASE/otp/send/'), headers: headers, body: jsonEncode({'phone': '+255712345678', 'sender_id': 'NewHope'})); final requestId = jsonDecode(sendRes.body)['request_id']; // Step 2 — verify OTP final verifyRes = await http.post(Uri.parse('$BASE/otp/verify/'), headers: headers, body: jsonEncode({'request_id': requestId, 'otp': userInput})); final valid = jsonDecode(verifyRes.body)['valid']; print('OTP valid: $valid');
Webhooks / Delivery Reports (DLR)
Set webhook_url in your send request. NewHope SMS will POST to that URL every time delivery status changes.
Your endpoint must respond with HTTP 200 within 10 seconds. Failed deliveries are retried up to 3 times with exponential back-off.
Payload — POST to your URL
{
"message_id": "9f3a1c2e-…",
"recipient": "+255712345678",
"status": "delivered",
"delivered_at": "2026-06-07T10:00:05Z",
"gateway": "beem"
}
🐍 Python — Flask webhook handler
from flask import Flask, request app = Flask(__name__) @app.route('/dlr', methods=['POST']) def dlr(): d = request.json print(f"[{d['status'].upper()}] {d['recipient']} — {d['message_id']}") if d['status'] == 'delivered': pass # update DB, send follow-up, etc. return 'OK', 200 # MUST return 200
🟨 Node.js — Express webhook handler
app.post('/dlr', (req, res) => { const { message_id, recipient, status } = req.body; console.log(`[${status.toUpperCase()}] ${recipient} — ${message_id}`); if (status === 'delivered') { /* update DB */ } res.sendStatus(200); // MUST return 200 });
🐘 PHP webhook handler
$d = json_decode(file_get_contents('php://input'), true); error_log("DLR [{$d['status']}] {$d['recipient']} — {$d['message_id']}"); if ($d['status'] === 'delivered') { /* update DB */ } http_response_code(200); echo 'OK';
☕ Java — Spring Boot webhook
@PostMapping("/dlr") public ResponseEntity<String> dlr(@RequestBody Map<String, Object> payload) { String status = (String) payload.get("status"); log.info("DLR [{}] {} — {}", status, payload.get("recipient"), payload.get("message_id")); if ("delivered".equals(status)) { /* update DB */ } return ResponseEntity.ok("OK"); // MUST return 200 }
🔷 C# — ASP.NET Core webhook
[HttpPost("/dlr")] public IActionResult Dlr([FromBody] JsonElement payload) { var status = payload.GetProperty("status").GetString(); var recipient = payload.GetProperty("recipient").GetString(); _logger.LogInformation("DLR [{status}] {recipient}", status, recipient); if (status == "delivered") { /* update DB */ } return Ok("OK"); // MUST return 200 }
Error Handling Best Practices
Build resilient integrations using these patterns.
Error response format
{ "error": "Insufficient SMS credits." }
{ "detail": "Authentication credentials were not provided." }
Retry with exponential back-off (for 429 / 5xx)
🐍 Python — retry pattern
import time, requests
def send_sms_with_retry(session, payload, max_retries=3):
for attempt in range(max_retries):
r = session.post(f"{BASE}/sms/send/", json=payload)
if r.status_code == 201:
return r.json()
if r.status_code == 429 or r.status_code >= 500:
wait = 2 ** attempt # 1s, 2s, 4s
time.sleep(wait)
continue
r.raise_for_status() # 4xx errors — don't retry
raise Exception("Max retries exceeded")
🟨 Node.js — retry pattern
async function sendWithRetry(payload, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const { data } = await api.post('/sms/send/', payload);
return data;
} catch (err) {
const status = err.response?.status;
if (status === 429 || status >= 500) {
await new Promise(r => setTimeout(r, 1000 * 2 ** i));
continue;
}
throw err; // 4xx — don't retry
}
}
throw new Error('Max retries exceeded');
}
Rate limit headers
When approaching the rate limit (200 req/min), the API returns 429 Too Many Requests. Back off and retry after a short delay. Do not retry immediately.
Balance checks (avoid 402)
curl https://sms.newhope.co.tz/v1/profile/balance/ \ -H "Authorization: ApiKey YOUR_API_KEY:YOUR_SECRET_KEY" # Returns: {"sms_balance": 5000, "currency": "TZS"}
Error Code Reference
Authorization: ApiKey nhk_…:nhs_…. Do not use Bearer.Pricing (TZS per SMS credit)
Credits are pre-purchased and never expire. The more you buy, the lower the per-SMS cost.
| SMS Range | Price per SMS (TZS) |
|---|---|
| 1 – 10,000 | TZS 20.00 |
| 10,001 – 25,000 | TZS 18.00 |
| 25,001 – 50,000 | TZS 17.00 |
| 50,001 – 100,000 | TZS 16.00 |
| 100,001 – 250,000 | TZS 14.00 |
| 250,001 – 500,000 | TZS 13.00 |
| 500,001 – 1,000,000 | TZS 12.00 |
| 1,000,001 – 2,000,000 | TZS 10.70 |
Payment methods: M-Pesa (Vodacom), Airtel Money, Tigo Pesa, TTCL Pesa, Halotel, Bank Transfer — added instantly after mobile money confirmation.
Buy credits: sms.newhope.co.tz/purchases
Tools & Libraries
Import the OpenAPI spec into any API client to get auto-generated documentation, request builders, and code snippets in your language.
Recommended HTTP libraries by language
| Language / Platform | Recommended library | Install |
|---|---|---|
| Python | requests | pip install requests |
| Node.js | axios or native fetch | npm install axios |
| Browser JS | Native fetch (built-in) | — |
| PHP | cURL (built-in) or GuzzleHTTP | composer require guzzlehttp/guzzle |
| Java | OkHttp or Java 11 HttpClient | implementation 'com.squareup.okhttp3:okhttp:4.12.0' |
| Android | OkHttp or Retrofit | implementation 'com.squareup.retrofit2:retrofit:2.11.0' |
| C# / .NET | System.Net.Http.HttpClient (built-in) | — |
| Dart / Flutter | http package | flutter pub add http |
| Go | net/http (built-in) | — |
| Ruby | Net::HTTP (built-in) or faraday | gem install faraday |
| Kotlin | OkHttp or Ktor | implementation 'io.ktor:ktor-client-core:2.3.12' |
| Swift / iOS | URLSession (built-in) or Alamofire | pod 'Alamofire' |
Need help integrating?
Our team assists with API integration questions, Sender ID approvals, and billing issues.