Skip to documentation
Get started Login

SMS API · v1

Ship SMS in minutes, not days

Messages that Move Mountains

One HTTPS endpoint. Hashed API keys. JSON in, JSON out. Copy a snippet, try it live, then go to production.

TLSRequired
API keysNever passwords
OpenAPI 3Machine-readable
curl -X POST "https://app.connectmedia.co.ke/api.php" \
  -H "Authorization: Bearer $CM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "send",
    "to": "254712345678",
    "sender": "ConectMedia",
    "message": "Hello from Connect Media"
  }'

Trusted by teams that send at scale

Hashed API keys

Keys are bcrypt-hashed at rest, shown once, and revocable from the dashboard.

One endpoint

JSON or form POST. Four actions cover send, credits, history, and inbound SMS.

OpenAPI 3

Import the spec into Postman, Insomnia, or generate a client in your language.

Quickstart

The Connect Media SMS API is a single HTTPS endpoint. Authenticate with an API key, pick an action, and parse the JSON envelope.

  1. Create an account

    Register, then open Profile → API keys in the dashboard. Register · Manage keys

  2. Generate a key

    The raw 64-character hex key is shown once. Store it as CM_API_KEY. Hashes are stored server-side; we never keep the plaintext.

  3. Send your first message

    POST JSON to the production endpoint. Recipients must include the country code (E.164 without +).

export CM_API_KEY="your-64-char-hex-key"
curl -X POST "https://app.connectmedia.co.ke/api.php" \
  -H "Authorization: Bearer $CM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"balance"}'

Authentication

Every request requires a valid API key over TLS. Dashboard passwords are never accepted on the wire.

Preferred: Bearer token

Authorization: Bearer YOUR_API_KEY

Also accepted

Location Name
HeaderX-Api-Key
JSON or form bodyapi_key
Query stringapi_key — avoid; keys leak in logs
Security Do not embed keys in front-end JavaScript, mobile apps you cannot update, or public repos. Rotate by generating a new key and revoking the old one from Profile → API keys. Non-TLS requests are rejected.

Endpoint

Base URL

POST https://app.connectmedia.co.ke/api.php

POST is recommended. GET is accepted for read actions. Send either application/json or application/x-www-form-urlencoded. All actions share this URL.

Envelope

Responses are JSON. Treat the envelope code as the source of truth — HTTP status is typically 200 even when an application error is returned.

{
  "code": "201",
  "message": "Message Sent Successfully via Proxy!",
  "data": {},
  "api_version": "8.2.1"
}

Actions

Action Use Success code
sendSend SMS to one or more recipients201
balanceRead remaining credits200
historyList outbound messages202
inboxList inbound (2-way) messages302

Parameters

Send UTF-8 text as-is. Do not pre-urlencode the message body — the client library already encodes form fields.

Field Type Required Notes
api_keystringYes*Omit if using Authorization or X-Api-Key
actionenumYessend · balance · history · inbox
tostringSendE.164 without +. Comma-separated for bulk. Alias: destination
senderstringNoMax 11 alphanumeric. Default ConectMedia. Alias: source
messagestringSendSMS body. Alias: message-box
schedule0 | 1No1 queues instead of sending immediately
schedule_datetimedatetimeIf scheduledMySQL datetime, Africa/Nairobi
limitintegerNoHistory/inbox. Default 50.
offsetintegerNoSkip N records. Default 0.
start_date / end_datedateNoYYYY-MM-DD

Errors, limits, and conventions

How to handle errors

Parse JSON, then branch on code. Retry 500s with exponential backoff. Do not retry 100/401 (rotate the key) or 101 (top up balance).

Phone numbers

Use country code + national number with no plus, spaces, or dashes. Kenya example: 254712345678. Invalid or empty recipients return 104.

Pagination

history and inbox accept limit and offset. Page until the messages array is shorter than limit.

Console rate limit

The in-browser console is proxied and capped at 10 requests per IP per minute (HTTP 429). Production api.php traffic from your servers is not subject to that console cap.

Idempotency

Retries of send can produce duplicate SMS. Deduplicate on your side before retrying a successful 201, or wait until you have a timeout with no envelope.

Response codes

Application codes live in the JSON envelope. HTTP status is usually 200.

Code Meaning Action
100Invalid or missing API keyAuth
101Insufficient balanceSend
102Rejected (content, account hold, or gateway busy)Send
103No action definedAll
104No valid recipientsSend
105Message missingSend
106Message contains banned wordsSend
200Balance retrievedBalance
201Message accepted for deliverySend
202History retrievedHistory
302Inbox retrieved (including empty)Inbox
401Unauthorized (legacy key error on some paths)Auth
500Server or gateway failureAll

Examples

The following examples demonstrate how to use the ConnectMedia API for various operations.

Send SMS

Comma-separate recipients for bulk. Each successful send deducts credits.

POST https://app.connectmedia.co.ke/api.php
{
  "action": "send",
  "to": "254712345678,254722000000",
  "sender": "ConectMedia",
  "message": "Hello from Connect Media"
}

Success

{
  "code": "201",
  "message": "Message Sent Successfully via Proxy!",
  "data": {
    "recipients": 2,
    "balance": 1249,
    "batch_id": 12345
  },
  "api_version": "8.2.1"
}

Insufficient balance

{
  "code": "101",
  "message": "No enough balance to send message"
}

Check balance

POST https://app.connectmedia.co.ke/api.php
{ "action": "balance" }
{
  "code": "200",
  "message": "Balance retrieved successfully",
  "data": {
    "balance": 1250.5,
    "currency": "KES"
  },
  "api_version": "8.2.1"
}

Message history

{
  "action": "history",
  "limit": 10,
  "offset": 0,
  "start_date": "2026-01-01",
  "end_date": "2026-12-31"
}
{
  "code": "202",
  "message": "Message History Retrieved Successfully",
  "data": {
    "messages": [
      {
        "id": 12345,
        "message": "Hello from Connect Media!",
        "sender": "ConectMedia",
        "msisdn": 254712345678,
        "status": "PENDING",
        "created": "2026-09-14 10:30:45"
      }
    ]
  },
  "api_version": "8.2.1"
}

https://app.connectmedia.co.ke/api.php

Inbox (2-way SMS)

{
  "action": "inbox",
  "limit": 10
}
{
  "code": "302",
  "message": "Inbox Data Retrieved Successfully",
  "data": {
    "messages": [
      {
        "id": 1,
        "sender": "254712345678",
        "message": "Yes, please proceed with the order",
        "received_at": "2026-09-14 11:45:22"
      }
    ]
  },
  "api_version": "8.2.1"
}
{
  "code": "302",
  "message": "No messages found"
}

Official SDKs

Install a maintained client library instead of writing HTTP calls yourself.

  • PHP: composer require connectmedia/sms · Packagist · GitHub
  • Python: pip install connectmedia-sms · PyPI · GitHub
  • Node.js: npm install connectmedia-sms · npm · GitHub

Copy-paste clients

Use environment variables. These snippets talk to production over TLS with Bearer auth.

PHP

<?php
class ConnectMediaAPI {
    private $apiKey;
    private $baseUrl;

    public function __construct($apiKey, $baseUrl = 'https://app.connectmedia.co.ke/api.php') {
        $this->apiKey = $apiKey;
        $this->baseUrl = $baseUrl;
    }

    public function request(string $action, array $params = []): array {
        $ch = curl_init($this->baseUrl);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $this->apiKey,
                'Content-Type: application/json',
                'Accept: application/json',
            ],
            CURLOPT_POSTFIELDS => json_encode(array_merge(['action' => $action], $params)),
            CURLOPT_TIMEOUT => 30,
        ]);
        $raw = curl_exec($ch);
        $err = curl_error($ch);
        curl_close($ch);
        if ($err) {
            return ['ok' => false, 'error' => $err];
        }
        $json = json_decode($raw, true);
        return ['ok' => isset($json['code']) && in_array($json['code'], ['200','201','202','302'], true), 'data' => $json];
    }

    public function sendSms(string $to, string $message, string $sender = 'ConectMedia'): array {
        return $this->request('send', compact('to', 'message', 'sender'));
    }
}

$api = new ConnectMediaAPI(getenv('CM_API_KEY'));
print_r($api->sendSms('254712345678', 'Hello from Connect Media'));

Node.js

export class ConnectMediaAPI {
  constructor(apiKey, baseUrl = 'https://app.connectmedia.co.ke/api.php') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async request(action, params = {}) {
    const res = await fetch(this.baseUrl, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ action, ...params }),
    });
    const data = await res.json();
    return { ok: ['200', '201', '202', '302'].includes(String(data.code)), data };
  }

  sendSms(to, message, sender = 'ConectMedia') {
    return this.request('send', { to, message, sender });
  }
}

const api = new ConnectMediaAPI(process.env.CM_API_KEY);
const result = await api.sendSms('254712345678', 'Hello from Connect Media');
console.log(result);

Python

import os
import requests

class ConnectMediaAPI:
    def __init__(self, api_key, base_url='https://app.connectmedia.co.ke/api.php'):
        self.api_key = api_key
        self.base_url = base_url

    def request(self, action, **params):
        r = requests.post(
            self.base_url,
            headers={'Authorization': f'Bearer {self.api_key}'},
            json={'action': action, **params},
            timeout=30,
        )
        data = r.json()
        return {'ok': str(data.get('code')) in {'200', '201', '202', '302'}, 'data': data}

    def send_sms(self, to, message, sender='ConectMedia'):
        return self.request('send', to=to, message=message, sender=sender)

api = ConnectMediaAPI(os.environ['CM_API_KEY'])
print(api.send_sms('254712345678', 'Hello from Connect Media'))

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    body, _ := json.Marshal(map[string]string{
        "action":  "send",
        "to":      "254712345678",
        "sender":  "ConectMedia",
        "message": "Hello from Connect Media",
    })
    req, _ := http.NewRequest("POST", "https://app.connectmedia.co.ke/api.php", bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("CM_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    res, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()
    fmt.Println(res.Status)
}

API console

Runs against production through a same-origin proxy. Use a dedicated test key. 10 requests per IP per minute.

Generated request

Changelog

  • 2026-09-14 — API keys only. Username/password on the wire is retired. Bearer and X-Api-Key headers are supported. Docs, OpenAPI, and console updated to match.
  • 1.0 — Single-endpoint send, balance, history, and inbox with JSON envelope.