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_approvalorcampaign_id, markaccount_idsas required, and typestatuswithoutdraft. 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
- Quick Start: Make your first API call in 5 minutes
- Authentication: API key scopes and security
- MCP Server: Use PostEverywhere from Claude, Cursor, or Windsurf
- API Reference: Full endpoint documentation
Related: the CLI · the social media API · the no-code Zapier route · the Claude walkthrough