RelayAPI

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/sdk

Authenticate

import Relay from '@relayapi/sdk';

const client = new Relay(); // reads RELAY_API_KEY from environment

Or 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 CodeError Type
400BadRequestError
401AuthenticationError
403PermissionDeniedError
404NotFoundError
422UnprocessableEntityError
429RateLimitError
>=500InternalServerError
N/AAPIConnectionError

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 VarDescription
apiKey / RELAY_API_KEYYour API key (required if not passed to constructor)
baseURL / RELAY_BASE_URLCustom API base URL (default: https://api.relayapi.dev)
maxRetriesNumber of automatic retries (default: 2)
timeoutRequest timeout in ms (default: 1 minute)
logLevel / RELAY_LOGdebug, 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.

Found something wrong? Help us improve this page.

On this page