Posts
Create a post now or scheduled, understand every status, track it to completion, reschedule and cancel. Idempotency and automatic retries.
A post is the caption, the images and one or more target accounts. Each account is a target (platforms[]) with its own state; the post status is derived from its targets.
Creating
curl -X POST https://api.uat.postmatic.dev/v1/posts \
-H "x-access-key: pm_live_…" \
-H "content-type: application/json" \
-d '{
"content": "Now live.",
"platforms": [{ "platform": "instagram", "accountId": "int_…" }],
"mediaItems": [{ "type": "image", "url": "https://cdn.example.com/photo.jpg" }],
"scheduledFor": "2026-09-15T13:00:00-03:00",
"idempotencyKey": "launch-2026-09-15"
}'| Field | Required | Description |
|---|---|---|
content | no | Caption, up to 2,200 characters. Applies to every target unless overridden. |
platforms[] | yes | 1 to 5 targets, no repeated accountId. Each: platform (instagram), accountId, optional content (caption override for that account). |
mediaItems[] | yes | 1 to 10 items. Each one has type (image or video) and either url (https) or mediaId from Media. One item publishes to the feed (image) or as a Reel (video). Two or more items, images and videos mixed, become a carousel — all with the same aspect ratio. |
platformSpecificData | no | Instagram options, inside each entry of platforms[]. An unknown field answers 400. |
publishNow | no | true publishes right away. Do not combine with scheduledFor. |
scheduledFor | no | ISO 8601 date with Z or an offset, between 1 minute and 75 days ahead. |
timezone | no | IANA zone (America/Sao_Paulo), stored with the post for display. Does not change the instant of scheduledFor. |
idempotencyKey | no | Up to 128 characters. See below. |
profileId | no | Associates the post with a profile of the project. |
One of publishNow: true or scheduledFor is required. publishNow: false without scheduledFor is rejected.
Response
The API answers 202 and returns the post in its initial state. Publishing happens in the background, even with publishNow.
{
"success": true,
"postId": "post_…",
"status": "scheduled",
"idempotencyKey": "launch-2026-09-15",
"content": "Now live.",
"scheduledFor": "2026-09-15T16:00:00.000Z",
"timezone": null,
"profileId": null,
"platforms": [
{
"platform": "instagram",
"accountId": "int_…",
"targetId": "tgt_…",
"status": "pending",
"platformPostId": null,
"platformPostUrl": null,
"errorCode": null,
"errorMessage": null,
"attemptCount": 0
}
],
"mediaUrls": ["https://…/media/…jpg"],
"createdAt": "2026-09-08T14:00:00.000Z",
"updatedAt": "2026-09-08T14:00:00.000Z"
}Before accepting, Postmatic validates the accounts (they exist, are connected, token alive), downloads and validates images given by URL, and checks each account's publishing quota with Meta. Any problem comes back as a synchronous error: 404 NOT_FOUND, 409 TOKEN_INVALID, 400 MEDIA_INVALID, 409 RATE_LIMIT_ACCOUNT.
Instagram options
Go in platformSpecificData, inside each entry of platforms[].
| Field | Applies to | Description |
|---|---|---|
contentType | image or video | "story" publishes as a Story: one item only, no caption (Meta ignores text so we refuse content), Business accounts only. No other option works alongside it. |
shareToFeed | video | false publishes only to the Reels tab, without showing up in the profile grid. Defaults to true. |
coverUrl | video | An https URL of an image for the Reel cover. Needs to be reachable at publish time. |
thumbOffset | video | Millisecond of the video to use as the cover. Ignored when coverUrl is present. |
audioName | video | Name of the audio track shown on the Reel, up to 100 characters. |
collaborators | video and image | Up to 3 accounts invited as co-authors. Each one needs to accept on Instagram. |
{
"content": "Behind the scenes of the new collection.",
"platforms": [{
"platform": "instagram",
"accountId": "int_…",
"platformSpecificData": { "shareToFeed": true, "thumbOffset": 1500 }
}],
"mediaItems": [{ "type": "video", "url": "https://cdn.example.com/reel.mp4" }],
"publishNow": true
}Story
{
"content": "",
"platforms": [{ "platform": "instagram", "accountId": "int_…", "platformSpecificData": { "contentType": "story" } }],
"mediaItems": [{ "type": "image", "url": "https://cdn.example.com/story.jpg" }],
"publishNow": true
}Stories disappear from the profile in 24 hours and don't show up on the grid. Response and tracking are the same as for a normal post; platformPostUrl is set while the Story is live. Creator accounts answer 403 PLATFORM_NOT_AVAILABLE.
Mixed carousel
Images and videos can go together in mediaItems. Carousel video is limited to 60 seconds and 100 MB (different from Reels), and all items must have the same aspect ratio — Instagram crops everything to match the first item's ratio, so the API refuses a difference of over 1% with MEDIA_INVALID, reason: aspect and details.position pointing to the item.
Idempotency
Send a unique idempotencyKey per post (the id of your own record, for instance). If the request is repeated with the same key, the API returns 200 with the original post and does not create another; the body of the repeat is ignored. The key never expires. Without idempotencyKey, Postmatic generates one and returns it in the response.
Status
| Post status | Meaning |
|---|---|
scheduled | Scheduled, waiting for its time. |
pending | Accepted and waiting for the queue (immediate publish, or a schedule whose time has come). |
publishing | At least one target is in progress. |
published | Every target published. |
partial | Some published, others failed. |
failed | Every target failed, was cancelled or missed its time. |
draft | No targets. Not created by the API today. |
Each target in platforms[] has a finer state:
| Target status | Meaning |
|---|---|
pending | Waiting for its time. |
queued | In the publishing queue. |
creating_container, awaiting_container, publishing | Publishing steps on Meta. |
published | Published. platformPostId and platformPostUrl are set. |
failed | Failed after exhausting retries. errorCode and errorMessage explain. |
missed | The time passed more than 30 minutes ago without publishing starting (prolonged outage). |
cancelled | Cancelled by you. errorCode is CANCELLED. |
Tracking
curl https://api.uat.postmatic.dev/v1/posts/post_… \
-H "x-access-key: pm_live_…"Poll every 15 to 30 seconds until the status is terminal (published, partial or failed). An immediate publish usually takes 30 seconds to 2 minutes; Meta processes the image before publishing. To avoid polling in a loop, register a webhook and receive the final event.
Automatic retries
Temporary Meta failures (instability, app-level limits) are retried on their own, with intervals of 1, 5, 15 and 60 minutes, up to 5 attempts. attemptCount shows which one it is on. Definitive errors (revoked account, rejected media, blocked content) are not retried and the target goes straight to failed.
Listing
curl "https://api.uat.postmatic.dev/v1/posts?status=scheduled&limit=50" \
-H "x-access-key: pm_live_…"| Parameter | Default | Description |
|---|---|---|
status | One of the post statuses. | |
profileId | Filter by profile; "null" returns those without one. | |
limit | 20 | 1 to 100. |
offset | 0 | How many to skip. |
sort | desc | asc or desc, by creation date. |
The response is { success, total, posts[] }, each post in the same shape as on creation.
Rescheduling
curl -X PATCH https://api.uat.postmatic.dev/v1/posts/post_… \
-H "x-access-key: pm_live_…" \
-H "content-type: application/json" \
-d '{ "scheduledFor": "2026-09-16T13:00:00-03:00", "timezone": "America/Sao_Paulo" }'Only for scheduled posts. Same window rules as creation. If any target already started, the response is 409 POST_NOT_CANCELLABLE.
Cancelling
curl -X DELETE https://api.uat.postmatic.dev/v1/posts/post_… \
-H "x-access-key: pm_live_…"Cancels the targets still pending. The post is not deleted: it stays in the history with status failed and errorCode: CANCELLED on each target. A post that already started publishing, or whose time has passed, answers 409 POST_NOT_CANCELLABLE. Nothing is removed from Instagram.
Retrying failed targets
curl -X POST https://api.uat.postmatic.dev/v1/posts/post_…/retry -H "x-access-key: pm_live_…"Reopens the targets in failed, missed or cancelled with the original caption and media; targets already published are left untouched. With no body, it publishes right away; with { "scheduledFor": "…", "timezone": "…" }, it schedules again. Answers 202 with the post. 400 when there is nothing to retry; 409 POST_NOT_CANCELLABLE if some target is still publishing.
Limits
- Up to 10 images per post (2 to 10 become a carousel) and 5 accounts per request.
- Caption up to 2,200 characters.
- 100 posts per account every 24 hours, Meta's quota. Postmatic rejects with
RATE_LIMIT_ACCOUNTwhen the quota is exhausted. - One video per post, published as a Reel. Stories and carousels with video are not accepted yet; see the Roadmap.