TypeScript SDK
Official TypeScript / Node.js client library for RelayAPI. Post to 22 platforms with a few lines of code.
Official TypeScript SDK for RelayAPI. This is the canonical, current client maintained with the repository's OpenAPI contract. It is fully typed, works in Node.js, Bun, Deno, Cloudflare Workers, and the browser, and covers the complete generated API surface.
Setup
Install
npm install @relayapi/sdkAuthenticate
import Relay from '@relayapi/sdk';
const client = new Relay(); // reads RELAY_API_KEY from environmentOr pass the key directly:
const client = new Relay({ apiKey: 'rlay_live_...' });Get your API key from relayapi.dev under Settings > API Keys.
Quick Examples
Create and Publish a Post
const post = await client.posts.create({
content: 'Hello from the TypeScript SDK!',
targets: ['twitter', 'linkedin'],
scheduled_at: 'now',
});
console.log(`Post created: ${post.id} — status: ${post.status}`);Schedule a Post
const post = await client.posts.create({
content: 'This will go out tomorrow morning',
targets: ['twitter', 'linkedin'],
scheduled_at: '2025-01-15T09:00:00Z',
timezone: 'America/New_York',
});Save as Draft
const post = await client.posts.create({
content: 'Work in progress...',
targets: ['instagram'],
scheduled_at: 'draft',
});List Connected Accounts
const accounts = await client.accounts.list();
for (const acc of accounts.data) {
console.log(`${acc.platform}: ${acc.display_name} (${acc.id})`);
}Upload Media
import { readFile } from 'node:fs/promises';
const file = await readFile('photo.jpg');
const session = await client.media.createUploadSession({
filename: 'photo.jpg',
content_type: 'image/jpeg',
size_bytes: file.byteLength,
});
if (session.mode !== 'single' || !session.upload) {
throw new Error('Use the multipart flow for files larger than 64 MiB');
}
const upload = await fetch(session.upload.url, {
method: 'PUT',
body: file,
headers: session.upload.headers,
});
if (!upload.ok) throw new Error(`Media upload failed: ${upload.status}`);
const confirmed = await client.media.completeUploadSession(session.id, {
parts: [],
});
if (!confirmed.reference_url) {
throw new Error('Ready media attachment URL is unavailable');
}
const post = await client.posts.create({
content: 'Check out this photo!',
targets: ['instagram'],
media: [{ url: confirmed.reference_url, type: 'image' }],
scheduled_at: 'now',
});Upload sessions accept direct-to-storage objects up to 200 MiB and switch to
16 MiB multipart parts above 64 MiB. See Media Uploads
for multipart retry/resume, automatic normalization, and custom covers. The
legacy getPresignURL() / confirm() pair remains available for single-PUT
clients; the Worker-proxy upload() method remains capped at 50 MiB. Persist
reference_url for post attachments; url is a read URL and may expire.
Cross-Post with Per-Platform Content
const post = await client.posts.create({
content: 'Default content for all platforms',
targets: ['twitter', 'linkedin', 'bluesky'],
target_options: {
twitter: { content: 'Short version for Twitter' },
linkedin: { content: 'Longer professional version for LinkedIn with more context' },
},
scheduled_at: 'now',
});Get Post Analytics
const analytics = await client.analytics.retrieve({ post_id: 'post_abc123' });
console.log(analytics);Check Account Health
const health = await client.accounts.health.list();
for (const acc of health.data) {
console.log(`${acc.platform} (${acc.username}): ${acc.healthy ? 'healthy' : 'needs attention'}`);
}Error Handling
When the API returns a non-success status code, a subclass of Relay.APIError is thrown:
import Relay from '@relayapi/sdk';
const client = new Relay();
const post = await client.posts
.create({ content: 'Hello!', targets: ['twitter'], scheduled_at: 'now' })
.catch((err) => {
if (err instanceof Relay.APIError) {
console.log(err.status); // 400
console.log(err.name); // BadRequestError
console.log(err.headers);
} else {
throw err;
}
});| Status Code | Error Type |
|---|---|
| 400 | BadRequestError |
| 401 | AuthenticationError |
| 403 | PermissionDeniedError |
| 404 | NotFoundError |
| 422 | UnprocessableEntityError |
| 429 | RateLimitError |
| >=500 | InternalServerError |
| N/A | APIConnectionError |
Retries
Connection errors, 408, 409, 429, and >=500 responses are automatically retried twice by default with exponential backoff. Configure with maxRetries:
// For all requests:
const client = new Relay({ maxRetries: 0 }); // default is 2
// Or per-request:
await client.posts.list({ maxRetries: 5 });Timeouts
Requests time out after 1 minute by default:
const client = new Relay({ timeout: 20 * 1000 }); // 20 seconds
// Override per-request:
await client.posts.list({ timeout: 5 * 1000 });Configuration
| Option / Env Var | Description |
|---|---|
apiKey / RELAY_API_KEY | Your API key (required if not passed to constructor) |
baseURL / RELAY_BASE_URL | Custom API base URL (default: https://api.relayapi.dev) |
maxRetries | Number of automatic retries (default: 2) |
timeout | Request timeout in ms (default: 1 minute) |
logLevel / RELAY_LOG | debug, info, warn (default), error, or off |
Logging
import Relay from '@relayapi/sdk';
const client = new Relay({
logLevel: 'debug', // logs all HTTP requests and responses
});Requirements
TypeScript >= 4.9. Supported runtimes: Node.js 20 LTS or later, Deno v1.28+, Bun 1.0+, Cloudflare Workers, Vercel Edge Runtime, and up-to-date browsers.
Links
Found something wrong? Help us improve this page.