Publishing is unlimited on every paid plan — there is no monthly cap on how many posts you send. Usage is still metered, and one request counts as one upload no matter how many platforms it targets: publishing to TikTok, Instagram, and Facebook in a single call records 1 upload, not 3. See Usage to read your current figures.
Choose an endpoint by media type
The photo endpoint uses
multipart/form-data for file uploads, or plain JSON when the images come from URLs (photo_urls[] — see Send photos by URL). Text and video use JSON.
Images per post
MADIAD Hub doesn’t impose its own cap on how many photos you attach tophotos[] — it passes them straight through. Each platform enforces its own maximum, so the effective limit for a fan-out post is the smallest limit among the platforms you targeted:
These are the platforms’ own published limits, not something MADIAD Hub enforces — they can change on the platform’s side without notice. When in doubt, test with a small batch first.
Fan out with platforms[]
List every target platform. The same content goes to each, unless you override per platform.
One request shares its media across every listed platform. You can override the text per
platform (
caption → <platform>_caption, below) and pass platform-specific options, but the
photos[] / photo_urls[] / video you attach go to all the platforms in that request — there is no
per-platform media field. To publish different images or video per platform, send a separate
request per platform (each with its own photos[]/video).Send photos by URL
Instead of uploading the bytes, pass public links withphoto_urls[] — MADIAD Hub downloads each image server-side and publishes it exactly as if you had uploaded the file. Prefer this for large images or big batches: the heavy transfer happens on our side, so your request stays tiny and can never trip a request-size error.
- Each URL must be a public
https://direct image link (a hostname, not an IP address). A share-page link that opens a web page (e.g. a drive viewer) is rejected with a clear error — use the direct file URL. Redirects are followed (up to 5 hops), but every hop must stayhttps://. - Up to 40 URLs per request, 30 MB per image, and 120 MB combined.
- Also works in multipart: send
photo_urls[]as text fields, and mix them freely withphotos[]file uploads in one request — on carousels, uploaded files come first, then URL photos, each in send order.
Per-platform overrides
caption is the shared default. Override it for a single platform with a <platform>_caption field — handy for hashtags, mentions, or length limits.
instagram_first_comment.
Instagram overrides are published as their own job. When
instagram_caption differs from
the shared caption and the request also targets other platforms, MADIAD Hub sends Instagram
separately so it gets exactly the text you wrote. This is invisible for a normal post — still
one upload against your quota, one request_id, one status to poll. For a scheduled
post it is visible in one place: the schedule list shows Instagram as its own entry, so
cancelling or editing it is a separate action from the other platforms.One combination is refused: add_to_queue together with an instagram_caption that differs
from the shared caption. Instagram would take its own queue slot and go out at a different
time from the rest, so the request is rejected instead — queue Instagram as its own request, or
use the shared caption.Character limits
caption maps to each platform’s main text field. The limit differs per platform — and on a few platforms that field is a short title, with the long text going in a separate description:
A few rules follow from the table:
- Posting to several platforms at once? A
captionis rejected up front only when it’s longer than every selected platform’s limit. If it fits at least one, the request proceeds and each platform reports its own result — so set a<platform>_captionfor any target with a tighter limit. - Per-platform overrides are validated too, for the platforms with a hard cap: Instagram, LinkedIn, TikTok, YouTube, Reddit, Pinterest and Google Business. A
<platform>_title(or<platform>_caption) over the cap — e.g.tiktok_title> 90 on a photo post,reddit_title> 300, orinstagram_caption> 2,200 — is rejected with a clear error before publishing. X, Threads and Bluesky are not pre-checked because they auto-thread rather than reject, Telegram and Discord truncate, and a Facebook override is only capped on Reels — those go to the platform and report their own result. - Links on X. Any URL X would turn into a clickable link is removed from X posts before publishing — put links in your profile or inside the image/video instead.
Advanced platform options
Any option a platform supports can be passed through — MADIAD Hub forwards it untouched, so you’re never limited to the fields listed here. Prefix the field with the platform name where the option is platform-specific. Common examples:
Array fields use the
field[] convention — repeat the key once per value.
For a full reference of every per-platform option — privacy levels, video types, polls, CTAs, subtitle upload, and more — see Platform options.
First comment
Auto-post a follow-up comment the instant a post publishes — handy for a link, hashtags, or a call to action you’d rather keep out of the main caption. Setfirst_comment as the shared default and override it per platform with <platform>_first_comment (the per-platform value wins where both are present).
- X and Threads — posted as a reply to the post. For an auto-threaded X post, it replies to the last tweet in the thread.
- YouTube — posted as a top-level comment on the video.
- Instagram — applies to photo and video posts only (Instagram has no text-only posts).
first_comment_media[] as binary file uploads, exactly like photos[]. Currently supported on Reddit only, and not available for scheduled or queued posts.
AI-generated captions
Let MADIAD Hub write the caption for you instead of supplying one. Most useful on photo and video posts:Platform requirements
Facebook publishes to a Page
Facebook publishes to a Page
Facebook posts go to a Page, not a personal profile. If the profile has exactly one Page it is used automatically; if it has several, pass
facebook_page_id to choose one. To list a profile’s Pages and find their IDs, use GET /v1/connections/facebook/pages — see Discover Page & board IDs.Photos: upload files or pass URLs; video by URL
Photos: upload files or pass URLs; video by URL
Send photos as binary file uploads (
photos[]=@file) or as public links (photo_urls[]) — see Send photos by URL. For video, pass a public video_url.Reddit needs a title
Reddit needs a title
Reddit posts always carry a title. By default the caption is used as the title; pass
reddit_title to set a separate one.Read the response
A publish call returns the poststatus, the platforms it targeted, and the identifiers you poll with:
completed— published synchronously;resultsholds the per-platform outcome.processing— still uploading (common for video). Poll withrequest_id, or subscribe to webhooks.scheduled— queued for a future time; poll withjob_id.partial— some platforms published and at least one was rejected; check each entry inresults.failed— every targeted platform rejected the post. The HTTP status is still200; the per-platform reasons are inresults.unknown— the outcome could not be confirmed in time (returned with HTTP502and codeupstream_timeout). The post may already be live — do not retry blindly; see Errors and Idempotency.
GET /v1/posts/status) use the same vocabulary.
When results is present it is keyed by platform. Each entry has success, plus publish_id and url when the post went out, or error and error_code when the platform refused it:
error_code is a short machine-readable reason (for example account_restricted, daily_cap_reached, media_invalid_format) — branch on it rather than on the wording of error. It is present only when the platform gave one.
The publish response and a status poll return this identical shape.
When some — but not all — platforms fail, the top-level status is partial and a failed_platforms array lists the ones that failed.
Check status later
Poll an asynchronous or scheduled post with therequest_id or job_id from the publish response:

