Publish once, to everywhere you post
PostAtom is one API for publishing, scheduling and measuring posts across social platforms. It handles the parts that are tedious to get right on your own: per-platform limits, retries that cannot duplicate a post, schedules that survive a daylight-saving change, and an honest answer about what actually went out.
Get a key
Every workspace has two: a live key and a test key. Start with the test key. A sandbox post travels the entire pipeline — validation, queue, lease, write-ahead, confirmation — and stops just short of a real timeline, so you can build the whole integration before anyone can see it.
Make a first request
Start with your test key. Every call is one HTTPS request, with the key as a bearer token.
curl https://api.postatom.com/v1/accounts \
-H "Authorization: Bearer $POSTNODE_API_KEY"The examples on this site use PostAtom’s TypeScript client, which is not published yet. Until it is, every call it makes is a request like the one above, and the API reference lists them all.
Publish to every connected account
Read the accounts first: the limits differ per account, not just per platform.
import { createClient } from '@postnode/sdk';
const postnode = createClient({ apiKey: process.env.POSTNODE_API_KEY! });
const { data: accounts } = await postnode.listAccounts();
const usable = accounts.filter((account) => account.publishingAvailable);
const receipt = await postnode.publish({
targets: usable.map((account) => account.id),
content: 'PostAtom publishes to every platform through one API.',
});
console.log(receipt.id, receipt.status);Then ask what happened
Publishing is not instant. publish returns as soon as
PostAtom has accepted the work; the platforms answer over the following seconds
or minutes. A post to four accounts can be published on three and refused by the
fourth, so there is no single overall status and PostAtom does not invent one.
Find out what actually happened
Publishing is accepted immediately and answered minutes later, per destination.
const { data: post } = await postnode.getPost(receipt.id);
for (const destination of post.destinations) {
if (destination.externalUrl)
console.log(destination.platform, 'published', destination.externalUrl);
else console.log(destination.platform, destination.status, destination.lastError);
}Scheduling
People schedule in wall-clock time. "Nine on Monday" is not an instant until you know the zone, and on the Sunday a clock changes it may be an instant that occurs twice or not at all. Send the zone and PostAtom resolves it, with a stated rule for both cases: a time in the spring-forward gap fires at the transition, and an ambiguous autumn time takes the first occurrence.
Schedule in someone’s own time zone
"Nine on Monday" is a wall-clock time, not an instant. Send the zone and PostAtom resolves it, including across a daylight-saving change.
await postnode.publish({
targets: [accountId],
content: 'Doors open at nine.',
publishAt: '2026-10-26T09:00:00',
timezone: 'Europe/London',
});Repeating a call safely
A publish that times out may still have arrived. The client attaches an idempotency key before its first attempt and reuses it on every retry, so its own retries can never produce a second post. Supply your own when you want the same guarantee across a process restart.
Make a publish safely repeatable
Repeating the same key returns the original post instead of creating a second one. The client attaches one automatically, so its own retries can never double-post.
await postnode.publish({
targets: [accountId],
content: 'Our autumn schedule is out.',
idempotencyKey: 'autumn-schedule-2026',
});When something is refused
Handle a refusal
The client already retries what is worth retrying, and never retries a conflict. What is left for you is the reason.
import { PostNodeError } from '@postnode/sdk';
try {
await postnode.publish({ targets, content });
} catch (error) {
if (error instanceof PostNodeError) {
// Quote the request id if you need support to look at this call.
console.error(error.code, error.message, error.requestId);
if (error.code === 'not_supported_on_platform') {
// Nothing to retry: the platform cannot do this.
}
}
throw error;
}Where to go next
- Platforms — what each one accepts, generated from the code that enforces it.
- API reference — every route, generated from the schemas that validate the requests.
- Webhooks — how to receive events and verify them.
- Agents — the MCP server and the installable skill.