How to Post to Instagram via API
The complete request flow for posting to Instagram via API: connect the account, create the media container, publish, and confirm delivery.
To post to Instagram via API you need a Business or Creator account (not personal) connected through OAuth. Natively Instagram builds a media container from a public asset URL and then publishes it; PostGate wraps both steps behind one POST /api/posts request with the profile, body, and media. Confirm delivery via the targets array or webhooks — the create response is acceptance, not proof.
This is the full journey, start to finish: connect an Instagram account, understand why a professional account is required at all, send the request, and confirm what actually happened on Instagram's side. If you already know the flow and just want the request shape, see the single-request version instead.
Step 1 — Connect a Business or Creator account
Instagram's publishing API only works against a professional account — Business or Creator, not personal. See why that requirement exists. Through PostGate's hosted OAuth, the customer authorizes access once; PostGate stores the resulting token and refreshes it, so your app never handles or stores Instagram credentials directly.
Step 2 — The container Instagram builds before publishing
Natively, Instagram splits publishing into two calls: create a media container from a public asset URL, then publish that container once it finishes processing. PostGate does both steps for you behind POST /api/posts, but the underlying behavior is the same — Instagram fetches and validates the asset asynchronously before anything goes live, which is why the response to a create-post call is acceptance, not proof of publication.
Step 3 — Send the request
Attach the connected profile, the post body, and media, with a stable Idempotency-Key so a dropped connection and a retry never post twice.
curl -X POST "https://api.postgate.studio/api/posts" \
-H "Authorization: Bearer $POSTGATE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ig-launch-004" \
-d '{
"post": { "body": "We just shipped something." },
"profiles": ["pro_instagram123"],
"media": ["https://cdn.example.com/announcement.jpg"]
}'Step 4 — Confirm delivery, do not assume it
Read GET /api/posts/{id} and look at the targets array, not just the aggregate status. A published target carries Instagram's own media ID and permalink. For production traffic, subscribe to the post.published / post.partial / post.failed webhooks instead of polling — see the full delivery lifecycle.
Step 5 — Handle the failure paths you will actually hit
A failed target returns a structured error with a stable code and a retryable flag — see errors & retries. profile_disconnected means Instagram revoked or expired the token and the customer needs to reconnect. platform_content_rejected means the content itself was rejected and needs to change, not retry. For the exact codes Instagram's Graph API returns underneath these, see the Instagram error code index.
Frequently asked questions
Can I post to a personal Instagram account via API?
No. Instagram’s publishing API only works against a professional account — Business or Creator, not personal. Through PostGate’s hosted OAuth, the customer authorizes access once; PostGate stores the resulting token and refreshes it, so your app never handles or stores Instagram credentials directly.
Why doesn’t the Instagram API publish immediately when I send a request?
Natively, Instagram splits publishing into two calls: create a media container from a public asset URL, then publish that container once it finishes processing. Instagram fetches and validates the asset asynchronously before anything goes live — which is why the response to a create-post call is acceptance, not proof of publication.
How do I know my Instagram post actually published?
Read GET /api/posts/{id} and look at the targets array, not just the aggregate status. A published target carries Instagram’s own media ID and permalink. For production traffic, subscribe to the post.published / post.partial / post.failed webhooks instead of polling.
What do Instagram publishing errors like profile_disconnected mean?
A failed target returns a structured error with a stable code and a retryable flag. profile_disconnected means Instagram revoked or expired the token and the customer needs to reconnect. platform_content_rejected means the content itself was rejected and needs to change, not retry.
