APIs / Publishing API
POST/v1/postsOne POST.
Eight platforms. Preflight before postflight.
The primary letmepost endpoint. Send text, media, or scheduled posts to one or many connected accounts in a single HTTP call. 80 preflight rules run locally before the upstream platform is touched. Idempotency keys on every write. Per-target results so you never have to guess whether four succeeded and one didn't.
No credit card · 90-second quickstart →
{
"targets": [
{ "platform": "bluesky" },
{ "platform": "x" },
{ "platform": "pinterest" }
],
"text": "Shipped multi-target publishing today.",
"scheduledAt": "2026-06-15T19:00:00Z"
}Why one endpoint instead of eight?
Per-platform SDKs
- 8 SDKs, 8 versions, 8 maintainers to keep up with
- 8 different error shapes to unify in your code
- 8 different auth dances to maintain
- 5 LinkedIn sunsets to patch your code through
- No shared idempotency layer
- Postflight failures only, you find out after the call
letmepost Publishing API
- One endpoint, one shape, one auth dance
- One stable error envelope across all platforms
- Idempotency keys handled in the middleware
- Platform versions pinned for you
- 80 preflight rules run before the upstream call
- Per-target outcomes, never an ambiguous boolean
✓ Preflight before postflight
Every documented constraint runs locally before the upstream platform sees the request: character counts, byte caps, media-format checks, audit-state validation, URN patterns, URL reachability. You get the rule id and remediation hint, not an empty body / empty message mystery.
Capabilities
CAPABILITIES
what the endpoint can do
How it works
HOW IT WORKS
request lifecycle · ~150ms median
Validate the envelope
Shape validation against the OpenAPI spec. Wrong type? invalid_request with the JSON path of the bad field. Median latency under 5ms.
Run preflight rules
~80 rules fire per request, scoped to the target platforms. Character counts, byte caps, media-format checks, audit-state validation. Failed preflight returns 422 with the rule id and a remediation hint without ever touching the upstream platform.
Idempotency check
If Idempotency-Key is present and we've seen it in the last 24h, return the cached response. If the body fingerprint differs, return idempotency_conflict (HTTP 409).
Publish to each target in parallel
One upstream call per target. Per-target outcome captured in the response envelope. Webhook post.published fires per-target. Median wall-clock under 2 seconds for a 3-target post.
Features
FEATURES
things the endpoint does that you don't have to build
Preflight, not postflight
Every documented platform constraint runs locally before the upstream call. Fails fast, fails loudly, fails with a doc URL.
Idempotency by default
Idempotency-Key on every write. 24-hour replay window. Body-hash conflict detection on key reuse, surfaced as 409.
Scheduled posts
scheduledAt: ISO8601 queues a delayed job. Returns 202 instead of 201. Webhook post.published confirms the publish when it lands.
Per-platform overrides
pinterest.boardId, x.replyToTweetId, threads.replyToId. Escape hatches without breaking the unified shape.
Per-target outcomes
Never an ambiguous boolean. Each target gets its own status, url, and (on failure) error sub-envelope. Iterate, log, retry per-target if needed.
One auth shape
Bearer-token lmp_live_ or lmp_test_ on every call. We hold the platform tokens, encrypted at rest, refreshed on the right schedule per platform.
Code example
multi-target publish with idempotency · typescript
import { Letmepost } from '@letmepost/sdk'; const lmp = new Letmepost({ apiKey: process.env.LMP_API_KEY }); const result = await lmp.posts.create({ targets: [ { platform: 'bluesky', accountId: 'acc_bsky_xyz' }, { platform: 'x', accountId: 'acc_x_xyz' }, { platform: 'pinterest', accountId: 'acc_pin_xyz', // per-platform override — escape hatch without breaking the shape pinterest: { boardId: '934821023' } }, ], text: 'Shipped multi-target publishing today.', media: [{ mediaId: 'med_01HXZ4N9...' }], // Idempotency-Key stamped automatically by the SDK }); // Per-target outcomes — never an ambiguous boolean for (const r of result.targets) { if (r.status === 'published') { console.log(r.platform, '→', r.url); } else if (r.status === 'failed') { console.error(r.platform, r.error.code, r.error.rule, r.error.docUrl); } }
Publishing API error reference
11 stable error codes plus the 80 preflight-rule identifiers. Each has its own docs page with remediation guidance.
Common questions
about the publishing endpoint
How does multi-target publishing count toward my quota?
One POST /v1/posts = one quota unit, regardless of how many targets. Posting to 8 platforms in a single call counts as 1 post, not 8.
What happens if one target succeeds and another fails?
You get a 200 with a per-target outcome list. The succeeded targets are live, the failed ones have status: "failed" + a full error sub-envelope. Retry the failed targets independently.
Is the Idempotency-Key required?
No, but recommended. Without it, retry-storms can double-post. With it, replays within 24h return the cached response. The SDK stamps one automatically if you don't provide one.
How do I schedule a post?
Add scheduledAt: "2026-06-15T19:00:00Z" (ISO 8601, UTC recommended). Returns 202 instead of 201. We fire post.queued immediately and post.published when it lands.
Can I cancel a scheduled post?
Yes. DELETE /v1/posts/:id while it's still in scheduled state. Returns 204. Cancellations fire a post.canceled webhook.
How fast is the endpoint?
Median request-to-response is ~150ms (preflight + idempotency check + accept). Wall-clock to publish is ~2s for a 3-target post, bound by upstream platforms.
What's the rate limit?
Per-org, not per-key. Burst of 100 requests in 10s, then 1,000/min sustained. Quota wall at your monthly cap. 429 responses carry Retry-After.
Can I dry-run a request?
Yes. POST /v1/preflight runs the rules without publishing. Same response envelope, same error codes. Useful for client-side validation before the actual write.
Other surfaces
The rest of the API.
Learn more
Good to know
Notes on the contract.
POST /v1/posts
Returns 201 on immediate publish, 202 on scheduled, 422 on preflight fail.
Authorization: Bearer lmp_live_…
Org-scoped. Profile-scoped via X-LMP-Profile header.
Idempotency-Key header. 24h replay window. Body-hash conflict → 409 idempotency_conflict.
100 req / 10s burst. 1,000 req / min sustained. 429 carries Retry-After.
80 rules. Each pure function, each tested, each has a docs page. Run them dry via POST /v1/preflight.
ISO 8601 timestamp. scheduledAt in the future returns 202. Cancel via DELETE /v1/posts/:id while still scheduled.