Works with all AI agents

Connect your AI agent

Choose your AI agent and connect in seconds.

Add to Claude

Works on every Claude plan, including free. Full Claude guide

  1. 1

    Add to Claude

    One click from Claude’s connector directory.

  2. 2

    Sign up or sign in

    Takes seconds, then you’re sent straight back.

  3. 3

    Ask Claude

    “Plan and schedule my posts for next week.”

PostEverywhere SDK Guide

Official PostEverywhere Node.js SDK and CLI for scheduling social media posts. Install via npm, authenticate, and start publishing.

PostEverywhere provides a Node.js SDK, a CLI, and REST API access for any language. Pick the approach that fits your workflow. The SDK and CLI are open source (MIT) on GitHub: posteverywhere/sdk and posteverywhere/cli.

Publish from Node.js with the official SDK

Install the PostEverywhere SDK

npm install @posteverywhere/sdk

Initialization

import { PostEverywhere } from '@posteverywhere/sdk';

const client = new PostEverywhere({
  apiKey: process.env.POSTEVERYWHERE_API_KEY, // pe_live_...
});

Caution: Never hard-code your API key. Use environment variables or a secrets manager. See Authentication for details.

List Connected Accounts

const { accounts } = await client.accounts.list();

for (const account of accounts) {
  console.log(`${account.platform} — ${account.display_name} (${account.health?.status ?? 'unknown'})`);
}

Create and Schedule a Post

const post = await client.posts.create({
  content: 'Just shipped a major update! Check it out at example.com',
  account_ids: [123, 456],
  scheduled_for: '2026-04-07T14:00:00Z', // canonical — always UTC
});

console.log(`Post ${post.post_id} scheduled for ${post.scheduled_for}`);

Round-trip safe

The response from client.posts.create() has the same top-level field names as the request, so you can take any post and create a clone by passing its fields straight back in. scheduled_for, account_ids, media_ids, platform_content, timezone, content: all round-trip.

Save a Draft, Then Schedule It

Pass draft: true to save a post without publishing or scheduling it (account_ids is optional for a draft). Review it later, then schedule it with POST /v1/posts/{id}/schedule, sending either scheduled_for or publish_now: true. The schedule endpoint only works on drafts.

The SDK's TypeScript types (1.6.0) lag the API here: they don't declare draft, request_approval or campaign_id, mark account_ids as required, and type status without draft. The API accepts all of it; until the types catch up, cast the params (as any) or call the endpoint directly.

// 1. Save a draft (nothing publishes yet)
const draft = await client.posts.create({
  content: 'Proposed announcement — review before sending.',
  draft: true,
  // account_ids optional for drafts
});

// 2. ...review it (e.g. client.posts.list({ status: 'draft' }))...

// 3. Schedule (or publish now) the approved draft
await fetch(
  `https://app.posteverywhere.ai/api/v1/posts/${draft.post_id}/schedule`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.POSTEVERYWHERE_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      scheduled_for: '2026-04-15T14:30:00Z', // OR: publish_now: true
      account_ids: [123, 456],               // optional if the draft already had targets
    }),
  },
);

This "draft → review → schedule" flow is the recommended human-in-the-loop pattern for AI agents: see Building AI Agents.

Platform-Specific Content

Customize content per platform using platform_content:

const post = await client.posts.create({
  content: 'Default content for all platforms',
  account_ids: [123, 456, 789],
  platform_content: {
    x: {
      content: 'Short version for X (under 280 chars)',
    },
    linkedin: {
      content: 'Longer, more detailed version for LinkedIn with professional tone...',
    },
    instagram: {
      content: 'Visual-first caption with #hashtags',
    },
  },
  scheduled_for: '2026-04-07T14:00:00Z',
});

Upload Media

import fs from 'fs';

// One call does presign → upload → complete for you
const media = await client.media.upload(
  new Blob([fs.readFileSync('./product-launch.jpg')], { type: 'image/jpeg' }),
  { filename: 'product-launch.jpg', contentType: 'image/jpeg' },
);
// Manual control instead: client.media.getUploadUrl({ filename, content_type, file_size })
// returns { upload_url, media_id }; upload the bytes, then client.media.complete(media_id).

// Use it in a post
await client.posts.create({
  content: 'Our new product is here!',
  account_ids: [123],
  media_ids: [media.id],
});

Schedule a Week of Posts

Loop through your posts and call create() for each with a future scheduled_for:

const posts = [
  { content: 'Monday motivation: ship fast, learn faster', scheduled_for: '2026-04-07T09:00:00Z' },
  { content: 'Tuesday tip: automate your social media with APIs', scheduled_for: '2026-04-08T09:00:00Z' },
  { content: 'Wednesday win: our users scheduled 10,000 posts last week', scheduled_for: '2026-04-09T09:00:00Z' },
  { content: 'Thursday thought: the best content strategy is consistency', scheduled_for: '2026-04-10T09:00:00Z' },
  { content: 'Friday launch: new API endpoints are live!', scheduled_for: '2026-04-11T09:00:00Z' },
];

const results = await Promise.allSettled(
  posts.map((p) =>
    client.posts.create({
      content: p.content,
      account_ids: [123, 456],
      scheduled_for: p.scheduled_for,
    })
  )
);

const created = results.filter((r) => r.status === 'fulfilled').length;
const failed = results.filter((r) => r.status === 'rejected').length;
console.log(`Scheduled ${created} posts, ${failed} failed`);

Check Post Results

const results = await client.posts.results('post_789');

// `results` is a per-platform array with account_id, account_name, status,
// platform_post_url, error (if failed), and published_at. (SDK 1.4.2's
// PostDestination type lags the API: account_name and platform_post_url exist at runtime.)
for (const dest of results.results) {
  console.log(`${dest.platform} (@${dest.account_name}): ${dest.status}`);
  if (dest.status === 'failed' && dest.error) {
    console.log(`  [${dest.error.code}] ${dest.error.message}`);
  } else if (dest.status === 'done') {
    console.log(`  ${dest.platform_post_url}`);
  }
}

Publish from the terminal with the CLI

The @posteverywhere/cli package manages posts and accounts from your terminal, or from any AI agent that can run shell commands. Every command prints structured JSON to stdout (errors go to stderr with a non-zero exit), so it's easy to pipe into jq or parse programmatically. Nothing to install: run it with npx.

Authentication

The CLI reads your API key from an environment variable, or from a saved device-flow login (posteverywhere login), so a login step is optional:

export POSTEVERYWHERE_API_KEY="pe_live_..."   # from the Developers page

Common commands

# Verify the key — shows the account, plan, and quota
npx @posteverywhere/cli whoami

# List connected accounts (you need the numeric `id`s to post)
npx @posteverywhere/cli accounts

# Publish now to accounts 123 and 456
npx @posteverywhere/cli post -c "Just shipped v2.0! 🚀" -a 123,456

# Schedule (ISO-8601, UTC) — add -s
npx @posteverywhere/cli post -c "Big announcement" -a 123,456 -s 2026-07-01T09:00:00Z

# Import an image or MP4 video by URL, then attach the returned media_id
# (videos import async — poll get_media until media_status is "ready")
npx @posteverywhere/cli upload https://example.com/banner.jpg   # → { "media_id": "..." }
npx @posteverywhere/cli post -c "New drop" -a 123 -m <media_id>

# Inspect posts and per-platform results
npx @posteverywhere/cli posts --status published --limit 10
npx @posteverywhere/cli results <postId>
npx @posteverywhere/cli retry <postId>          # retry failed platforms

# Why can't an account post? (needs reconnect, quota, etc.)
npx @posteverywhere/cli account:health <id>

# AI captions + analytics summary
npx @posteverywhere/cli caption -t "summer sale" --platform instagram --tone playful
npx @posteverywhere/cli analytics --period month

Run npx @posteverywhere/cli help for the full command list. Prefer a global install? npm i -g @posteverywhere/cli lets you drop the npx @posteverywhere/cli prefix and just call posteverywhere <command>.

CLI in CI/CD pipelines

Use the CLI in GitHub Actions or other CI to announce releases as part of your pipeline:

# .github/workflows/release.yml
- name: Announce release on social media
  env:
    POSTEVERYWHERE_API_KEY: ${{ secrets.POSTEVERYWHERE_API_KEY }}
  run: |
    npx @posteverywhere/cli post \
      -c "v${{ github.event.release.tag_name }} is live! ${{ github.event.release.html_url }}" \
      -a 123,456

Built for AI agents

The package ships a SKILL.md that teaches agents (Claude, Cursor, ChatGPT, OpenAI Codex, and others) the command surface for auto-discovery. If you'd rather connect over MCP than shell out, see the MCP Server guide: the hosted endpoint exposes the same capabilities with no install. (For ChatGPT, connect via the hosted connector on the agents page.)

Publish from Python

A dedicated Python SDK is not yet available. In the meantime, you can call the REST API directly with requests:

import requests
import os

API_KEY = os.environ["POSTEVERYWHERE_API_KEY"]
BASE_URL = "https://app.posteverywhere.ai/api/v1"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

# List accounts
accounts = requests.get(f"{BASE_URL}/accounts", headers=headers).json()
print(accounts["data"])

# Create a scheduled post
post = requests.post(
    f"{BASE_URL}/posts",
    headers=headers,
    json={
        "content": "Hello from Python!",
        "account_ids": [123, 456],
        "scheduled_for": "2026-04-07T14:00:00Z",  # canonical — UTC
    },
).json()
print(f"Post {post['data']['id']} scheduled for {post['data']['scheduled_for']}")

Publish with cURL

For quick one-off requests or shell scripts:

# List accounts
curl https://app.posteverywhere.ai/api/v1/accounts \
  -H "Authorization: Bearer $POSTEVERYWHERE_API_KEY"

# Create a post
curl -X POST https://app.posteverywhere.ai/api/v1/posts \
  -H "Authorization: Bearer $POSTEVERYWHERE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from cURL!",
    "account_ids": [123],
    "scheduled_for": "2026-04-07T14:00:00Z"
  }'

# Upload media — one-call from a public URL (preferred when source is online)
curl -X POST https://app.posteverywhere.ai/api/v1/media/upload-from-url \
  -H "Authorization: Bearer $POSTEVERYWHERE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hero.webp"}'

# Upload media — 3-step flow (for local files)
# Step 1: Get presigned URL. Required fields: filename, content_type, size.
curl -X POST https://app.posteverywhere.ai/api/v1/media/upload \
  -H "Authorization: Bearer $POSTEVERYWHERE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "photo.jpg", "content_type": "image/jpeg", "size": 2048576}'
# Step 2: send the bytes to the returned upload_url as upload_method says
#         (multipart POST with a "file" field for images; PUT with the same Content-Type for MP4 and PDF)
# Step 3: POST /api/v1/media/{media_id}/complete to finalise

Where to go after your first SDK call

Related: the CLI · the social media API · the no-code Zapier route · the Claude walkthrough