HTTP 开放接口
无需安装任何组件、不保持长连接,一个 HTTP 请求即可完成提交与结果回收。
支持单条 / 批量 / 变量 / 一对一四种提交方式;发送状态与用户上行回复可主动拉取,也可配好回调地址由平台推送;余额可实时查询。
凭据与下方接入参数完全共用(sp_id 即账号,密码即 API 密码);国内产品内容须以已报备【签名】开头。
接口、控制台与监控一应俱全,按你的技术栈自由选择接入方式。
无需安装任何组件、不保持长连接,一个 HTTP 请求即可完成提交与结果回收。
支持单条 / 批量 / 变量 / 一对一四种提交方式;发送状态与用户上行回复可主动拉取,也可配好回调地址由平台推送;余额可实时查询。
凭据与下方接入参数完全共用(sp_id 即账号,密码即 API 密码);国内产品内容须以已报备【签名】开头。
下列示例取自官方接口文档、与线上实现逐行一致,覆盖 7 种语言:凭据准备、单条发送、状态报告拉取与解析。把 sp_id 与 API 密码换成开发者页「接入参数」里的真实值即可直接运行。
<?php $BASE = 'https://user.arkai.site/api/v1/open'; // 开发者页文档顶部的接口基地址 $SP_ID = '669736'; // 开发者页「SPID / system_id」 $API_PWD = 'a1b2c3d4'; // 开发者页「密码」(8 位) // 服务端百分号编码基于 JS encodeURIComponent:! ' ( ) ~ 保留原样、* 编成 %2A、空格为 %20, // rawurlencode 会把 ! ' ( ) 编掉,故需还原(内容里出现半角 ! 或括号时签名才不会错) function percentEncode($s) { return str_replace(['%21', '%27', '%28', '%29'], ['!', "'", '(', ')'], rawurlencode((string) $s)); } // signature 鉴权(可选):字典序拼串 → METHOD&%2F&QueryString → HmacSHA1(密钥=API 密码原文) → base64 function makeSignature($method, array $params) { global $API_PWD; unset($params['signature']); ksort($params, SORT_STRING); $pairs = []; foreach ($params as $k => $v) { $pairs[] = percentEncode($k) . '=' . percentEncode($v); } $strToSign = strtoupper($method) . '&' . percentEncode('/') . '&' . implode('&', $pairs); return base64_encode(hash_hmac('sha1', $strToSign, $API_PWD, true)); } function call($path, array $params, $method = 'POST') { global $BASE; $ch = curl_init(); if ($method === 'POST') { curl_setopt_array($ch, [ CURLOPT_URL => $BASE . $path, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8'], ]); } else { curl_setopt($ch, CURLOPT_URL, $BASE . $path . '?' . http_build_query($params)); } curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15]); $body = curl_exec($ch); if ($body === false) { $e = curl_error($ch); curl_close($ch); throw new Exception('HTTP 请求失败: ' . $e); } curl_close($ch); return json_decode($body, true); } // 1. 单条发送(password = API 密码的 md5,PHP md5() 本身就是 32 位小写十六进制) $send = [ 'sp_id' => $SP_ID, 'password' => md5($API_PWD), 'mobile' => '176xxxxxxxxx', 'content' => '【测试签名】您的验证码是123456', // 国内产品必须带已报备签名 ]; // 改用 signature 鉴权:unset($send['password']); $send['signature'] = makeSignature('POST', $send); var_dump(call('/send-sms-single', $send)); // 2. 批量发送(一次最多一万条) var_dump(call('/send-sms-batch', [ 'sp_id' => $SP_ID, 'password' => md5($API_PWD), 'mobiles' => '176xxxxxxxxx,171xxxxxxxxx', 'content' => '【测试签名】您的验证码是123456', ])); // 3. 拉取状态报告:data 为 "扩展号,msg_id,手机号,状态,时间,售价",多条以 | 分隔 // 每条只能拉取一次,务必先落库再处理 $auth = ['sp_id' => $SP_ID, 'password' => md5($API_PWD)]; $report = call('/report', $auth, 'GET'); if (($report['code'] ?? -1) === 0 && !empty($report['data'])) { foreach (explode('|', $report['data']) as $line) { list($ext, $msgId, $phone, $status, $sentAt, $price) = explode(',', $line); echo $phone . ' ' . $status . ' ' . $sentAt . PHP_EOL; } } var_dump(call('/get-reply', $auth, 'GET')); // 4. 上行回复 var_dump(call('/balance', $auth, 'GET')); // 5. 余额
# ===== 0. 准备(基地址与凭据都来自用户端「开发者」页)===== BASE="https://user.arkai.site/api/v1/open" SP_ID="669736" # 开发者页「SPID / system_id」 API_PWD="a1b2c3d4" # 开发者页「密码」(8 位,CMPP/SMPP 与 HTTP 共用) # 鉴权用 password:API 密码做 md5(32 位小写十六进制) PWD=$(printf '%s' "$API_PWD" | md5sum | cut -d' ' -f1) # macOS: PWD=$(md5 -qs "$API_PWD") # ===== 1. 单条短信发送 ===== curl -s -X POST "$BASE/send-sms-single" \ --data-urlencode "sp_id=$SP_ID" \ --data-urlencode "password=$PWD" \ --data-urlencode "mobile=176xxxxxxxxx" \ --data-urlencode "content=【测试签名】您的验证码是123456" # => {"code":0,"msg":"success","msg_id":"100000017"} # ===== 2. 批量发送(一次最多一万条,号码用英文逗号分隔)===== curl -s -X POST "$BASE/send-sms-batch" \ --data-urlencode "sp_id=$SP_ID" \ --data-urlencode "password=$PWD" \ --data-urlencode "mobiles=176xxxxxxxxx,171xxxxxxxxx" \ --data-urlencode "content=【测试签名】您的验证码是123456" # ===== 3. 拉取状态报告(GET,每条只能拉一次,拿到立刻入库)===== curl -s -G "$BASE/report" \ --data-urlencode "sp_id=$SP_ID" \ --data-urlencode "password=$PWD" # => {"code":0,"msg":"success","data":"扩展号,msg_id,手机号,状态,时间,售价|..."} # ===== 4. 拉取上行回复 / 查询余额 ===== curl -s -G "$BASE/get-reply" --data-urlencode "sp_id=$SP_ID" --data-urlencode "password=$PWD" curl -s -G "$BASE/balance" --data-urlencode "sp_id=$SP_ID" --data-urlencode "password=$PWD"
import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; public class SmsDemo { static final String BASE = "https://user.arkai.site/api/v1/open"; // 开发者页文档顶部的接口基地址 static final String SP_ID = "669736"; // 开发者页「SPID / system_id」 static final String API_PWD = "a1b2c3d4"; // 开发者页「密码」(8 位,CMPP/SMPP 与 HTTP 共用) static String md5(String s) throws Exception { StringBuilder sb = new StringBuilder(); for (byte b : MessageDigest.getInstance("MD5").digest(s.getBytes(StandardCharsets.UTF_8))) { sb.append(String.format("%02x", b)); } return sb.toString(); } // 百分号编码需与服务端(JS encodeURIComponent) 完全一致:空格 %20、* 编成 %2A、~ 保留, // 而 ! ' ( ) 必须保持原样(URLEncoder 默认会把它们编掉,故要还原回来) static String pe(String s) throws Exception { return URLEncoder.encode(s, "UTF-8") .replace("+", "%20").replace("*", "%2A").replace("%7E", "~") .replace("%21", "!").replace("%27", "'").replace("%28", "(").replace("%29", ")"); } // signature 鉴权(可选):除 signature 外的全部参数按 key 字典序拼 QueryString, // 待签名串 = METHOD&%2F&QueryString,HmacSHA1 密钥为 API 密码原文,结果 base64 static String signature(String method, Map<String, String> params) throws Exception { StringBuilder qs = new StringBuilder(); for (String k : new TreeMap<>(params).keySet()) { if ("signature".equals(k)) continue; if (qs.length() > 0) qs.append('&'); qs.append(pe(k)).append('=').append(pe(params.get(k))); } Mac mac = Mac.getInstance("HmacSHA1"); mac.init(new SecretKeySpec(API_PWD.getBytes(StandardCharsets.UTF_8), "HmacSHA1")); String strToSign = method.toUpperCase() + "&" + pe("/") + "&" + qs; return Base64.getEncoder().encodeToString(mac.doFinal(strToSign.getBytes(StandardCharsets.UTF_8))); } static String form(Map<String, String> p) throws Exception { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> e : p.entrySet()) { if (sb.length() > 0) sb.append('&'); sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8")); } return sb.toString(); } static String call(String path, Map<String, String> p, boolean isPost) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest req = isPost ? HttpRequest.newBuilder(URI.create(BASE + path)) .header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") .POST(HttpRequest.BodyPublishers.ofString(form(p), StandardCharsets.UTF_8)).build() : HttpRequest.newBuilder(URI.create(BASE + path + "?" + form(p))).GET().build(); return client.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)).body(); } public static void main(String[] args) throws Exception { Map<String, String> send = new LinkedHashMap<>(); send.put("sp_id", SP_ID); send.put("password", md5(API_PWD)); // 鉴权方式一:password = API 密码的 md5(最简) send.put("mobile", "176xxxxxxxxx"); send.put("content", "【测试签名】您的验证码是123456"); // 国内产品必须带已报备签名 // 鉴权方式二(签名):去掉 password,改为 send.put("signature", signature("POST", send)); System.out.println("单条发送 -> " + call("/send-sms-single", send, true)); Map<String, String> batch = new LinkedHashMap<>(); batch.put("sp_id", SP_ID); batch.put("password", md5(API_PWD)); batch.put("mobiles", "176xxxxxxxxx,171xxxxxxxxx"); batch.put("content", "【测试签名】您的验证码是123456"); System.out.println("批量发送 -> " + call("/send-sms-batch", batch, true)); Map<String, String> auth = new LinkedHashMap<>(); auth.put("sp_id", SP_ID); auth.put("password", md5(API_PWD)); System.out.println("状态报告 -> " + call("/report", auth, false)); // data: 扩展号,msg_id,手机号,状态,时间,售价|... System.out.println("上行回复 -> " + call("/get-reply", auth, false)); System.out.println("账户余额 -> " + call("/balance", auth, false)); } }
# -*- coding: utf-8 -*- import base64 import hashlib import hmac import urllib.parse import requests BASE = "https://user.arkai.site/api/v1/open" # 开发者页文档顶部的接口基地址 SP_ID = "669736" # 开发者页「SPID / system_id」 API_PWD = "a1b2c3d4" # 开发者页「密码」(8 位,CMPP/SMPP 与 HTTP 共用) # 鉴权方式一:password = API 密码的 md5(hexdigest 已是 32 位小写十六进制) PASSWORD = hashlib.md5(API_PWD.encode("utf-8")).hexdigest() # 百分号编码需与 JS encodeURIComponent 一致:额外保留 - _ . ! ~ * ' ( ),再把 * 编成 %2A def percent_encode(value): return urllib.parse.quote(str(value), safe="-_.!~*'()").replace("*", "%2A") # signature 鉴权方式二:除 signature 外的参数按 key 字典序拼 QueryString, # 待签名串 = METHOD&%2F&QueryString,HmacSHA1 密钥为 API 密码**原文**,结果 base64 def make_signature(method, params): qs = "&".join( percent_encode(k) + "=" + percent_encode(params[k]) for k in sorted(params) if k != "signature" ) str_to_sign = method.upper() + "&" + percent_encode("/") + "&" + qs digest = hmac.new(API_PWD.encode("utf-8"), str_to_sign.encode("utf-8"), hashlib.sha1).digest() return base64.b64encode(digest).decode("utf-8") def post(path, params): # 表单按 UTF-8 提交即可;服务端解析出原文后重算签名,表单编码方式不必与签名串相同 return requests.post(BASE + path, data=params, timeout=15).json() def get(path, params): return requests.get(BASE + path, params=params, timeout=15).json() # 1. 单条短信发送 send = { "sp_id": SP_ID, "password": PASSWORD, "mobile": "176xxxxxxxxx", "content": "【测试签名】您的验证码是123456", # 国内产品必须带已报备签名 } # 改用 signature:send.pop("password"); send["signature"] = make_signature("POST", send) print("单条发送 ->", post("/send-sms-single", send)) # 2. 批量发送(一次最多一万条) print("批量发送 ->", post("/send-sms-batch", { "sp_id": SP_ID, "password": PASSWORD, "mobiles": "176xxxxxxxxx,171xxxxxxxxx", "content": "【测试签名】您的验证码是123456", })) # 3. 拉取状态报告:data = "扩展号,msg_id,手机号,状态,时间,售价",多条以 | 分隔 # 每条只能拉取一次,务必先落库再按 手机号+msg_id 匹配 auth = {"sp_id": SP_ID, "password": PASSWORD} rep = get("/report", auth) if rep.get("code") == 0 and rep.get("data"): for line in rep["data"].split("|"): ext, msg_id, phone, status, sent_at, price = line.split(",") print("状态报告 ->", phone, status, sent_at) print("上行回复 ->", get("/get-reply", auth)) # 4. 上行回复 print("账户余额 ->", get("/balance", auth)) # 5. 余额
const crypto = require('crypto'); const BASE = process.env.SMS_BASE || 'https://user.arkai.site/api/v1/open'; // 开发者页文档顶部的接口基地址 const SP_ID = process.env.SMS_SPID || '669736'; // 开发者页「SPID / system_id」 const API_PWD = process.env.SMS_PWD || 'a1b2c3d4';// 开发者页「密码」(8 位) // 鉴权方式一:password = API 密码的 md5(32 位小写十六进制) const md5 = (s) => crypto.createHash('md5').update(s, 'utf8').digest('hex'); // 百分号编码与服务端同源:encodeURIComponent 后按文档做三处替换 const percentEncode = (v) => encodeURIComponent(String(v)) .split('+').join('%20').split('*').join('%2A').split('%7E').join('~'); // signature 鉴权方式二:除 signature 外的参数按 key 字典序拼 QueryString, // 待签名串 = METHOD&%2F&QueryString,HmacSHA1 密钥为 API 密码原文,结果 base64 function makeSignature(method, params) { const qs = Object.keys(params) .filter((k) => k !== 'signature' && params[k] !== undefined && params[k] !== null) .sort() .map((k) => percentEncode(k) + '=' + percentEncode(params[k])) .join('&'); const strToSign = method.toUpperCase() + '&' + percentEncode('/') + '&' + qs; return crypto.createHmac('sha1', API_PWD).update(strToSign, 'utf8').digest('base64'); } async function call(path, params, method = 'POST') { const body = new URLSearchParams(params).toString(); const res = await fetch(method === 'POST' ? BASE + path : BASE + path + '?' + body, { method, ...(method === 'POST' ? { headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }, body } : {}), }); return res.json(); } (async () => { const auth = { sp_id: SP_ID, password: md5(API_PWD) }; // 1. 单条发送(国内产品内容必须以已报备【签名】开头) const send = { ...auth, mobile: '176xxxxxxxxx', content: '【测试签名】您的验证码是123456' }; // 改用 signature:去掉 password 后 send.signature = makeSignature('POST', send) console.log('单条发送 ->', await call('/send-sms-single', send)); // 2. 批量发送(一次最多一万条) console.log('批量发送 ->', await call('/send-sms-batch', { ...auth, mobiles: '176xxxxxxxxx,171xxxxxxxxx', content: '【测试签名】您的验证码是123456', })); // 3. 拉取状态报告:data = "扩展号,msg_id,手机号,状态,时间,售价",多条以 | 分隔 const rep = await call('/report', auth, 'GET'); if (rep.code === 0 && rep.data) { rep.data.split('|').forEach((line) => { const [ext, msgId, phone, status, sentAt, price] = line.split(','); console.log('状态报告 ->', phone, status, sentAt); // 每条只能拉一次,务必先落库 }); } console.log('上行回复 ->', await call('/get-reply', auth, 'GET')); // 4. 上行回复 console.log('账户余额 ->', await call('/balance', auth, 'GET')); // 5. 余额 })();
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; class SmsDemo { const string Base = "https://user.arkai.site/api/v1/open"; // 开发者页文档顶部的接口基地址 const string SpId = "669736"; // 开发者页「SPID / system_id」 const string ApiPwd = "a1b2c3d4"; // 开发者页「密码」(8 位) static readonly HttpClient Http = new HttpClient { Timeout = TimeSpan.FromSeconds(15) }; // 鉴权方式一:password = API 密码的 md5(32 位小写十六进制) static string Md5(string s) => Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(s))).ToLowerInvariant(); // 百分号编码需与服务端(JS encodeURIComponent) 对齐:! ' ( ) ~ 保留原样,* 编成 %2A,空格 %20 static string PercentEncode(string s) => Uri.EscapeDataString(s) .Replace("%21", "!").Replace("%27", "'").Replace("%28", "(").Replace("%29", ")"); // signature 鉴权方式二:字典序拼 QueryString → METHOD&%2F&QS → HmacSHA1(密钥=API 密码原文) → base64 static string MakeSignature(string method, Dictionary<string, string> p) { var qs = string.Join("&", p.Where(kv => kv.Key != "signature") .OrderBy(kv => kv.Key, StringComparer.Ordinal) .Select(kv => PercentEncode(kv.Key) + "=" + PercentEncode(kv.Value))); using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(ApiPwd)); var strToSign = method.ToUpperInvariant() + "&" + PercentEncode("/") + "&" + qs; return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(strToSign))); } static async Task<string> Call(string path, Dictionary<string, string> p, string method = "POST") { HttpResponseMessage resp; if (method == "POST") resp = await Http.PostAsync(Base + path, new FormUrlEncodedContent(p)); else resp = await Http.GetAsync(Base + path + "?" + await new FormUrlEncodedContent(p).ReadAsStringAsync()); return await resp.Content.ReadAsStringAsync(); } static async Task Main() { var auth = new Dictionary<string, string> { ["sp_id"] = SpId, ["password"] = Md5(ApiPwd) }; // 1. 单条发送(国内产品内容必须以已报备【签名】开头) var send = new Dictionary<string, string>(auth) { ["mobile"] = "176xxxxxxxxx", ["content"] = "【测试签名】您的验证码是123456" }; // 改用 signature:去掉 password 后 send["signature"] = MakeSignature("POST", send); Console.WriteLine("单条发送 -> " + await Call("/send-sms-single", send)); // 2. 批量发送(一次最多一万条) var batch = new Dictionary<string, string>(auth) { ["mobiles"] = "176xxxxxxxxx,171xxxxxxxxx", ["content"] = "【测试签名】您的验证码是123456" }; Console.WriteLine("批量发送 -> " + await Call("/send-sms-batch", batch)); // 3. 状态报告:data = "扩展号,msg_id,手机号,状态,时间,售价",多条以 | 分隔,每条只能拉一次 Console.WriteLine("状态报告 -> " + await Call("/report", new Dictionary<string, string>(auth), "GET")); Console.WriteLine("上行回复 -> " + await Call("/get-reply", new Dictionary<string, string>(auth), "GET")); Console.WriteLine("账户余额 -> " + await Call("/balance", new Dictionary<string, string>(auth), "GET")); } }
package main import ( "crypto/hmac" "crypto/md5" "crypto/sha1" "encoding/base64" "fmt" "io" "net/http" "net/url" "sort" "strings" ) const ( base = "https://user.arkai.site/api/v1/open" // 开发者页文档顶部的接口基地址 spID = "669736" // 开发者页「SPID / system_id」 apiPwd = "a1b2c3d4" // 开发者页「密码」(8 位) ) // 鉴权方式一:password = API 密码的 md5(32 位小写十六进制) func md5Hex(s string) string { return fmt.Sprintf("%x", md5.Sum([]byte(s))) } // 百分号编码需与服务端(JS encodeURIComponent) 同规则:保留字母数字与 - _ . ! ~ ' ( ), // 其余按 UTF-8 逐字节 %XX(* 不在保留集合内,天然编成 %2A,空格编成 %20) func percentEncode(s string) string { const keep = "-_.!~'()" var b strings.Builder for i := 0; i < len(s); i++ { c := s[i] if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || strings.IndexByte(keep, c) >= 0 { b.WriteByte(c) continue } fmt.Fprintf(&b, "%%%02X", c) } return b.String() } // signature 鉴权方式二:字典序拼 QueryString → METHOD&%2F&QS → HmacSHA1(密钥=API 密码原文) → base64 func makeSignature(method string, params map[string]string) string { keys := make([]string, 0, len(params)) for k := range params { if k != "signature" { keys = append(keys, k) } } sort.Strings(keys) pairs := make([]string, 0, len(keys)) for _, k := range keys { pairs = append(pairs, percentEncode(k)+"="+percentEncode(params[k])) } strToSign := strings.ToUpper(method) + "&" + percentEncode("/") + "&" + strings.Join(pairs, "&") mac := hmac.New(sha1.New, []byte(apiPwd)) mac.Write([]byte(strToSign)) return base64.StdEncoding.EncodeToString(mac.Sum(nil)) } func call(path string, params map[string]string, method string) string { form := url.Values{} for k, v := range params { form.Set(k, v) } encoded := form.Encode() var ( resp *http.Response err error ) if method == "POST" { resp, err = http.Post(base+path, "application/x-www-form-urlencoded; charset=utf-8", strings.NewReader(encoded)) } else { resp, err = http.Get(base + path + "?" + encoded) } if err != nil { return "请求失败: " + err.Error() } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) return string(body) } func main() { auth := map[string]string{"sp_id": spID, "password": md5Hex(apiPwd)} // 1. 单条发送(国内产品内容必须以已报备【签名】开头) send := map[string]string{"mobile": "176xxxxxxxxx", "content": "【测试签名】您的验证码是123456"} for k, v := range auth { send[k] = v } // 改用 signature:delete(send, "password"); send["signature"] = makeSignature("POST", send) fmt.Println("单条发送 ->", call("/send-sms-single", send, "POST")) // 2. 批量发送(一次最多一万条) batch := map[string]string{"mobiles": "176xxxxxxxxx,171xxxxxxxxx", "content": "【测试签名】您的验证码是123456"} for k, v := range auth { batch[k] = v } fmt.Println("批量发送 ->", call("/send-sms-batch", batch, "POST")) // 3. 状态报告:data = "扩展号,msg_id,手机号,状态,时间,售价",多条以 | 分隔,每条只能拉一次 fmt.Println("状态报告 ->", call("/report", auth, "GET")) fmt.Println("上行回复 ->", call("/get-reply", auth, "GET")) fmt.Println("账户余额 ->", call("/balance", auth, "GET")) }
接口基地址 https://user.arkai.site/api/v1/open,代码注释沿用官方文档原文;状态报告与上行回复每条只能取到一次,请先落库再走业务处理。
从注册到上线全流程线上自助完成,沙箱环境免费调试,正式通道按量计费。
提交企业资料,线上完成实名认证与短信签名报备,无需线下邮寄材料。
在控制台创建应用并提交短信模板,最快 2 小时审核通过,支持多模板批量管理。
直接调用 HTTP API,提供 7 种语言示例,沙箱环境先跑通再切正式通道。
配置发送频率与失败告警,实时查看发送、送达与失败明细,问题分钟级定位。
所有接口以 sp_id 标识产品,POST 表单编码提交,返回结构统一为 code / msg / data;发送类接口的拦截错误实时返回。
| 接口 | 请求 | 说明 |
|---|---|---|
| 单条发送 | POST /send-sms-single | 单个号码提交,拦截类错误实时返回;返回的 msg_id 用于匹配状态报告。 |
| 批量发送 | POST /send-sms-batch | 号码用英文逗号分隔,一次最多 10 万个;单包上限 1 万,超出由平台自动拆包,对外仍是同一个 msg_id。 |
| 变量发送 | POST /send-variable | 内容用 {变量名} 占位(支持中文),每个号码的变量值按顺序跟在手机号后,多组以分号分隔。 |
| 一对一发送 | POST /send-biunique | params 传 JSON:key 为手机号、value 为内容;一次最多 500 条,同一批的签名必须一致。 |
| 获取状态报告 | GET /report | 每条状态仅可取到一次,拉到请立刻落库;单次最多返回 1000 条,未拉完继续轮询。 |
| 推送状态报告 | PUSH PUSH 回调 | 推送到开发者页配置的接收地址,POST 原始字节流;报文与 GET /report 的 data 完全同构,切换模式无需改解析代码。 |
| 获取上行回复 | GET /get-reply | 用户回复(如退订 TD)每条仅可取到一次;报文本身不带 msg_id,平台按最近一次提交反查回填。 |
| 推送上行回复 | PUSH PUSH 回调 | 同样以 POST 推送,内容为 urlencode 后的回复文本;你的接口返回 2xx 即视为接收成功。 |
| 获取余额 | GET /balance | 返回账户可用余额,用于发送前的额度自检与告警。 |
状态报告只返回最终态:DELIVRD 送达、UNDELIV 未送达、REJECTD 平台拒绝、DELETED 已删除;处理中的短信不会提前返回,请继续轮询。