Your users make something in your product and then leave to post it somewhere else. Closing that gap is a common feature request, and the shape of the work depends almost entirely on one decision made at the start.
The decision: whose account is it?
If posts go to your own accounts, an API key is enough. Server-to-server, no consent flow, simplest possible thing.
If posts go to your users’ accounts, you need OAuth. Each user authorises your app and you receive a scoped token bound to the workspace they chose.
Get this right at the start. Products that begin by asking users to paste an API key end up migrating to OAuth later, and in the meantime they’ve taught users a habit that’s bad for both sides: pasting a long-lived credential with broad access into a third-party product.
What you’d otherwise build
The reason to use a publishing API rather than integrating platforms directly is that direct integration is a project per platform that never finishes:
- A separate OAuth flow per platform, with different scopes and token lifetimes
- A refresh job per platform, which fails silently and takes publishing with it
- A separate media pipeline per platform, with different upload models
- Asynchronous publishing on several, where a 200 doesn’t mean published
- A separate rate limit per platform
- App review on several, which is calendar time you don’t control and can be refused
- Ongoing breaking changes on each platform’s own schedule
For a SaaS where publishing is a feature rather than the product, that’s a permanent maintenance commitment attached to something that isn’t your differentiator.
What you still build
Using a publishing API doesn’t remove all the work. You still need:
A connection UI. Somewhere users connect their accounts and see the connection’s state.
A visible broken state. Connections break. Users need to see that and fix it, and it needs to be a state in your product rather than an error you swallow.
Composition, in your product’s terms. The API takes content and channels. What your users actually do, and how it maps onto a post, is yours.
Failure handling. Something will fail to publish. Decide now whether the user finds out from you or from the absence of a post.
What to check when choosing
| Requirement | Why |
|---|---|
| OAuth with granular scopes | Multi-user products need it, and narrow scopes limit blast radius |
| The specific platform list | Not “all major networks” |
| Validation on create | So an over-limit caption fails where you can show the user |
| Rate limits at your tier | The number that decides whether it scales with you |
| Structured errors | So you can surface something useful rather than “publishing failed” |
BulkPublish for this

- Auth: OAuth 2.1 for acting on your users’ accounts, API keys for your own
- OAuth details: PKCE (S256) required,
scoperequired with no implicit default, single-use authorization codes, rotating refresh tokens, tokens passed asBearer bpat_... - Scopes:
posts:read,posts:write,media:read,media:write,analytics:read,channels:read,full - Surface: 59 documented endpoints
- Platforms: 15
One boundary to design around: OAuth tokens reach posts, schedules, labels, media, analytics, quota usage and read-only channel data. Account administration, meaning team, organizations, billing, credit purchases, API keys and OAuth app management, returns 403 for any OAuth token including full, because those actions would outlive a user disconnecting your app. Those need an API key.
Better to know that now than to find it as an unexplained 403 in production.
| Free | Pro | Business | |
|---|---|---|---|
| API requests/day | 30 | 5,000 | 50,000 |
| API keys | 1 | 5 | 10 |
The short version
Decide first whether you’re publishing to your own accounts or your users’. If it’s users’, use OAuth from day one rather than migrating later. Integrating platforms directly is a permanent maintenance commitment on something that isn’t your product, and the parts you still have to build are the connection UI, a visible broken state, and telling users when something failed.