Client SDKs
Official client libraries for Python, JavaScript/TypeScript, and Go.
The Vendel SDKs provide a simple interface for sending SMS, checking quotas, and verifying
webhook signatures. They authenticate with integration API keys (vk_ prefix)
— create one in the Dashboard under Settings → API Keys.
Installation
pip install vendel-sdk npm install vendel-sdk go get github.com/JimScope/vendel-sdk-go
The JS/TS SDK has zero runtime dependencies — uses built-in fetch (Node 18+). TypeScript types included.
Quick start
Send an SMS in three lines:
from vendel_sdk import VendelClient
client = VendelClient("https://app.vendel.cc", "vk_your_api_key")
response = client.send_sms(["+1234567890"], "Hello from Vendel!") import { VendelClient } from "vendel-sdk";
const client = new VendelClient({
baseUrl: "https://app.vendel.cc",
apiKey: "vk_your_api_key",
});
const response = await client.sendSms(["+1234567890"], "Hello from Vendel!"); package main
import (
"context"
"fmt"
vendel "github.com/JimScope/vendel-sdk-go"
)
func main() {
client := vendel.NewClient("https://app.vendel.cc", "vk_your_api_key")
resp, err := client.SendSMS(context.Background(), vendel.SendSMSRequest{
Recipients: []string{"+1234567890"},
Body: "Hello from Vendel!",
})
if err != nil {
panic(err)
}
fmt.Println("Batch ID:", resp.BatchID)
} Send SMS
Send to one or more recipients in E.164 format. Optionally specify a device_id to
route through a particular device.
response = client.send_sms(
recipients=["+1234567890", "+0987654321"],
body="Your order has shipped!",
device_id="optional_device_id",
)
print(f"Batch: {response.batch_id}, Messages: {response.message_ids}") const response = await client.sendSms(
["+1234567890", "+0987654321"],
"Your order has shipped!",
{ deviceId: "optional_device_id" },
);
console.log(`Batch: ${response.batch_id}, Messages: ${response.message_ids}`); resp, err := client.SendSMS(ctx, vendel.SendSMSRequest{
Recipients: []string{"+1234567890", "+0987654321"},
Body: "Your order has shipped!",
DeviceID: "optional_device_id",
})
fmt.Printf("Batch: %s, Messages: %v\n", resp.BatchID, resp.MessageIDs) Send to contact groups
Pass group_ids to include all members of one or more contact groups as recipients.
Group members are merged with any explicit recipients you provide.
response = client.send_sms(
recipients=["+1234567890"],
body="Flash sale starts now!",
group_ids=["group_abc", "group_xyz"],
)
print(f"Sent to {response.recipients_count} recipients") const response = await client.sendSms(
["+1234567890"],
"Flash sale starts now!",
{ groupIds: ["group_abc", "group_xyz"] },
);
console.log(`Sent to ${response.recipients_count} recipients`); resp, err := client.SendSMS(ctx, vendel.SendSMSRequest{
Recipients: []string{"+1234567890"},
Body: "Flash sale starts now!",
GroupIDs: []string{"group_abc", "group_xyz"},
})
fmt.Printf("Sent to %d recipients\n", resp.RecipientsCount) Send SMS with template
Send personalized SMS using a saved template.
Reserved variables ({{name}}, {{phone}}) are auto-filled from your contacts.
Custom variables are passed as a key-value map.
response = client.send_sms_template(
recipients=["+1234567890"],
template_id="template_abc",
variables={"code": "1234", "expiry": "10 minutes"},
group_ids=["vip_customers"],
)
print(f"Batch: {response.batch_id}") const response = await client.sendSmsTemplate(
["+1234567890"],
"template_abc",
{
variables: { code: "1234", expiry: "10 minutes" },
groupIds: ["vip_customers"],
},
);
console.log(`Batch: ${response.batch_id}`); resp, err := client.SendSMSTemplate(ctx, vendel.SendSMSTemplateRequest{
Recipients: []string{"+1234567890"},
TemplateID: "template_abc",
Variables: map[string]string{"code": "1234", "expiry": "10 minutes"},
GroupIDs: []string{"vip_customers"},
})
fmt.Printf("Batch: %s\n", resp.BatchID) Get quota
Check your current plan limits and usage.
quota = client.get_quota()
print(f"Plan: {quota.plan}")
print(f"SMS: {quota.sms_sent_this_month}/{quota.max_sms_per_month}")
print(f"Devices: {quota.devices_registered}/{quota.max_devices}")
print(f"Resets: {quota.reset_date}") const quota = await client.getQuota();
console.log(`Plan: ${quota.plan}`);
console.log(`SMS: ${quota.sms_sent_this_month}/${quota.max_sms_per_month}`);
console.log(`Devices: ${quota.devices_registered}/${quota.max_devices}`);
console.log(`Resets: ${quota.reset_date}`); quota, err := client.GetQuota(ctx)
fmt.Printf("Plan: %s\n", quota.Plan)
fmt.Printf("SMS: %d/%d\n", quota.SMSSentThisMonth, quota.MaxSMSPerMonth)
fmt.Printf("Devices: %d/%d\n", quota.DevicesRegistered, quota.MaxDevices)
fmt.Printf("Resets: %s\n", quota.ResetDate) Get message status
Check the delivery status of a single SMS message by its ID. See Message Status for the full API reference.
status = client.get_message_status("MESSAGE_ID")
print(f"Status: {status.status}")
print(f"Recipient: {status.recipient}")
if status.error_message:
print(f"Error: {status.error_message}") const status = await client.getMessageStatus("MESSAGE_ID");
console.log(`Status: ${status.status}`);
console.log(`Recipient: ${status.recipient}`);
if (status.error_message) {
console.log(`Error: ${status.error_message}`);
} status, err := client.GetMessageStatus(ctx, "MESSAGE_ID")
fmt.Printf("Status: %s\n", status.Status)
fmt.Printf("Recipient: %s\n", status.Recipient)
if status.ErrorMessage != "" {
fmt.Printf("Error: %s\n", status.ErrorMessage)
} Get batch status
When sending to multiple recipients, the response includes a batch_id.
Use it to retrieve the status of all messages in the batch, including aggregate counts per status.
batch = client.get_batch_status("BATCH_ID")
print(f"Total: {batch.total}")
print(f"Status counts: {batch.status_counts}")
for msg in batch.messages:
print(f" {msg.recipient}: {msg.status}") const batch = await client.getBatchStatus("BATCH_ID");
console.log(`Total: ${batch.total}`);
console.log(`Status counts:`, batch.status_counts);
for (const msg of batch.messages) {
console.log(` ${msg.recipient}: ${msg.status}`);
} batch, err := client.GetBatchStatus(ctx, "BATCH_ID")
fmt.Printf("Total: %d\n", batch.Total)
fmt.Printf("Status counts: %v\n", batch.StatusCounts)
for _, msg := range batch.Messages {
fmt.Printf(" %s: %s\n", msg.Recipient, msg.Status)
} List contacts
Retrieve your contacts with optional search and group filtering. Results are paginated (default 50 per page, max 200).
contacts = client.list_contacts(search="Alice", per_page=20)
print(f"Found {contacts.total_items} contacts")
for c in contacts.items:
print(f" {c.name}: {c.phone_number}") const contacts = await client.listContacts({ search: "Alice", perPage: 20 });
console.log(`Found ${contacts.total_items} contacts`);
for (const c of contacts.items) {
console.log(` ${c.name}: ${c.phone_number}`);
} contacts, err := client.ListContacts(ctx, vendel.ListContactsParams{
Search: "Alice",
PerPage: 20,
})
fmt.Printf("Found %d contacts\n", contacts.TotalItems)
for _, c := range contacts.Items {
fmt.Printf(" %s: %s\n", c.Name, c.PhoneNumber)
} List contact groups
Retrieve your contact groups. Use group IDs when sending to entire groups via group_ids.
groups = client.list_contact_groups(per_page=50)
print(f"Found {groups.total_items} groups")
for g in groups.items:
print(f" {g.id}: {g.name}") const groups = await client.listContactGroups({ perPage: 50 });
console.log(`Found ${groups.total_items} groups`);
for (const g of groups.items) {
console.log(` ${g.id}: ${g.name}`);
} groups, err := client.ListContactGroups(ctx, 1, 50)
fmt.Printf("Found %d groups\n", groups.TotalItems)
for _, g := range groups.Items {
fmt.Printf(" %s: %s\n", g.ID, g.Name)
} Webhook verification
When you receive a webhook, verify the X-Webhook-Signature header to ensure
it came from Vendel. The signature is an HMAC-SHA256 hex digest of the request body.
from vendel_sdk import verify_webhook_signature
@app.route("/webhook", methods=["POST"])
def webhook():
signature = request.headers.get("X-Webhook-Signature", "")
is_valid = verify_webhook_signature(
payload=request.get_data(as_text=True),
signature=signature,
secret="your_webhook_secret",
)
if not is_valid:
return "Invalid signature", 401
event = request.json
print(f"Event: {event['event']}")
return "OK", 200 import { verifyWebhookSignature } from "vendel-sdk";
import express from "express";
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhook", (req, res) => {
const signature = req.headers["x-webhook-signature"] as string;
const isValid = verifyWebhookSignature(
req.body.toString(),
signature,
"your_webhook_secret",
);
if (!isValid) return res.status(401).send("Invalid signature");
const event = JSON.parse(req.body.toString());
console.log("Event:", event.event);
res.sendStatus(200);
}); import (
"io"
"net/http"
vendel "github.com/JimScope/vendel-sdk-go"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
signature := r.Header.Get("X-Webhook-Signature")
if !vendel.VerifyWebhookSignature(string(body), signature, "your_webhook_secret") {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Process the event...
w.WriteHeader(http.StatusOK)
} Error handling
All SDKs throw typed errors. Quota errors (HTTP 429) include limit, used,
and available fields so you can show meaningful messages to users.
from vendel_sdk import VendelClient, VendelAPIError, VendelQuotaError
try:
client.send_sms(["+1234567890"], "Hello")
except VendelQuotaError as e:
print(f"Quota exceeded: {e.used}/{e.limit} used, {e.available} remaining")
except VendelAPIError as e:
print(f"API error [{e.status_code}]: {e.message}") import { VendelClient, VendelQuotaError, VendelAPIError } from "vendel-sdk";
try {
await client.sendSms(["+1234567890"], "Hello");
} catch (e) {
if (e instanceof VendelQuotaError) {
console.log(`Quota exceeded: ${e.used}/${e.limit} used, ${e.available} remaining`);
} else if (e instanceof VendelAPIError) {
console.log(`API error [${e.statusCode}]: ${e.message}`);
}
} resp, err := client.SendSMS(ctx, req)
if err != nil {
if vendel.IsQuotaError(err) {
qe := err.(*vendel.QuotaError)
fmt.Printf("Quota exceeded: %d/%d used, %d remaining\n", qe.Used, qe.Limit, qe.Available)
} else if vendel.IsAPIError(err) {
ae := err.(*vendel.VendelError)
fmt.Printf("API error [%d]: %s\n", ae.StatusCode, ae.Message)
} else {
fmt.Printf("Network error: %v\n", err)
}
}