Send SMS API: A Step-by-Step Quickstart Guide
Updated: Sep 21
Quick Answer
Sending an SMS via API means making a single authenticated HTTP request from your own application no dashboard, no manual composition. With TechTo's real API, that's a token-authenticated GET request that returns a numeric message ID on success. This guide walks through that whole path: what you need before you start, your first working call in five languages, how to check it actually delivered, what to do when something goes wrong, and what to check before you send real production traffic. If you've read other "send SMS API" guides that describe JSON request bodies, webhooks, or an SDK to install, that's not what you'll find here this guide is built strictly around the real, documented API, not an idealized version of one.
Table of Contents
What Sending SMS via API Actually Involves
Before You Start: What You Need
Your First API Call, Step by Step
Code in 5 Languages
Understanding the Response
Checking Delivery Status
Sending to Multiple Recipients
Handling Errors and Retries
DLT Compliance for API-Triggered Sends
Choosing Between the HTTP, Tally, and XML Methods
A Realistic Integration Timeline
Security Best Practices
Testing Before You Go Live
Monitoring Your Integration in Production
Sending OTPs via API
Common Mistakes When Integrating an SMS API
Which Guide Should You Read Next?
Where TechTo Networks Fits
Frequently Asked Questions
1. What Sending SMS via API Actually Involves
Sending an SMS through a dashboard means a person logs in, composes a message, and clicks send. Sending an SMS via API means your own application does that same job programmatically — the moment an order is placed, a user requests a login code, or a scheduled job fires, your code makes an HTTP request and the message goes out without anyone touching a dashboard.
This is the right approach whenever a message needs to fire in response to something happening in your system: an OTP the instant someone tries to log in, an order confirmation the instant a payment clears, an alert the instant a threshold is crossed. If you're running manually composed marketing campaigns instead, a dashboard is often the better tool — see our guide to sending bulk text messages for that side of things, and sending bulk SMS online for the dashboard-specific walkthrough.
A quick gut check for which path fits your situation: if the message content and timing are decided by a human sitting down to plan a campaign, use the dashboard. If the message content and timing are decided by something your system already knows the instant it happens — an order status, a login attempt, a threshold being crossed — use the API. Most businesses that use SMS seriously end up needing both, run by different teams for different purposes, rather than picking one exclusively.
2. Before You Start: What You Need
An account and API token. Your token authenticates every request — treat it like a password, not a public identifier. If you're evaluating multiple providers, note that credential formats differ (some use a single token, others a username/password pair), which affects how you'll structure this step for each.
A registered Sender ID. The alphanumeric name (e.g., TECHTO) recipients see in place of a phone number, registered on the DLT platform. A sender ID registered for one category (promotional) generally can't be reused for another (transactional) — confirm which headers you have available before assuming one covers your use case.
At least one approved message template, with the exact wording you plan to send and any variable fields correctly tagged (see Section 9). Template approval isn't instant, so this is worth starting before you sit down to write integration code, not something to request the day you plan to go live.
A basic understanding of the route and message-type parameters you'll pass with every send — covered in Section 4 below and in full in Message APIs.
A test number you control. Having your own phone available to receive test sends throughout development saves significant back-and-forth compared to relying only on logs and API responses to judge whether something worked.
If you don't have these yet, get your entity, header, and first template registered before writing any integration code — building against an unapproved template will fail at the carrier level regardless of how correct your API call is. Trying to shortcut this step by testing against a placeholder template is a common source of confusion early on, since the failure looks identical to a bug in your code when it's actually a compliance gate doing exactly what it's supposed to do.
3. Your First API Call, Step by Step
Confirm your token and sender ID are active — a quick manual test send through the dashboard (if available) is a fast way to rule out account-level issues before you start debugging code.
Build the request URL with your token, sender ID, destination number, route, message type, and content — the exact structure is shown in Section 4.
URL-encode the message content. Any space, punctuation, or non-Latin character needs proper encoding, or the request can fail or truncate — every code sample below handles this for you.
Send the request and read the response as plain text, not JSON — a successful call returns a bare numeric message ID.
Store that message ID. It's your only reference for checking delivery status afterward (Section 6) — there's no way to look it up later by recipient number alone.
Send to your own number first. Confirm the message arrives, the sender ID displays correctly, and the content matches your approved template exactly before pointing the integration at real customer traffic.
4. Code in 5 Languages
The endpoint below is TechTo's real, documented HTTP API (token-based). For the Tally (username/password) and XML variants, plus the full parameter reference, see Message APIs.
Endpoint:
Route codes: Promotional (1) · Transactional (2) · Sender ID (3) · International (9) · Trans2 (10) Message type codes: Text (1) · Flash (2) · Unicode (3)
cURL
curl -G "https://godspeed.liveair.co.in/httpapi/tokan/" \
--data-urlencode "token=YOUR_API_TOKEN" \
--data-urlencode "sender=TECHTO" \
--data-urlencode "number=9198XXXXXXX0" \
--data-urlencode "route=2" \
--data-urlencode "type=1" \
--data-urlencode "sms=Your order TN10234 has shipped."
Python
import requests
API_TOKEN = "YOUR_API_TOKEN"
BASE_URL = "https://godspeed.liveair.co.in/httpapi/tokan/"
def send_sms(sender: str, number: str, message: str, route: str = "2", msg_type: str = "1") -> str:
params = {
"token": API_TOKEN,
"sender": sender,
"number": number,
"route": route,
"type": msg_type,
"sms": message # requests handles URL-encoding automatically
}
response = requests.get(BASE_URL, params=params, timeout=10)
return response.text.strip() # numeric message ID, or a 101–111 error code
message_id = send_sms("TECHTO", "9198XXXXXXX0", "Your order TN10234 has shipped.")
print(message_id)
Node.js
const axios = require("axios");
const API_TOKEN = "YOUR_API_TOKEN";
const BASE_URL = "https://godspeed.liveair.co.in/httpapi/tokan/";
async function sendSms(sender, number, message, route = "2", type = "1") {
const response = await axios.get(BASE_URL, {
params: { token: API_TOKEN, sender, number, route, type, sms: message },
timeout: 10000
});
return String(response.data).trim();
}
sendSms("TECHTO", "9198XXXXXXX0", "Your order TN10234 has shipped.")
.then(id => console.log(id));
PHP
<?php
function sendSms(string $sender, string $number, string $message, string $route = "2", string $type = "1"): string {
$params = [
"token" => "YOUR_API_TOKEN",
"sender" => $sender,
"number" => $number,
"route" => $route,
"type" => $type,
"sms" => $message
];
$url = "https://godspeed.liveair.co.in/httpapi/tokan/?" . http_build_query($params);
return trim(file_get_contents($url));
}
$messageId = sendSms("TECHTO", "9198XXXXXXX0", "Your order TN10234 has shipped.");
echo $messageId;
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class SendSms {
private static final String API_TOKEN = "YOUR_API_TOKEN";
private static final String BASE_URL = "https://godspeed.liveair.co.in/httpapi/tokan/";
public static String send(String sender, String number, String message, String route, String type) throws Exception {
String url = String.format("%s?token=%s&sender=%s&number=%s&route=%s&type=%s&sms=%s",
BASE_URL, enc(API_TOKEN), enc(sender), enc(number), enc(route), enc(type), enc(message));
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
return response.body().trim();
}
private static String enc(String value) throws Exception {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}
public static void main(String[] args) throws Exception {
System.out.println(send("TECHTO", "9198XXXXXXX0", "Your order TN10234 has shipped.", "2", "1"));
}
}
5. Understanding the Response
A successful call returns a bare numeric message ID as plain text — not a JSON object. A response matching one of the reserved error codes (101–111) means the send failed; anything else is a valid message ID to store. This is a genuinely easy thing to get wrong when integrating: don't assume every response is JSON, and don't assume every numeric-looking response is automatically a success without checking it against the reserved error range.
ERROR_CODES = set(range(101, 112)) # 101 through 111
def handle_response(response_text: str) -> dict:
stripped = response_text.strip()
if stripped.isdigit() and int(stripped) in ERROR_CODES:
return {"success": False, "error_code": int(stripped)}
return {"success": True, "message_id": stripped}
See Message APIs — Error Code Reference for the complete list and what each code means.
6. Checking Delivery Status
A successful API response confirms the platform accepted your request — it doesn't confirm the message reached the handset. Check actual delivery by polling with the message ID:
def check_delivery(message_id: str) -> dict:
response = requests.get(BASE_URL, params={"token": API_TOKEN, "messageid": message_id}, timeout=10)
return response.json()
Allow a short delay (a few seconds) after sending before your first check — the message needs time to reach the carrier and generate a status. This is a poll-based lookup, not a pushed notification; if you need your application to react to delivery status automatically, build that as a short polling loop with a defined timeout rather than waiting for a callback that this API doesn't send. A reasonable pattern is to check once a few seconds after sending, then again after a longer interval if the first check comes back pending, rather than polling continuously in a tight loop — the status won't update faster because you're asking more often.
7. Sending to Multiple Recipients
The number parameter accepts a comma-separated list for sending the same message to several recipients in one request:
send_sms("TECHTO", "9198XXXXXXX0,9198XXXXXXX1,9198XXXXXXX2", "Flash sale ends tonight!")
For per-recipient personalization (a different name or order ID for each recipient), send one request per recipient with the personalized content substituted in, rather than trying to combine personalization with the comma-separated batch format:
recipients = [
{"number": "9198XXXXXXX0", "name": "Anita", "order_id": "TN10234"},
{"number": "9198XXXXXXX1", "name": "Rahul", "order_id": "TN10235"},
]
for recipient in recipients:
message = f"Hi {recipient['name']}, your order {recipient['order_id']} has shipped."
send_sms("TECHTO", recipient["number"], message)
On throughput: every account has a practical sending rate it can sustain reliably, and this varies by plan and infrastructure rather than being a fixed number we can state generally. If you're planning a large send (tens of thousands of messages in a short window), confirm your account's actual throughput capacity with your account team beforehand rather than assuming a specific rate — pacing your requests to match confirmed capacity avoids the send queuing or throttling that can otherwise happen under unplanned load. A simple rate limiter in your sending loop (a short sleep between requests, or a proper queue with worker concurrency limits for larger volumes) is a reasonable default until you've confirmed your actual ceiling.
8. Handling Errors and Retries
Code | Meaning | Retry? |
101 | Invalid user | No — fix credentials first |
102 | Invalid sender ID | No — fix the sender parameter |
103 | Invalid contact(s) | No — fix number formatting |
104 | Invalid SMS route | No — fix the route parameter |
105 | Invalid message type | No — fix the type parameter |
106 | Message content not exist | No — usually an encoding issue |
107 | Spam blocked | No — review message content |
108 | Low credits in the specified route | No — top up before retrying |
109 | No SMSC | Yes — transient, retry with backoff |
110 | Promotional route works 9 AM–9 PM only | No — reschedule within the window |
111 | Connection error | Yes — transient, retry with backoff |
Only 109 and 111 are genuinely worth an automatic retry — the rest reflect a request-construction or account issue that a retry won't fix. Build your integration to distinguish between these two categories rather than retrying every non-success response identically, since retrying a malformed request repeatedly just wastes cycles without changing the outcome.
import time
RETRYABLE_CODES = {109, 111}
def send_with_retry(sender, number, message, route="2", max_retries=3):
for attempt in range(max_retries):
result = handle_response(send_sms(sender, number, message, route))
if result["success"]:
return result
if result["error_code"] not in RETRYABLE_CODES:
return result # don't retry a non-retryable failure
time.sleep(2 ** attempt) # exponential backoff for transient errors
return result
9. DLT Compliance for API-Triggered Sends
Every parameter in your API call maps to India's DLT/TCCCPR framework: your sender must be a DLT-registered header, your route determines which DND rules apply, and your message content must match an approved template exactly — a mismatch fails at the carrier level even when your API call itself is well-formed. This is worth internalizing early in your integration work rather than treating it as a separate compliance task handled by someone else on your team: the code you write and the template your compliance process registers have to stay in lockstep, and a change to one without the other is one of the most common causes of sudden, unexplained delivery failures in an otherwise-working integration.
This is a summary; for the complete regulatory picture — PE-TM chaining, header registration, and the 2024/2025 TRAI amendments — see Bulk SMS India: DLT Compliance, Pricing, and APIs.
10. Choosing Between the HTTP, Tally, and XML Methods
This guide uses the token-based HTTP API throughout, since it's the simplest starting point for a new integration. Two alternatives exist:
Method | Auth | Format | Best For |
HTTP API | Token | GET, query string | New integrations — the path this guide uses |
Tally HTTP API | Username + password | GET, query string | Migrating a system already storing credentials that way |
XML API | Token | POST, XML body | Passing a DLT Template ID directly in a structured request |
Tally HTTP API — functionally identical, authenticated with a username and password instead of a token. Only worth using if you're migrating a system that already stores credentials that way.
XML API — a POST-based method that lets you pass a DLT Template ID directly in a structured request body, useful if your application manages multiple templates per sender ID.
Full documentation for both, including complete parameter tables and additional code samples, is in Message APIs: The Complete SMS HTTP, XML, and Delivery Report Reference.
11. A Realistic Integration Timeline
Setting expectations up front avoids the common mistake of planning a go-live date before compliance and testing steps are accounted for:
DLT entity and header registration — typically a few business days with account support, longer if self-managed and something is formatted incorrectly on first submission.
Template approval — commonly within 24–48 hours per template once your entity and header are active, assuming the content and variable tagging are correctly structured the first time.
Basic integration coding — often the fastest part of the whole process for a simple send-only integration; a working test send can realistically happen within an hour or two once credentials and an approved template are in hand.
Testing and hardening (Section 12) — budget real time here rather than treating it as a formality; this is where template mismatches, encoding issues, and error-handling gaps actually surface.
Production rollout — consider a staged rollout (a small percentage of real traffic first) rather than cutting over 100% of sends to a new integration at once, so any issue affects a limited number of customers while you confirm everything behaves as expected under real load.
A realistic total timeline from a standing start (no existing DLT registration) to a tested, production-ready integration is commonly one to two weeks — most of that is compliance approval time, not coding time.
12. Security Best Practices
Call this API only from server-side code, never from a browser or mobile app. Your token travels as a URL parameter, so any client-side call exposes it directly in network traffic visible to anyone inspecting the device.
Use HTTPS, and confirm with your account team that it's available for your specific endpoint — sending a token over plain HTTP exposes it in transit.
Avoid logging full request URLs in your application or infrastructure logs, since the token travels in the query string and would end up in those logs by default unless explicitly redacted.
Rotate your token periodically, and immediately if you suspect it's been exposed — checked into a public repository, logged somewhere accessible, or shared insecurely.
Validate and sanitize any user-supplied content before inserting it into a message body, particularly if your application lets end users influence message content indirectly (a support ticket reply, a user-generated field).
Set a request timeout on every API call in your integration code. Without one, a slow or hung connection can block your application's own request-handling thread — the code samples in Section 4 all include a timeout for this reason.
Restrict which parts of your application can trigger a send. An SMS-sending function that any internal service can call without a permission check is a soft spot an unrelated bug elsewhere in your system could exploit to generate unexpected sends and cost.
13. Testing Before You Go Live
Before pointing your integration at real customer traffic, a short structured test catches most issues that would otherwise surface as a production incident:
Send to your own number across the message types you'll actually use — a plain text send and a Unicode send behave differently, and it's worth confirming both render correctly rather than assuming Unicode support "just works" because plain text did.
Deliberately send a malformed request — a missing parameter, an invalid route number — and confirm your application correctly recognizes the resulting error code rather than treating it as a success because a response came back.
Test with a number your DLT setup would actually restrict, if you can arrange one (a DND-registered number for a promotional send, for example), to confirm your route classification behaves as expected rather than only discovering it in production.
Time the full round trip from your API call to the message appearing on the test handset, so you have a real baseline if delivery speed later seems to degrade and you need something to compare against.
Confirm your delivery-status polling logic actually reflects reality by checking a message you know delivered against one you know failed (an invalid number is an easy way to generate a controlled failure) and verifying your application reports each correctly.
14. Monitoring Your Integration in Production
Once live, a few ongoing checks keep a working integration from silently degrading:
Track your delivery rate as a trend, not a one-off number. A sudden drop is worth investigating immediately; a single low-delivery batch might just reflect that batch's specific number mix.
Monitor your credit balance proactively, not reactively. Running out mid-campaign produces error 108 for every subsequent send until topped up — a scheduled balance check avoids finding out from a support ticket instead.
Log message IDs alongside the business event that triggered them (the order ID, the login attempt) so you can trace a specific customer's report of "I didn't get my code" back to exactly what your system did, rather than searching logs blind.
Alert on unusual error-code patterns, not just outright failures. A sudden spike in 107 (spam blocked) responses, for example, might indicate a content change accidentally tripped a filter, which is worth catching quickly rather than after a full campaign has already gone out with the same issue.
Periodically re-confirm your template content still matches what's registered, especially after any copy changes to your application — a template edited in code without a corresponding DLT re-approval is a common, easy-to-miss source of sudden delivery failures.
15. Sending OTPs via API
OTP delivery is one of the most common reasons to integrate an SMS API, and it comes with its own considerations beyond a standard send — secure code generation, safe storage, expiry enforcement, and rate limiting to prevent abuse. The send mechanism itself is identical to everything covered in this guide; what changes is what happens on either side of that send call. Your application needs to generate an unpredictable code (not with a standard pseudorandom function — a cryptographically secure generator matters here), store it safely (hashed, never plaintext, with an expiry), and verify what the user types back against that stored value with a capped number of attempts. None of that is something the send API does for you, regardless of provider — it's application-layer responsibility that sits around the same API call shown in Section 4.
We've covered this full cycle in depth separately, including real code for the generate/store/verify pattern: see OTP SMS API: How It Works, Security Best Practices, and Real Code Examples and OTP Sender Online: How OTP SMS Sending Actually Works.
16. Common Mistakes When Integrating an SMS API
Assuming the response is JSON. It's plain text — a bare message ID or an error code, not a structured object.
Skipping URL encoding on message content. Punctuation, line breaks, and non-Latin characters need encoding or the request can fail or truncate.
Treating "accepted by the API" as "delivered." Poll the delivery status endpoint (Section 6) to confirm actual handset delivery.
Retrying every failed request identically. Most error codes (101–108, 110) reflect a fixed problem a retry won't solve — see Section 8.
Calling the API from client-side code. This exposes your token — always call it from your server.
Hardcoding a route number without confirming it matches your DLT template's registered category. A mismatch here is a compliance issue, not just a technical one.
Not testing with your own number first. Catching a template mismatch or encoding issue on one test message is far cheaper than catching it after sending to a full list.
Building your integration around a specific throughput assumption you never confirmed. As covered in Section 7, sending capacity varies by account and plan — an integration built assuming an unconfirmed rate can queue or throttle unexpectedly under real load.
Ignoring the difference between a template being "approved" and your code actually sending content that matches it. These can drift apart over time as your application's copy changes, silently breaking delivery until someone investigates why messages stopped arriving.
17. Which Guide Should You Read Next?
Need the complete API reference — every parameter, both the Tally and XML methods, and delivery-report/credit-check endpoints in full? See Message APIs.
Building an OTP flow specifically? See OTP SMS API for secure generation, storage, and verification patterns.
Sending from a specific application or website rather than a general backend service? See Send SMS From Application.
Sending specifically to Indian numbers and need the DLT/routing picture? See Send SMS to India.
Running marketing campaigns rather than triggered, application-driven sends? See Send Bulk Text Messages and Online Bulk SMS for the dashboard-first path, or Send Bulk SMS Messages, Send Bulk SMS Online India, and Send Bulk Texts for related angles on the same core process.
Want the fuller picture of what a complete SMS messaging service includes — infrastructure, compliance, and support beyond just the API? See Bulk SMS Messaging Services.
18. Where TechTo Networks Fits
TechTo Networks' API is built around direct Tier-1 carrier connectivity to Jio, Airtel, Vodafone Idea, and BSNL, with DLT registration (entity, headers, and templates) handled as part of onboarding rather than left entirely to you. The HTTP API requires no proprietary SDK — any language that can make an HTTP request can integrate directly — and OTP traffic runs on infrastructure isolated from promotional sending, so a marketing campaign in flight doesn't compete with a live login attempt for delivery priority.
For a developer specifically, the practical upshot is that there's no library to install, no dependency to keep updated, and no version-lock risk from a third-party SDK going unmaintained — the entire integration surface is the plain HTTP requests shown in Section 4, which tend to age better than a wrapped SDK over the life of an application.
Where a different platform may fit better: if you need a single API spanning SMS, WhatsApp, RCS, voice, and email across many countries with deep enterprise SLAs, a larger multichannel CPaaS platform may offer more breadth. For an honest, named comparison against other providers rather than a self-declared "why we win" table, see Best Bulk SMS Service Provider in India.
19. Frequently Asked Questions
1. What does a successful send API response actually look like?
A bare numeric message ID as plain text — not a JSON object. A response matching a reserved error code (101–111) indicates failure instead.
2. Do I need an SDK to integrate TechTo's SMS API?
No. It's a plain HTTP GET request — any language with basic HTTP request capability can integrate directly without a dedicated library.
3. How do I know if my SMS actually delivered, not just got accepted by the API?
Poll the delivery status endpoint using the message ID your send request returned (Section 6) — the send response alone only confirms the platform accepted the request.
4. Can I send to multiple people in one API call?
Yes, using a comma-separated list in the number parameter, for sending identical content. For personalized content per recipient, send one request per recipient instead.
5. Should I retry a failed API call automatically?
Only for connection-related errors (111) or a temporary carrier unavailability (109) — most other error codes reflect a fixed problem in the request that a retry won't resolve. See Section 8 for the full breakdown.
6. Is it safe to call this API directly from my website's frontend JavaScript?
No. Your token would be exposed in every request visible to anyone inspecting network traffic from the browser. Always call the API from server-side code.
7. What's the difference between this guide and the full Message APIs documentation?
This is a quickstart focused on getting your first successful send working. Message APIs is the complete reference — every parameter, the Tally and XML variants in full, and all error and delivery-report details.
8. Can I use this API to send OTPs?
Yes — the send mechanism is the same API covered here. OTP-specific concerns (secure code generation, storage, expiry, and verification) are covered separately in OTP SMS API, since those are your application's responsibility, not something the send API itself handles.
9. What happens if I send a message that doesn't match my approved DLT template? It will typically fail at the carrier's scrubbing layer even if your API call itself succeeds and returns a message ID — the platform accepting a request isn't the same as the carrier delivering it. Keep your application's message content in sync with exactly what's approved.
10. How fast does an SMS sent via API actually arrive?
Typically within a few seconds under normal conditions with direct Tier-1 routing, though this varies by carrier, geography, and network conditions at the time of sending — treat any specific guaranteed number you see elsewhere with some skepticism unless it comes with a disclosed SLA and remedy terms.
11. How do I test my integration without risking a bad send to a real customer?
Send to your own number throughout development, and deliberately trigger a few failure conditions (an invalid number, a malformed parameter) to confirm your application handles errors correctly before any real customer traffic touches the integration. See Section 12 for the full pre-launch testing approach.
12. What should I monitor once my integration is live?
Delivery rate trends, credit balance, and unusual error-code patterns are the three most useful ongoing checks — see Section 13 for the full breakdown of what to watch and why each matters.
13. Can I use the same integration code for both promotional and transactional messages?
The request structure is identical — only the route value and the registered template differ. Just make sure your application selects the correct route and template for the message category you're actually sending, since using the wrong one is a compliance issue, not just a technical mismatch.
14. What's a realistic timeline to go from signing up to a working production integration?
Commonly one to two weeks from a standing start, with most of that time spent on DLT entity and template approval rather than writing integration code itself. See Section 11 for a fuller breakdown of where the time actually goes.
15. Do I need to rebuild my integration if I later add a second message template?
No your integration code for the send call stays the same; you're just referencing a different approved template's content and, for the XML method, a different template ID. The underlying API mechanics don't change per template.
16. Is there a real SDK or library I should be using instead of plain HTTP requests?
Not currently for this API it's designed to be simple enough that a dedicated SDK isn't necessary, and the plain-HTTP approach shown throughout this guide is the accurate, supported integration path. Be skeptical of any third-party package claiming to wrap this API unless you can verify it's officially maintained.




Comments