PublishQPublishQ

SDK

Install the PublishQ SDK for typed access to posts, accounts, media, and workspaces from a backend, a cron job, or an agent you built yourself.

@publishq/sdk is the official SDK for PublishQ. Typed calls for posts, accounts, media and workspaces, in the language your backend already speaks.

Install

npm install @publishq/sdk

Quick start

import PublishQ from '@publishq/sdk';

const publishq = new PublishQ({ apiKey: process.env.PUBLISHQ_API_KEY! });

const { data } = await publishq.accounts.list();
for (const account of data?.accounts ?? []) {
  console.log(account.platform, account.accountName);
}

Server-side only

A pq_live_ key is a secret. Call the SDK from a backend, a script, a cron job or an agent you run yourself. Never ship it to a browser.

Errors are returned, not thrown

Every call resolves to { data, error } and never throws on an HTTP error, so a failed publish is a value you check rather than an exception that unwinds your job.

const { data, error } = await publishq.posts.create({
  content: 'Shipped.',
  accounts: [{ accountId: '<account-id>' }],
  publishNow: true,
});

if (error) {
  // The API's own error body, not a wrapper's summary of it
  console.error(error);
  return;
}
console.log(data.id);

Posts

// A post with no schedule and no publishNow is saved as a draft
const { data: draft } = await publishq.posts.create({
  content: 'Draft for review',
  accounts: [{ accountId: '<account-id>' }],
});

// Scheduled, to several accounts, with per-account text
await publishq.posts.create({
  content: 'The long version, for the platforms that allow it',
  accounts: [
    { accountId: '<linkedin-account-id>' },
    {
      accountId: '<x-account-id>',
      postOverrides: { content: 'The short version' },
    },
  ],
  scheduledAt: '2026-08-01T09:00:00.000Z',
});

await publishq.posts.list({ page: 1, limit: 20 });
await publishq.posts.get('<post-id>');
await publishq.posts.update('<post-id>', { content: 'Updated text' });
await publishq.posts.delete('<post-id>');
await publishq.posts.removeAccount('<post-id>', '<account-id>');

Accounts

const { data } = await publishq.accounts.list({ platform: ['TIKTOK'] });

// TikTok has no default privacy level, and the options depend on the account.
// The privacyLevel you send must be one of the returned privacyLevelOptions.
const { data: info } = await publishq.accounts.getTikTokCreatorInfo('<account-id>');

Media

const { data: media } = await publishq.media.upload({ file });
await publishq.media.list({ page: 1, limit: 20 });
await publishq.media.get('<media-id>');

Pass the returned id in a post's mediaIds.

Workspaces

await publishq.workspaces.list();
await publishq.workspaces.get('<workspace-id>');
await publishq.workspaces.create({ name: 'Client A' });

Runtime schemas

The SDK also ships Zod schemas for every request shape, so a body assembled at runtime can be checked before it is sent. This is useful when the values come from a model rather than from your own code.

import { zCreatePostAccount } from '@publishq/sdk';

const parsed = zCreatePostAccount.safeParse(whateverTheAgentProduced);
if (!parsed.success) {
  // Ask the agent to try again, without spending an API call
}

On this page