Skip to content

Repository files navigation

PostZen

PostZen Node.js SDK

npm version license

One API to post everywhere. 8 platforms, zero headaches.

The official Node.js SDK for the PostZen API — schedule and publish social media posts across X/Twitter, Instagram, TikTok, LinkedIn, Facebook, YouTube, Threads, and Pinterest with a single integration.

Installation

npm install @postzen/node

Requires Node 18+. Ships CommonJS and ESM builds with TypeScript declarations, and has zero runtime dependencies (it uses the built-in fetch).

Quick Start

Create an API key on the API keys page and expose it as POSTZEN_API_KEY:

export POSTZEN_API_KEY="pzn_live_..."

Then create and publish a post:

import PostZen from '@postzen/node';

const postzen = new PostZen();

const { data } = await postzen.posts.createPost({
  body: {
    title: 'Launch post',
    content: 'We shipped the new release.',
    publishNow: true,
    platforms: [
      {
        platform: 'twitter',
        accountId: 'account_id',
      },
    ],
  },
});

if (data && 'post' in data) {
  console.log(`Created post ${data.post._id}`);
}

Authentication

The client authenticates with a bearer API key, resolved in this order:

  1. The apiKey option passed to the constructor.
  2. The POSTZEN_API_KEY environment variable.

If neither is set, the constructor throws a PostZenApiError with code missing_api_key.

import PostZen from '@postzen/node';

// Explicit key
const postzen = new PostZen({ apiKey: 'pzn_live_...' });

// Or rely on POSTZEN_API_KEY in the environment
const fromEnv = new PostZen();

Configuration

import PostZen from '@postzen/node';

const postzen = new PostZen({
  apiKey: 'pzn_live_...',
  baseURL: 'https://api.postzen.dev',
  timeout: 60000,
  defaultHeaders: {
    'X-Request-Source': 'my-app',
  },
});
Option Type Default Description
apiKey string process.env.POSTZEN_API_KEY PostZen API key used as a bearer token.
baseURL string | null https://api.postzen.dev Override the API base URL.
timeout number 60000 Request timeout in milliseconds. The client aborts timed-out requests.
defaultHeaders Record<string, string> {} Headers sent with every API request.

Examples

Create a Profile

const { data } = await postzen.profiles.createProfile({
  body: {
    name: 'Marketing Team',
    description: 'Profile for marketing campaigns',
    color: '#4caf50',
  },
});

console.log(data.profile._id);

Connect URL Flow

const profileId = 'profile_id';

const { data: start } = await postzen.connect.createConnectUrl({
  path: {
    platform: 'linkedin',
  },
  query: {
    profileId,
    redirectUrl: 'https://yourapp.example/connected',
  },
});

console.log(start.authUrl);

await postzen.connect.completeConnect({
  path: {
    platform: 'linkedin',
  },
  body: {
    code: 'oauth_code_from_redirect',
    state: start.state,
    profileId,
  },
});

Media Presign Upload

const file = await fetch('https://example.com/image.png').then((response) => response.blob());

const { data: presign } = await postzen.media.createMediaPresign({
  body: {
    filename: 'image.png',
    contentType: 'image/png',
    size: file.size,
  },
});

await fetch(presign.uploadUrl, {
  method: 'PUT',
  headers: {
    'Content-Type': 'image/png',
  },
  body: file,
});

await postzen.posts.createPost({
  body: {
    title: 'Post with media',
    content: 'Uploaded through a PostZen presigned URL.',
    publishNow: true,
    mediaItems: [
      {
        url: presign.publicUrl,
        title: 'image.png',
      },
    ],
    platforms: [
      {
        platform: 'instagram',
        accountId: 'account_id',
      },
    ],
  },
});

Upload media

media.upload is a one-step helper: it presigns, uploads the raw bytes to the returned storage URL, and resolves with the public URL and metadata. Pass a file path (the content type is inferred from the extension) or raw bytes with an explicit filename.

// From a file path — content type inferred from the extension.
const media = await postzen.media.upload('./photo.jpg');

// From a Buffer or Uint8Array — filename is required.
const fromBytes = await postzen.media.upload(buffer, { filename: 'photo.jpg' });

// media => { publicUrl, key, type, size, filename }
console.log(media.publicUrl);

Reference the returned publicUrl in posts.createPost:

const media = await postzen.media.upload('./photo.jpg');

await postzen.posts.createPost({
  body: {
    title: 'Post with media',
    content: 'Uploaded through the PostZen SDK.',
    publishNow: true,
    mediaItems: [{ url: media.publicUrl, title: media.filename }],
    platforms: [{ platform: 'instagram', accountId: 'account_id' }],
  },
});

List Accounts

const { data } = await postzen.accounts.listAccounts({
  query: {
    status: 'connected',
    platform: 'linkedin',
  },
});

for (const account of data.accounts) {
  console.log(`${account.platform}: ${account.displayName}`);
}

Paginate Accounts

accounts.listAccounts accepts page and limit (max 100) and returns a pagination object with page, limit, total, and totalPages.

let page = 1;
let totalPages = 1;

do {
  const { data } = await postzen.accounts.listAccounts({
    query: { page, limit: 50 },
  });

  for (const account of data.accounts) {
    console.log(`${account.platform}: ${account.displayName}`);
  }

  totalPages = data.pagination?.totalPages ?? 1;
  page += 1;
} while (page <= totalPages);

Error Handling

The SDK throws PostZenApiError for non-2xx API responses. Specialized subclasses are used for rate limits and validation errors.

import PostZen, { PostZenApiError, RateLimitError, ValidationError } from '@postzen/node';

const postzen = new PostZen();

try {
  await postzen.posts.createPost({
    body: {
      content: 'Hello from PostZen',
      publishNow: true,
      platforms: [{ platform: 'twitter', accountId: 'account_id' }],
    },
  });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry in ${error.getSecondsUntilReset()} seconds.`);
  } else if (error instanceof ValidationError) {
    console.error('Validation failed:', error.fields);
  } else if (error instanceof PostZenApiError) {
    console.error(`PostZen API error ${error.statusCode}: ${error.message}`);
  } else {
    throw error;
  }
}

Error classes:

Class Description
PostZenApiError Base API error with statusCode, code, details, and helper methods.
RateLimitError HTTP 429 with limit, remaining, resetAt, and getSecondsUntilReset().
ValidationError HTTP 400 with fields: Record<string, string[]>.

SDK Reference

Profiles

Method Description
profiles.listProfiles() List profiles
profiles.createProfile() Create a profile
profiles.getProfile() Get a profile
profiles.updateProfile() Update a profile
profiles.deleteProfile() Delete a profile

Accounts

Method Description
accounts.listAccounts() List accounts
accounts.createPinterestBoard() Create Pinterest board
accounts.getPinterestBoards() List Pinterest boards
accounts.updatePinterestBoards() Set default Pinterest board
accounts.disconnectAccount() Disconnect an account

Connect (OAuth)

Method Description
connect.listPinterestBoardsForSelection() List Pinterest boards for the connect flow
connect.createConnectUrl() Create an OAuth connect URL
connect.completeConnect() Complete an OAuth connection
connect.selectPinterestBoard() Select Pinterest board

Media

Method Description
media.createMediaPresign() Create a presigned media upload URL

Posts

Method Description
posts.listPostComments() List comments on a LinkedIn post
posts.listPostReactions() List reactions on a LinkedIn post
posts.listPosts() List posts
posts.createPost() Create a post

Requirements

  • Node.js 18 or later
  • A PostZen API key (create one)

PostZen developer tools

License

MIT

About

PostZen's Official Node.js SDK

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages