개발자

대량 SMS API 문서

SendBulkSMS REST API는 한 번의 요청으로 10,000개의 번호에 SMS를 보내고, 캠페인 ID를 즉시 반환하며 모든 메시지 상태를 웹훅으로 보고해요. HTTPS 위에서 JSON을 사용하고, Bearer API 키로 인증하며, 콘솔과 동일한 메시지당 요금을 청구해요.

https://sendbulksms.cc/api/v1

빠른 시작

  1. 콘솔에서 계정을 생성하고 잔액을 충전하세요.
  2. API 키를 열고 키를 생성하세요. 첫 충전이 완료된 후에 키를 만들 수 있어요. 키는 한 번만 표시되니 비밀번호처럼 안전하게 보관하세요.
  3. /sms에 번호와 텍스트를 포함한 POST 요청을 보내세요. 반환된 캠페인 ID를 확인한 후 콘솔이나 웹훅을 통해 전송 상태를 확인하세요.

인증

공개 요금표를 제외한 모든 요청에는 Authorization 헤더에 API 키를 넣어요. 키는 sbk_live_로 시작해요. 새 키를 만들면 이전 키는 바로 대체돼요.

Authorization: Bearer sbk_live_YOUR_KEY

SMS 보내기

POST/sms

캠페인을 생성하고 잔액에서 요금을 차감한 후 모든 메시지를 큐에 넣어요. 번호는 정리되고 중복 제거가 되며, 사용할 수 없는 번호는 dropped에 이유와 함께 반환되고 요금이 청구되지 않아요.

필드설명
to필수국제 형식의 하나의 번호 또는 최대 10,000개의 번호 배열, 예: "+447700900461"
text필수메시지 텍스트, 최대 10 세그먼트. GSM-7 텍스트는 세그먼트당 160자, UCS-2는 70자까지 가능
schedule_at선택ISO 8601 형식의 시간, 5분에서 60일 이후. 생략하면 즉시 전송
client_ref선택자신의 참조, 최대 64자의 a-z A-Z 0-9 _ . : -, 보고서 및 웹훅에 반환됨
opt_out선택true로 설정하면 계정 설정에 있는 수신 거부 안내 문구가 추가돼요
curl -X POST https://sendbulksms.cc/api/v1/sms \
  -H "Authorization: Bearer sbk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["+447700900461", "+5511955550123"],
    "text": "Hi, your order 4821 ships today.",
    "client_ref": "orders-4821"
  }'

응답: 캠페인과 비용 및 처음 1,000개의 메시지 캠페인 메시지로 더 많은 정보를 가져오세요.

HTTP/1.1 201 Created
{
  "id": "CP-7KQ2-M9XA-4HTD",
  "status": "sending",
  "source": "api",
  "recipients": 2,
  "segments": 1,
  "encoding": "GSM-7",
  "cost": "0.01078",
  "refunded": "0.00000",
  "client_ref": "orders-4821",
  "scheduled_at": null,
  "created_at": "2026-11-07T10:32:04.118Z",
  "done_at": null,
  "counts": { "queued": 2, "sent": 0, "delivered": 0, "failed": 0, "canceled": 0 },
  "dropped": [],
  "balance": "248.30422",
  "messages": [
    { "id": "SM-2RBW-8MZN-QF6K", "campaign_id": "CP-7KQ2-M9XA-4HTD", "to": "+447700900461", "country": "GB", "parts": 1, "price": "0.00819", "status": "queued", "error": null, "created_at": "2026-11-07T10:32:04.118Z", "sent_at": null, "done_at": null }
  ]
}

전송 예상

POST/estimate

/sms와 동일한 본문을 받아서 실제로 요금을 청구하거나 발송하지 않고 수신자, 세그먼트, 인코딩, 총 비용 및 국가별 비용을 반환해요.

{ "recipients": 2, "dropped": [], "segments": 1, "encoding": "GSM-7", "cost": "0.01078", "balance": "248.31500",
  "countries": [ { "country": "GB", "recipients": 1, "cost": "0.00819" }, { "country": "BR", "recipients": 1, "cost": "0.00259" } ] }

메시지 상태

GET/sms/{message_id}

메시지 하나의 정보를 반환해요: 번호, 국가, 세그먼트 수, 가격, 상태, 실패 시 오류 내용, 그리고 각 단계의 시간이에요.

캠페인

GET/campaigns/{campaign_id}

캠페인의 비용, 환불 및 각 상태별 메시지 수를 확인할 수 있어요.

GET/campaigns/{campaign_id}/messages?offset=0&limit=100

큐에 들어간 순서대로 최대 1,000개의 메시지

POST/campaigns/{campaign_id}/cancel

예약된 캠페인이 아직 시작되지 않았다면 취소하고 전체 비용을 잔액으로 환불해요.

잔액 및 요금

GET/balance

{ "balance": "248.31500", "currency": "USD" }

GET/rates

공개 API로 키가 필요하지 않아요. 모든 국가의 국가 코드와 세그먼트당 가격을 확인할 수 있어요. /rates/GB를 호출하면 특정 국가의 정보를 반환해요.

{ "updated": "2026-11-07", "rates": [ { "country": "GB", "name": "United Kingdom", "calling_code": "+44", "price": "0.00819", "currency": "USD", "unit": "SMS segment" } ] }

전송 웹훅

콘솔의 API 키 메뉴에서 웹훅 URL을 설정해요. 메시지가 최종 상태(전송 완료 또는 실패)에 도달하면 리포트를 해당 URL로 POST로 보내줘요. 8초 이내에 2xx 상태로 응답하면 돼요. 그렇지 않으면 약 11시간 동안 최대 8번 재시도해요. 이미 처리한 리포트는 id를 사용해서 무시할 수 있어요.

POST https://your-server.example/sms-status
Content-Type: application/json
X-SendBulkSMS-Signature: t=1794047524,v1=5f0c3b9e...

{
  "event": "message.status",
  "id": "evt_8c1d2e4f6a7b9c0d1e2f",
  "created_at": "2026-11-07T10:32:09.004Z",
  "data": {
    "message_id": "SM-2RBW-8MZN-QF6K",
    "campaign_id": "CP-7KQ2-M9XA-4HTD",
    "to": "+447700900461",
    "country": "GB",
    "status": "delivered",
    "error": null,
    "parts": 1,
    "price": "0.00819",
    "client_ref": "orders-4821",
    "done_at": "2026-11-07T10:32:08.912Z"
  }
}

보고서를 신뢰하기 전에 서명을 확인하세요: 웹훅 비밀 키로 t + "." + raw body의 HMAC-SHA256을 상수 시간에 비교하고 5분 이상 지난 시간을 거부하세요.

import { createHmac, timingSafeEqual } from "node:crypto";

// raw = the request body exactly as received (string), header = X-SendBulkSMS-Signature
export function verify(raw, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = Buffer.from(createHmac("sha256", secret).update(m[1] + "." + raw).digest("hex"));
  const got = Buffer.from(m[2]);
  return want.length === got.length && timingSafeEqual(want, got);
}

상태

상태의미
queued수락되어 요금이 청구되었으며, 대기열에서 순서를 기다리고 있어요
sent경로로 전달되었으며, 전송 리포트를 기다리는 중이에요
delivered네트워크에서 핸드폰으로 전송이 확인되었어요
failed전달되지 않았어요. 요금은 환불되고 error에 이유가 담겨요
canceled예약된 캠페인이 시작되기 전에 취소되어 환불되었어요

오류

에러는 HTTP 상태 코드와 JSON 본문을 사용해요: { "error": { "code": "insufficient_balance", "message": "..." } }. 에러가 발생한 요청은 잔액에서 요금이 청구되지 않아요.

HTTP코드
400bad_request필드가 없거나 유효하지 않아요. 메시지에 어떤 필드인지 설명돼요
400no_valid_numbers"to"에 있는 번호 중 사용 가능한 번호가 없어요
401unauthorizedAPI 키가 없거나 유효하지 않아요
402insufficient_balance잔액이 부족해서 전송이 안 돼요; 아무것도 청구되지 않았어요
404not_found캠페인, 메시지 또는 엔드포인트가 계정에 존재하지 않아요
409not_scheduled아직 시작하지 않은 예약된 캠페인만 취소할 수 있어요
413too_many_recipients한 요청의 번호가 10,000개를 넘었어요
429rate_limited이 키로 1분에 300번 이상 요청했어요
500server_error다시 시도해 보세요. 실패한 요청에는 요금이 부과되지 않아요

제한

  • API 키당 분당 300개의 요청.
  • 전송 요청당 10,000개의 번호; 메시지당 10개의 세그먼트.
  • 메시지는 분당 최대 200건까지 큐에 따라 순차적으로 발송돼요.
  • 5분 후부터 60일 후까지 예약할 수 있어요.
  • 발송 제외 목록에 있는 번호는 건너뛰고 dropped에 코드 suppressed로 반환돼요. 요금은 청구되지 않아요.
대량 SMS API 문서: REST API로 SMS 보내기 | SendBulkSMS