Docs › API reference › Providers
Providers API: platform limits and media rules
Read what each platform supports (text limits, media rules, settings) and why one may be locked, then build your client from it.
A provider is a platform OmniPosts can post to, such as Bluesky or LinkedIn. GET /v1/providers is the
capability registry: for every provider it states what a post may contain, which extra settings it accepts, how
to connect it, which numbers it can report, and whether it is open right now.
OmniPosts's own composer draws its screens from this registry and nothing is hard-coded. Your client can do the same and stay correct when limits or availability change.
List providers
The answer is { "items": [ ... ] } with one object per provider. This example is abridged to the parts that
matter most. The values belong to one provider and differ for others, so always read them live.
curl https://omniposts.dev/v1/providers \
-H "Authorization: Bearer omniposts_..."
{
"items": [
{
"id": "bluesky",
"displayName": "Bluesky",
"tier": "FREE",
"enabled": true,
"lockedReason": null,
"lockedKind": null,
"auth": {
"kind": "APP_PASSWORD",
"fields": [
{ "name": "identifier", "label": "Handle or email", "help": "...", "secret": false },
{ "name": "appPassword", "label": "App password", "help": "...", "secret": true }
],
"inputs": []
},
"text": { "maxLength": 300, "counter": "GRAPHEMES", "required": true },
"media": {
"maxImages": 4,
"maxImageBytes": 2000000,
"imageMimes": ["image/jpeg", "image/png", "image/webp"],
"maxVideos": 0,
"maxVideoBytes": 0,
"videoMimes": [],
"maxAltTextLength": 2000,
"mediaRequired": false,
"ingest": "UPLOAD"
},
"uiFields": [],
"readback": "EXACT"
}
]
}
Fields
| Field | Meaning |
|---|---|
id, displayName |
The provider id you pass as providerId when connecting, and its name |
tier |
FREE or GATED. A gated provider depends on an outside approval or a paid platform plan |
enabled |
true when you can connect it and post to it now |
lockedReason, lockedKind |
When enabled is false: one sentence of why, and a machine kind (below). Both are null when enabled |
auth |
How to connect. See below |
text |
maxLength, how it is counted (counter), and whether text is required |
media |
Image and video rules |
uiFields |
Extra per-channel settings a post can carry |
readback |
How OmniPosts can check a post after sending: EXACT, SEARCH or NONE |
analytics, accountAnalytics |
Which numbers the platform can report. See Reports |
apiVersion, checkedDate, stale |
The platform API version OmniPosts targets, when it was last checked, and true if that was over 90 days ago |
Text
maxLength is measured by counter:
GRAPHEMEScounts what a person sees as one character (an emoji is one).CODE_POINTScounts Unicode code points.WEIGHTEDcounts with the platform's own weighting, where some characters count for more than one.
If required is true, a post with no text and no media is refused for that channel.
Media
| Field | Meaning |
|---|---|
maxImages, maxVideos |
How many of each a post may carry. 0 means none |
maxImageBytes, maxVideoBytes |
Largest file, in bytes |
imageMimes, videoMimes |
Accepted types |
maxAltTextLength |
Longest image description |
mediaRequired |
true if a post must have an image or video |
ingest |
UPLOAD (OmniPosts uploads the file) or URL (the platform fetches it from a public address) |
A post cannot mix images and video. Upload files with Media, which also reports, per provider, what each file would be refused for.
Settings (uiFields)
Some platforms take extra options, such as a visibility or a title. Each entry has a name, a label, a
type (TEXT, TEXTAREA, SELECT or TOGGLE), options (for SELECT), a defaultValue, whether it is
required, some help, and a lockedValue (a value you must keep for now). Send values as strings in a
target's settings map when you create a post:
A TOGGLE takes "true" or "false". A name that is not listed is refused for that channel as
setting_unknown.
{ "channelId": "CHANNEL_ID", "settings": { "name": "value" } }
Why a provider is locked
lockedKind is one of:
| Kind | Meaning |
|---|---|
platform_review |
Waiting for the platform's own app review or audit |
paid_access |
The platform charges for posting and paid posting is not switched on |
not_configured |
This OmniPosts server has no app credentials for the platform yet |
switched_off |
Posting is switched off for another reason, such as a safety switch |
upcoming |
Coming soon: OmniPosts has announced it, and it is not open to connect yet |
While a provider is locked, connecting it is refused, and a post to an existing channel on it is rejected
with channel_switched_off. Availability changes as reviews finish. lockedReason and lockedKind
are for your integration and your logs: the OmniPosts app and website show every locked provider simply
as "Coming soon", and the failure messages for a locked provider say only that ("X is coming soon to
OmniPosts.", "Posting to X is paused for now, so this post wasn't sent."). See Channels
and the Channels screen in the app.
How to use it
- Fetch the registry when your app starts and cache it for a short time, not forever.
- Validate text length and media against it before you call the API, using
counterto count. The API checks again and answers per channel, so your check is a convenience. - Show a provider as "Coming soon" when
enabledisfalse. KeeplockedReasonandlockedKindfor your logs and your own logic; do not show them to people. - Draw connect forms from
auth.fields(andauth.inputs), and settings forms fromuiFields. - Do not hard-code a list of providers or limits. New providers appear here first.