Facebook API: Post to a Page with the Graph API

By Mika ReyesPublished 5 min read

The short answer

To post to a Facebook Page with the API, get a Page access token for a user who can create content on the Page, with pages_manage_posts, pages_read_engagement, and pages_show_list. Then POST to the Page's feed edge for text and links, or its photos edge for an image. The Graph API can't post to personal profiles or Groups. Adeli publishes to Facebook Pages with one API key.

To post to a Facebook Page through the API, you call Meta's Graph API with a Page access token: POST /{page-id}/feed for text and links, and POST /{page-id}/photos for images. The token needs pages_manage_posts and must belong to someone who can create content on the Page. The calls below come straight from Meta's docs, along with the multi-photo flow and the places the API can't post to.

For what the Graph API is and how access works, start with our Facebook Graph API guide. Video and Reels have their own upload flow, covered in the Facebook video upload API guide.

Can you post to Facebook through the API?

Yes, to Facebook Pages. Meta's Pages API posts guide covers creating, updating, and deleting Page posts. Personal profiles and Groups are off the table:

Where Can an app post there? Why
Facebook Page Yes pages_manage_posts with a Page access token
Personal profile No Facebook deprecated publish_actions in 2018 and pointed developers to the Share dialog
Facebook Group No Meta deprecated the Groups API in v19.0, including publish_to_groups, and removed it from all versions 90 days later

If your product promised "post to Facebook" to users who only have a personal profile, the answer for them is a Page. There's no API workaround.

What do you need before you start?

You need four things before the first post goes through:

  • A Facebook Page the user can act on. Meta requires the CREATE_CONTENT task on the Page, and its posts guide asks for CREATE_CONTENT, MANAGE, and MODERATE.
  • A Meta app with Facebook Login. The user logs in and grants your permissions on Meta's consent screen.
  • The permissions: pages_show_list (list the user's Pages), pages_read_engagement, and pages_manage_posts (publish). These are the three Meta's Page photos reference lists for publishing.
  • A Page access token for that Page, not the user's token.

Standard Access only works for people with a role on your app. Your own test Pages will work, then the first customer without a role gets an error. Serving customers needs Advanced Access through App Review. See how long Meta app review takes for the timeline.

Step 1: Get the Page ID and Page access token

Call the accounts edge with the user's token. It returns every Page the user manages, each with its ID and its own token:

curl "https://graph.facebook.com/v26.0/me/accounts?access_token=$USER_TOKEN"

Store the id and access_token for the Page the user picks. If you exchange the user token for a long-lived one first, the Page token you get has no expiration date. Our Facebook Page access token guide walks through that exchange and the ways the token still gets invalidated.

Step 2: Publish a text or link post to /feed

Send message, and optionally link, to the Page's feed edge. This is the call from Meta's posts guide:

curl -X POST "https://graph.facebook.com/v26.0/$PAGE_ID/feed" \
  -H "Content-Type: application/json" \
  -d '{"message":"We shipped it.","link":"https://example.com/changelog","access_token":"'"$PAGE_TOKEN"'"}'

The response is the new post's ID:

{ "id": "page_post_id" }

The post's URL is https://www.facebook.com/ plus that ID. The same edge takes a few more parameters:

Parameter What it does
message The post text
link A URL Facebook turns into a link preview
published true by default. false creates an unpublished post
scheduled_publish_time Required when published is false. Must be 10 minutes to 30 days from the request
targeting.geo_locations Limits who sees the post by location

Meta says passing access_token as a parameter is fine for testing. In production, send it from your server and never expose a Page token to the browser.

Step 3: Post an image with /photos

POST the image's public URL to the Page's photos edge. Meta fetches the file from url, or you can upload the bytes as multipart/form-data instead:

curl -X POST "https://graph.facebook.com/v26.0/$PAGE_ID/photos" \
  -F "url=https://www.example.com/images/launch.jpg" \
  -F "caption=Launch day" \
  -F "access_token=$PAGE_TOKEN"

The response carries both IDs:

{ "id": "photo_id", "post_id": "page_post_id" }

From Meta's Page photos reference:

  • Caption field: caption. The older message and name fields are deprecated for photos.
  • File types: JPEG, BMP, PNG, GIF, and TIFF.
  • Size: files can't exceed 10 MB. Meta recommends PNGs under 1 MB to avoid pixelation.
  • Metadata: Facebook strips location data and resizes the image for different screens.

How do I post multiple photos in one Facebook post?

Upload each photo unpublished, then attach them all to one feed post. It takes one call per photo plus one to publish:

  1. Upload each photo to /{page-id}/photos with published=false. Each call returns a photo ID.
  2. Create the post on /{page-id}/feed with the IDs in attached_media:
curl -X POST "https://graph.facebook.com/v26.0/$PAGE_ID/feed" \
  -F "message=Three photos, one post" \
  -F 'attached_media[0]={"media_fbid":"PHOTO_ID_1"}' \
  -F 'attached_media[1]={"media_fbid":"PHOTO_ID_2"}' \
  -F 'attached_media[2]={"media_fbid":"PHOTO_ID_3"}' \
  -F "access_token=$PAGE_TOKEN"

Unpublished photos are deleted after about 24 hours, per Meta's reference, so upload them right before you create the post.

How do you know it worked?

A post ID in the response means it's live (unless you set published=false). Read it back with GET /{page-id}/feed to confirm. When it fails, the error code in the response tells you why. These are the ones a publishing integration sees most, from Meta's error handling guide:

Code Meaning Fix
190 Access token expired or invalid Get a new token. Subcodes 460 (password changed) and 463/467 (expired, revoked) mean the user has to log in again
10, 200–299 Permission not granted or removed Request pages_manage_posts again; check Advanced Access if the user has no role on your app
506 Duplicate post Facebook won't publish the same content twice in a row. Change the text
368 Temporarily blocked for policy violations Wait before retrying, and check the content
4, 17, 341 Rate or capability limits Back off and retry later

Meta's docs also say an app can only update a Page post that the same app created, so posts made in the Facebook app or another tool can't be edited through yours.

Is there a faster way to post to a Facebook Page?

Yes, if you'd rather not own the Meta app. The calls above are short. The work around them is the Meta app, Facebook Login, Business Verification, App Review for pages_manage_posts, and catching every Page token that gets invalidated after launch.

App review is usually the slowest part. When we went through Meta's review for Pages publishing, the screencast drew far more scrutiny than the written use case. Expect reviewers to want the full path on video: the login, the user granting the permission, picking the Page, and the post showing up live on Facebook.

Adeli's Facebook API removes that work. Adeli maintains the Facebook developer app, goes through Meta's review, and refreshes every connected Page's tokens, so your team holds one API key. Each of your users connects their own Facebook Page. A photo post is one request to POST /api/v1/posts:

curl -X POST "https://app.tryadeli.com/api/v1/posts" \
  -H "Authorization: Bearer $ADELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "facebook",
    "accountId": "00000000-0000-0000-0000-000000000000",
    "message": "Launch day",
    "image": { "contentType": "image/jpeg", "base64": "<base64 JPEG>" }
  }'

You send the image as base64 and Adeli stages it on its own media host, because Meta fetches media by URL. The 201 response is the finished post, with its permalink. Swap image for images to publish 2 to 10 photos as one multi-photo post, with no unpublished-photo step. Adeli caps each Facebook photo at 4 MiB decoded.

Facebook connections take one extra step, because the user has to choose a Page. Until they do, the account sits in pending_page_selection and every publish returns 409 page_not_selected, so your UI knows to prompt them.

The same posting API also publishes to Instagram, TikTok, YouTube, X, and Bluesky. On Adeli's pricing, your first 3 connected accounts are free, and one Facebook Page counts as one connected account.

faq

Frequently asked questions

Can I post to my personal Facebook profile with the API?

No. Facebook deprecated the publish_actions permission in 2018, and it was the only way an app could post to a personal profile. The Graph API now publishes to Pages only. For profiles, Meta points developers to its Share dialog, where the person posts it themselves.

Can I post to a Facebook Group with the API?

No. Meta deprecated the Facebook Groups API in Graph API v19.0, including the publish_to_groups permission, and said it would be removed from all versions 90 days later. There is no supported way for an app to publish to a Group today.

Why is my API post going to my personal profile instead of my Page?

You're almost certainly calling the API with your user access token. Ask the accounts edge for the Page's own token, then send the post to the Page ID with that Page token. A user token can't act as the Page.

Do I need Meta App Review to post to a Facebook Page?

Only to post to Pages managed by people without a role on your app. Standard Access works for your own Pages, which is enough for testing. A product where customers connect their own Pages needs Advanced Access for pages_manage_posts, which means Business Verification and App Review.

Power your next project
with one social media API

Give your users publishing, scheduling, and analytics through one integration your team can actually maintain.

Try with agents

Start with free credits. No credit card required.