Fields
The response carries a
target per account with its status and, once live,
the platformPostUrl.
Media
mediaItems is the shape to use — it carries the type, the alt text and a
custom video thumbnail, none of which a bare URL can express:
The media library
Files you upload stay in the account’s media library, whichever way they got in — the dashboard composer, an upload link, an import from a URL, or the API. List them withGET /media, and pass their ids to a post as mediaIds instead of
copying URLs around:
list_media, which is what lets someone say
“publish the reel I just uploaded” without pasting anything. DELETE /media/:id
removes a file for good; it is refused while a post that has not been published
yet still uses it.
Uploading a file
No public URL? Two ways in, depending on size. Small files, straight through the API:mediaUrl (not uploadUrl) in mediaItems. The ticket is single-use and
expires.
No terminal? Hand the ticket to a person. The same address opened in a
browser (uploadPageUrl, returned alongside uploadUrl) is a page where they
drop the file — it goes from their browser straight to storage. This is the way
in when an assistant drives PlugKit from a web chat: it has no shell to run the
curl, and attaching the video to the conversation instead runs into the chat
client’s own attachment limit (30 MB on claude.ai), which is not PlugKit’s.
Opening the page doesn’t consume the ticket; only the upload does.
Per-platform overrides
One idea rarely fits every network verbatim — 280 characters on X, a vertical video on TikTok.platforms lets one post carry different content per target,
with customContent and customMedia overriding the defaults where they’re
given:
What each platform expects
WhatsApp doesn’t do posts — see Messaging.
Threads
Threads is a Meta platform, but it is not Instagram: it has its own app credentials, its own authorization screen and its ownthreads_* permissions.
Nothing carries over from a connected Instagram account.
- 500 characters, and Threads counts emoji as UTF-8 bytes. A longer
contentis rejected up front rather than silently truncated. - A carousel takes 2 to 20 items. The caption belongs to the post, not to the individual media.
allowComment: falsecloses replies to mentioned accounts only — that is the strictest setting Threads offers; there is no “nobody”.- No stories, no drafts, no privacy levels. A Threads post is public. Those options are refused rather than accepted and ignored.
- No messaging at all. Threads has no DM API, so it never appears in the Inbox, in broadcasts, in sequences, or in comment-to-DM automations.
- Rate limits are the platform’s: 250 published posts per 24 hours.
Instagram stories
SetasStory: true and the post goes to the account’s story instead of the
feed. It is the same call, and the same connection — nothing extra to
authorise.
A story is not a short post, and Instagram is strict about it:
- It lasts 24 hours, then it is gone. There is no permanent URL, so the
target comes back without a
platformPostUrl, and the story is left out of Analytics rather than mirrored as a post frozen at zero. - It carries no caption. Instagram drops the text, so
contentbecomes optional when every target of the post is an Instagram account. Keep it when the same post also goes to X or LinkedIn — they still need it. - One media, and only these formats: a JPEG image (8 MB max), or an MP4/MOV video of 3 to 60 seconds (100 MB max, H.264/HEVC + AAC). A PNG is refused.
- No stickers, polls, links or mentions. Instagram’s API publishes the media and nothing else; interactive elements only exist in the app.
asStoryandtrialReelare mutually exclusive — a trial only means something in the feed.
Scheduling
PassscheduledFor instead of publishNow:
timezone. A date carrying its own
offset (2027-01-01T09:00:00+01:00) is absolute and timezone is ignored.
Publish a scheduled post early, or retry one that failed:
When a target fails
A post to three accounts where one fails comes backpartial, not failed —
and the failing target carries its own error. The others are live; the fix is to
retry that one target, which the publish call above does.
Subscribe to post.published, post.partial and post.failed to hear about it
without polling — see Webhooks.
See all fields
Open
POST /v1/posts in the API Reference to try it live.