SDKs
Official SDKs wrap every endpoint in these docs except the Admin endpoints. Using one saves you from dealing with the API's quirks yourself:
- Errors are exceptions. Most endpoints answer a failure with HTTP 200 and a sentence such as
List does not exist(see Authentication). The SDKs recognise these messages and throw a typed error with a stable code, such aslist_not_found, so you never have to match strings. - Results are parsed. Plain-text and JSON responses become native values, and an empty result (
No lists found) is an empty list rather than an error. - Reads are retried, writes never are. A read that hits a dropped connection or a 429/502/503/504 is retried, honouring
Retry-After. Sends and other writes are not, because the API has no idempotency keys and a retried send could go out twice. - Request quirks are handled for you: the form vs JSON bodies, the
boolean=trueflag on subscribe, comma-joined id lists, and the 250-recipient limit on Send email, which is checked before anything is sent.
| Language | Package | Version | Source |
|---|---|---|---|
| TypeScript / JavaScript | @skrybe/sdk on npm | 2.0.0 | skry-be/skrybe-sdk-ts |
| PHP | skrybe/sdk-php on Packagist | 2.0.0 | skry-be/skrybe-sdk-php |
| Python | skrybe-sdk on PyPI | 2.0.0 | skry-be/skrybe-sdk-py |
| Java | com.github.skry-be:skrybe-sdk-java on JitPack | 2.0.0 | skry-be/skrybe-sdk-java |
Use a brand's API key with the SDKs, and keep it on your server.
Install
- TypeScript
- PHP
- Python
- Java
npm install @skrybe/sdk
Node.js 18 or newer. No runtime dependencies, with ESM and CommonJS builds.
composer require skrybe/sdk-php
PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12 or newer within those branches. Uses Guzzle 7.
pip install skrybe-sdk
Python 3.10 or newer. Imports as skrybe; uses requests and certifi 2026.10.19 or newer.
Add JitPack as a repository, then the dependency. Maven:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.skry-be</groupId>
<artifactId>skrybe-sdk-java</artifactId>
<version>v2.0.0</version>
</dependency>
Gradle:
repositories { maven { url 'https://jitpack.io' } }
dependencies { implementation 'com.github.skry-be:skrybe-sdk-java:v2.0.0' }
Java 11 or newer. Uses the JDK's HttpClient and Jackson.
Quick start
- TypeScript
- PHP
- Python
- Java
import { Skrybe } from '@skrybe/sdk'
const skrybe = new Skrybe({ apiKey: process.env.SKRYBE_API_KEY! })
await skrybe.emails.sendTransactional({
toEmail: 'ada@example.com',
subject: 'Your code',
plainBody: 'Your code is 123456',
})
const lists = await skrybe.lists.list() // [{ id, name }, ...]
use Skrybe\Skrybe;
$skrybe = new Skrybe(getenv('SKRYBE_API_KEY'));
$skrybe->emails->sendTransactional([
'toEmail' => 'ada@example.com',
'subject' => 'Your code',
'plainBody' => 'Your code is 123456',
]);
$lists = $skrybe->lists->list(); // [['id' => ..., 'name' => ...], ...]
import os
from skrybe import Skrybe
skrybe = Skrybe(os.environ["SKRYBE_API_KEY"])
skrybe.emails.send_transactional(
to_email="ada@example.com",
subject="Your code",
plain_body="Your code is 123456",
)
lists = skrybe.lists.list() # [{"id": ..., "name": ...}, ...]
import com.skrybe.sdk.Skrybe;
import com.skrybe.sdk.params.SendTransactionalParams;
Skrybe skrybe = new Skrybe(System.getenv("SKRYBE_API_KEY"));
skrybe.emails().sendTransactional(SendTransactionalParams.builder()
.toEmail("ada@example.com")
.subject("Your code")
.plainBody("Your code is 123456")
.build());
var lists = skrybe.lists().list(); // typed SkrybeList objects: getId(), getName()
Each client also takes a base URL (default https://dashboard.skry.be), a timeout and a retry count. See each SDK's README for all of the options.
A campaign from start to finish
- TypeScript
- PHP
- Python
- Java
const { campaignId } = await skrybe.campaigns.create({
fromName: 'Ada', fromEmail: 'ada@example.com', replyTo: 'ada@example.com',
title: 'October news', subject: 'What we shipped', htmlText: html,
})
const id = Number(campaignId)
await skrybe.campaigns.testSend(id, ['me@example.com'])
const { recipients } = await skrybe.campaigns.send(id, { listIds: [listId], dryRun: true })
await skrybe.campaigns.send(id, { listIds: [listId] })
const stats = await skrybe.campaigns.stats(id)
$created = $skrybe->campaigns->create([
'fromName' => 'Ada', 'fromEmail' => 'ada@example.com', 'replyTo' => 'ada@example.com',
'title' => 'October news', 'subject' => 'What we shipped', 'htmlText' => $html,
]);
$id = (int) $created['campaignId'];
$skrybe->campaigns->testSend($id, ['me@example.com']);
$dry = $skrybe->campaigns->send($id, ['listIds' => [$listId], 'dryRun' => true]);
$skrybe->campaigns->send($id, ['listIds' => [$listId]]);
$stats = $skrybe->campaigns->stats($id);
created = skrybe.campaigns.create(
from_name="Ada", from_email="ada@example.com", reply_to="ada@example.com",
title="October news", subject="What we shipped", html_text=html,
)
campaign_id = int(created["campaign_id"])
skrybe.campaigns.test_send(campaign_id, ["me@example.com"])
dry = skrybe.campaigns.send(campaign_id, list_ids=[list_id], dry_run=True)
skrybe.campaigns.send(campaign_id, list_ids=[list_id])
stats = skrybe.campaigns.stats(campaign_id)
CreateCampaignResult created = skrybe.campaigns().create(CreateCampaignParams.builder()
.fromName("Ada").fromEmail("ada@example.com").replyTo("ada@example.com")
.title("October news").subject("What we shipped").htmlText(html)
.build());
int id = Integer.parseInt(created.getCampaignId());
skrybe.campaigns().testSend(id, "me@example.com");
skrybe.campaigns().send(id, Recipients.builder().listIds(listId).dryRun(true).build());
skrybe.campaigns().send(id, Recipients.builder().listIds(listId).build());
CampaignStats stats = skrybe.campaigns().stats(id);
Errors
Every failure is one error type with a subclass for its kind:
| Kind | TypeScript and Python | PHP and Java | When |
|---|---|---|---|
| Authentication | AuthenticationError | AuthenticationException | The key is missing or wrong (invalid_api_key). |
| Invalid request | InvalidRequestError | InvalidRequestException | The request is wrong and will fail again unchanged, e.g. list_id_invalid, invalid_schedule. |
| Rate limit | RateLimitError | RateLimitException | A quota or rate limit was hit: quota_exceeded, rate_limited. |
| Connection | ConnectionError | ConnectionException | The API could not be reached (network_error) or did not answer in time (timeout). |
| API | ApiError | ApiException | Anything else the API refused, e.g. campaign_not_found, campaign_not_draft, domain_not_verified. |
They all extend SkrybeError (TypeScript and Python) or SkrybeException (PHP and Java), and carry a stable code, a hint about what to do when one is known, the HTTP status and the raw response body.
- TypeScript
- PHP
- Python
- Java
import { RateLimitError, SkrybeError } from '@skrybe/sdk'
try {
await skrybe.campaigns.send(id, { listIds: [listId] })
} catch (err) {
if (err instanceof RateLimitError) {
// quota_exceeded or rate_limited
} else if (err instanceof SkrybeError && err.code === 'campaign_not_draft') {
// already sent
} else {
throw err
}
}
use Skrybe\Exception\ApiException;
use Skrybe\Exception\RateLimitException;
try {
$skrybe->campaigns->send($id, ['listIds' => [$listId]]);
} catch (RateLimitException $e) {
// quota_exceeded or rate_limited
} catch (ApiException $e) {
if ($e->getErrorCode() === 'campaign_not_draft') {
// already sent
}
}
from skrybe import RateLimitError, SkrybeError
try:
skrybe.campaigns.send(campaign_id, list_ids=[list_id])
except RateLimitError:
... # quota_exceeded or rate_limited
except SkrybeError as err:
if err.code == "campaign_not_draft":
... # already sent
else:
raise
import com.skrybe.sdk.exception.ApiException;
import com.skrybe.sdk.exception.RateLimitException;
try {
skrybe.campaigns().send(id, Recipients.builder().listIds(listId).build());
} catch (RateLimitException e) {
// quota_exceeded or rate_limited
} catch (ApiException e) {
if (e.getErrorCode().equals("campaign_not_draft")) {
// already sent
}
}
The exceptions are unchecked.
Methods and endpoints
Method names are the same in every SDK, written in each language's style.
| Endpoint | TypeScript | PHP | Python | Java |
|---|---|---|---|---|
| Send email | emails.send() | emails->send() | emails.send() | emails().send() |
| Send transactional | emails.sendTransactional() | emails->sendTransactional() | emails.send_transactional() | emails().sendTransactional() |
| Create campaign | campaigns.create() | campaigns->create() | campaigns.create() | campaigns().create() |
| Get campaigns | campaigns.list(), listAll() | campaigns->list(), listAll() | campaigns.list(), list_all() | campaigns().list(), listAll() |
| Get campaign | campaigns.get() | campaigns->get() | campaigns.get() | campaigns().get() |
| Campaign stats | campaigns.stats() | campaigns->stats() | campaigns.stats() | campaigns().stats() |
| Send campaign | campaigns.send() | campaigns->send() | campaigns.send() | campaigns().send() |
| Test send | campaigns.testSend() | campaigns->testSend() | campaigns.test_send() | campaigns().testSend() |
| Schedule | campaigns.schedule() | campaigns->schedule() | campaigns.schedule() | campaigns().schedule() |
| Unschedule | campaigns.unschedule() | campaigns->unschedule() | campaigns.unschedule() | campaigns().unschedule() |
| Update campaign | campaigns.update() | campaigns->update() | campaigns.update() | campaigns().update() |
| Duplicate campaign | campaigns.duplicate() | campaigns->duplicate() | campaigns.duplicate() | campaigns().duplicate() |
| Delete campaign | campaigns.delete() | campaigns->delete() | campaigns.delete() | campaigns().delete() |
| Campaign activity | campaigns.activity(), activityAll() | campaigns->activity(), activityAll() | campaigns.activity(), activity_all() | campaigns().activity(), activityAll() |
| Get lists | lists.list() | lists->list() | lists.list() | lists().list() |
| Get list | lists.get() | lists->get() | lists.get() | lists().get() |
| Create list | lists.create() | lists->create() | lists.create() | lists().create() |
| Update list | lists.update() | lists->update() | lists.update() | lists().update() |
| Delete list | lists.delete() | lists->delete() | lists.delete() | lists().delete() |
| Add custom field | lists.addCustomField() | lists->addCustomField() | lists.add_custom_field() | lists().addCustomField() |
| Rename custom field | lists.renameCustomField() | lists->renameCustomField() | lists.rename_custom_field() | lists().renameCustomField() |
| Delete custom field | lists.deleteCustomField() | lists->deleteCustomField() | lists.delete_custom_field() | lists().deleteCustomField() |
| Active subscriber count | lists.activeSubscriberCount() | lists->activeSubscriberCount() | lists.active_subscriber_count() | lists().activeSubscriberCount() |
| Subscribe | subscribers.subscribe() | subscribers->subscribe() | subscribers.subscribe() | subscribers().subscribe() |
| Unsubscribe | subscribers.unsubscribe() | subscribers->unsubscribe() | subscribers.unsubscribe() | subscribers().unsubscribe() |
| Delete subscriber | subscribers.delete() | subscribers->delete() | subscribers.delete() | subscribers().delete() |
| Subscription status | subscribers.status() | subscribers->status() | subscribers.status() | subscribers().status() |
| Get subscribers | subscribers.list(), listAll() | subscribers->list(), listAll() | subscribers.list(), list_all() | subscribers().list(), listAll() |
| Get templates | templates.list() | templates->list() | templates.list() | templates().list() |
| Get template | templates.get() | templates->get() | templates.get() | templates().get() |
| Create template | templates.create() | templates->create() | templates.create() | templates().create() |
| Update template | templates.update() | templates->update() | templates.update() | templates().update() |
| Delete template | templates.delete() | templates->delete() | templates.delete() | templates().delete() |
| Get autoresponders | autoresponders.list() | autoresponders->list() | autoresponders.list() | autoresponders().list() |
| Get autoresponder emails | autoresponders.get() | autoresponders->get() | autoresponders.get() | autoresponders().get() |
| Autoresponder stats | autoresponders.emailStats() | autoresponders->emailStats() | autoresponders.email_stats() | autoresponders().emailStats() |
| Create autoresponder | autoresponders.create() | autoresponders->create() | autoresponders.create() | autoresponders().create() |
| Create autoresponder email | autoresponders.createEmail() | autoresponders->createEmail() | autoresponders.create_email() | autoresponders().createEmail() |
| Get brands | brands.list() | brands->list() | brands.list() | brands().list() |
| Emails sent | stats.totalEmailsSent() | stats->totalEmailsSent() | stats.total_emails_sent() | stats().totalEmailsSent() |
Parameters are camelCase in the TypeScript and PHP SDKs (fromEmail, listIds) where the endpoints take snake_case (from_email, list_ids); Python uses snake_case keyword arguments, and Java uses builders such as CreateCampaignParams.builder(). Results come back shaped as each endpoint's page describes.
Upgrading from 1.x
2.0 of each SDK is a rewrite (Python's previous release was 0.1.0, and its import package is now skrybe rather than skrybe_sdk; Java's Maven groupId is now com.github.skry-be, as JitPack serves it): methods moved onto resources (skrybe.campaigns.create() instead of sdk.createCampaign()), results are parsed, and failures throw instead of being returned. Each SDK's README has a table mapping every 1.x method to its replacement.