# Getting started Textbelt is an SMS API that is built for developers who just want to send and receive SMS. Sending an SMS is a simple thing. Our goal is to provide an API that is correspondingly simple, without requiring account configuration, logins, or extra recurring billing. ## Send an SMS using HTTP POST The **`https://textbelt.com/text`** endpoint accepts a POST request with the following parameters: * **phone:** A phone number. If you're in the U.S. or Canada, you can just send a normal 10-digit phone number with area code. Outside the U.S., it is best to send the phone number in [E.164 format](https://docs.textbelt.com/faq#how-should-i-format-my-phone-numbers) with your country code. * **message:** The content of your SMS. * **key:** Your API key (use `textbelt` to send a free message). * **sender:** Optionally, the name of the business/organization you represent. This field is for regulatory purposes and *is not visible to the end user* in most countries.\ \ If not set, sender will default to your account-wide sender name. See [Compliance](https://docs.textbelt.com/compliance#sender-identification) for more detail. Every programming language has a way to send an HTTP POST request. Instead of installing a special Textbelt library, just send a POST request using your preferred method. Below are some examples in common languages. ### Examples {% tabs %} {% tab title="Bash" %} ```bash curl -X POST https://textbelt.com/text \ --data-urlencode phone='5555555555' \ --data-urlencode message='Hello world' \ -d key=textbelt ``` {% endtab %} {% tab title="Python" %} Using the popular [requests](http://docs.python-requests.org/en/master/) library: ```python import requests resp = requests.post('https://textbelt.com/text', { 'phone': '5555555555', 'message': 'Hello world', 'key': 'textbelt', }) print(resp.json()) ``` {% endtab %} {% tab title="Ruby" %} ```ruby require 'net/http' require 'uri' uri = URI.parse("https://textbelt.com/text") Net::HTTP.post_form(uri, { :phone => '5555555555', :message => 'Hello world', :key => 'textbelt', }) ``` {% endtab %} {% tab title="Node" %} Using the popular [request](https://www.npmjs.com/package/request) or [axios](https://www.npmjs.com/package/axios) libraries: ```javascript // Using request const request = require('request'); request.post('https://textbelt.com/text', { form: { phone: '5555555555', message: 'Hello world', key: 'textbelt', }, }, (err, httpResponse, body) => { console.log(JSON.parse(body)); }); // Using axios const axios = require('axios'); axios.post('https://textbelt.com/text', { phone: '5555555555', message: 'Hello world', key: 'textbelt', }).then(response => { console.log(response.data); }) ``` {% endtab %} {% tab title="Javascript" %} Using the browser [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) and a [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) request: ```javascript fetch('https://textbelt.com/text', { method: 'post', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: '5555555555', message: 'Hello world', key: 'textbelt', }), }).then(response => { return response.json(); }).then(data => { console.log(data); }); ``` {% endtab %} {% tab title="PHP" %} ```php $ch = curl_init('https://textbelt.com/text'); $data = array( 'phone' => '5555555555', 'message' => 'Hello world', 'key' => 'textbelt', ); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); ``` {% endtab %} {% tab title="C#" %} ```csharp using System; using System.Collections.Specialized; using System.Net; using (WebClient client = new WebClient()) { byte[] response = client.UploadValues("http://textbelt.com/text", new NameValueCollection() { { "phone", "5555555555" }, { "message", "Hello world" }, { "key", "textbelt" }, }); string result = System.Text.Encoding.UTF8.GetString(response); } ``` {% endtab %} {% tab title="Java" %} Using the popular [Apache HttpComponents](https://hc.apache.org/) library: ```java final NameValuePair[] data = { new BasicNameValuePair("phone", "5555555555"), new BasicNameValuePair("message", "Hello world"), new BasicNameValuePair("key", "textbelt") }; HttpClient httpClient = HttpClients.createMinimal(); HttpPost httpPost = new HttpPost("https://textbelt.com/text"); httpPost.setEntity(new UrlEncodedFormEntity(Arrays.asList(data))); HttpResponse httpResponse = httpClient.execute(httpPost); String responseString = EntityUtils.toString(httpResponse.getEntity()); JSONObject response = new JSONObject(responseString); ``` {% endtab %} {% tab title="Go" %} ```go import ( "net/http" "net/url" ) func main() { values := url.Values{ "phone": {"5555555555"}, "message": {"Hello world"}, "key": {"textbelt"}, } http.PostForm("https://textbelt.com/text", values) } ``` {% endtab %} {% tab title="PSH" %} ```bash $body = @{ "phone"="5555555555" "message"="Hello World" "key"="textbelt" } $submit = Invoke-WebRequest -Uri https://textbelt.com/text -Body $body -Method Post ``` {% endtab %} {% endtabs %} **Note:** Regulations require that you state the name of your business/organization in SMS messages. If you are sending recurring SMS, opt-out language is required (e.g. *"Reply STOP to opt-out"*. We automatically handle STOP replies). ### Response The `/text` endpoint will respond with JSON: * **success:** Whether the message was successfully sent (true/false). * **quotaRemaining:** The amount of credit remaining on your key. * **textId:** The ID of the sent message, used for looking up its status. Only present when success=true. Use this to [check SMS delivery status](https://docs.textbelt.com/other-api-endpoints#checking-sms-delivery-status). * **error:** A string describing the problem. Only present when success=false. An example of a message successfully sent: ```javascript {"success": true, "quotaRemaining": 40, "textId": 12345} ``` Here's an example of when you've run out of quota: ```javascript {"success": false, "quotaRemaining": 0, "error": "Out of quota"} ``` Or when you're missing a required variable such as **phone**, **message**, or **key**: ```javascript {"success": false, "error": "Incomplete request"} ``` If you're getting the above message even though you're specifying the variable, you should make sure that you're sending the request correctly as an HTTP POST request. ### Testing this API If you want to validate your key without actually using your text quota, **append "`_test`" to your key** and you will receive a response from the `/text` endpoint confirming that a text would send. However, **credit will not be deducted from your account.** ## Receiving SMS replies **U.S. phone numbers only:** Textbelt lets you receive replies to SMS you've sent. Replies are sent by webhook, meaning you will have to set up an HTTP or HTTPS route on your website that will process inbound SMS. Add a `replyWebhookUrl` parameter to your send message request. This is the same as the examples above, except it includes **replyWebhookUrl**. For example: ```bash curl -X POST https://textbelt.com/text \ --data-urlencode phone='5555555555' \ --data-urlencode message='Hello?' \ -d replyWebhookUrl='https://my.site/api/handleSmsReply' \ -d key=textbelt ``` This will send an SMS. If the recipient responds, Textbelt will send an HTTP POST request to the specified endpoint (in this case, `https://my.site/api/handleSmsReply`). The webhook payload is `application/json` encoded. Your server must interpret it like any other HTTP POST request with a JSON payload. The JSON payload contains the following: * **textId:** The ID of the original text that began the conversation. * **fromNumber:** The phone number of the user that sent the reply (you can use this, for example, to send them a response depending on their reply). * **text:** The content of their reply Here's an example payload: ```javascript { "textId": "123456", "fromNumber": "+1555123456", "text": "Here is my reply" } ``` {% hint style="info" %} Note: SMS replies cannot be received on the free `textbelt` key. {% endhint %} #### Verifying the webhook It is best practice to verify the incoming POST request to make sure it is not forged. The POST request contains a header `X-textbelt-signature`, which is an HMAC that authenticates the JSON payload using a SHA-256 hash function. The header `X-textbelt-timestamp` contains a UNIX timestamp (in seconds). You should ensure that this timestamp is not more than 15 minutes out of date. To verify that the request is valid, take the timestamp + raw JSON payload and sign it with your API key. The result should be equal to the signature. For example, in Javascript: ```javascript const crypto = require("crypto"); function verify(apiKey, timestamp, requestSignature, requestPayload) { const mySignature = crypto .createHmac("sha256", apiKey) .update(timestamp + requestPayload) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(requestSignature), Buffer.from(mySignature) ); } ``` And in Python: ```python import hmac import hashlib def verify(api_key, timestamp, request_signature, request_payload): my_signature = hmac.new(api_key.encode('utf-8'), (timestamp + request_payload).encode('utf-8'), hashlib.sha256).hexdigest() return hmac.compare_digest(request_signature, my_signature) ``` #### Including custom data The `/text` endpoint supports a `webhookData` field. This data is passed as `data` in the webhook request. For example: ```bash curl -X POST https://textbelt.com/text \ --data-urlencode phone='5555555555' \ --data-urlencode message='Hello?' \ -d replyWebhookUrl='https://my.site/api/handleSmsReply' \ -d webhookData='my custom data' -d key=textbelt ``` Produces a response: ```javascript { "textId": "123456", "fromNumber": "+1555123456", "text": "Here is my reply" "data": "my custom data" } ``` There is a maximum length of 100 characters in the `webhookData` field. ## Get an API key [Create an API key](https://textbelt.com/create-key/) to start sending and receiving SMS!