Skip to content

Facebook API

Postproxy publishes feed posts, Reels, and Stories to a Facebook Page, and offers the fullest engagement support of any network: complete comment management (including likes), Messenger direct messages with reactions and private replies, and Page reviews (recommendations) with replies. It also reads and updates the Page’s profile fields — about, description, website, phone, emails.

Every request below uses the base URL https://api.postproxy.dev and an Authorization: Bearer YOUR_API_KEY header. Replace YOUR_API_KEY and the example IDs with your own.

Every post targets one or more profiles. A profile is one connected account — for Facebook, a Facebook Page. Reference one in a request by its id (the prof_abc123 in the examples below) or by the platform name "facebook", which selects the group’s Facebook profile (a group holds at most one profile per platform). List what’s connected with GET /api/profiles.

Profiles live in profile groups — containers that organize the accounts for one brand, client, or project. List groups with GET /api/profile_groups, and connect a new Facebook profile with the Initialize Connection endpoint.

Example: get profiles and profile groups

List the profiles you can post to — GET /api/profiles:

Terminal window
curl -X GET "https://api.postproxy.dev/api/profiles" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "prof_abc123",
"name": "My Company Page",
"platform": "facebook",
"status": "active",
"profile_group_id": "grp_xyz789",
"expires_at": null,
"post_count": 84,
"avatar_url": "https://cdn.postproxy.dev/uploads/avatar_prof_abc123.jpg"
}
]
}

List your profile groups — GET /api/profile_groups:

Terminal window
curl -X GET "https://api.postproxy.dev/api/profile_groups" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{ "id": "grp_xyz789", "name": "Main Brand", "profiles_count": 5 },
{ "id": "grp_def456", "name": "Client Project", "profiles_count": 3 }
]
}
Platform IDfacebook
Formatspost (default), reel, story
Character limit63,206 (2,200 for Reels)
MediaOptional on feed posts; required for Reels and Stories
PlacementsPages — page_id (required)
CommentsList, reply, edit, delete, hide/unhide, like/unlike
ReviewsPage recommendations — list, reply, delete your reply (via Profile Comments)
Direct messagesYes — text, media, reactions, quick replies and buttons, private replies, m.me referrals
Page profileRead and update about, description, website, phone, emails, and more — see Page profile management
Country restrictionFeed posts — see Country restriction
Link postsFeed posts — see Link posts
Post chainsNo
FormatDescription
postFeed post (default)
reelFacebook Reel
storyFacebook Story

Every format requires a page_id — the Page to publish to. Fetch the IDs from placements.

ParameterTypeRequiredDescription
formatstringNo"post" (default)
first_commentstringNoComment added after publishing
page_idstringYesPage to publish to — fetch from placements
alt_textstring or arrayNoAlt text for images — see Alt text
made_with_aibooleanNoShow Meta’s “AI info” label. Feed posts with images only — see AI disclosure
countriesarray or stringNoRestrict the post to viewers in these countries — see Country restriction
linkstringNoPublish as a native link post with Facebook’s preview card — see Link posts
MediaMax sizeFormatsCountDuration
Image4 MBjpg, png, gif, webp10—
Video4 GBmp4, mov11 s – 4 h
  • Text-only posts are allowed; media is optional.
  • Images and video cannot be mixed.
  • Minimum image dimensions: 200×200 px.
Terminal window
# Feed post with an image and a first comment
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": { "body": "Exciting news! Check out our latest update." },
"profiles": ["prof_abc123"],
"media": ["https://example.com/image.jpg"],
"platforms": {
"facebook": {
"format": "post",
"first_comment": "What do you think? Let us know!",
"page_id": "123456789"
}
}
}'

Restrict a feed post so only people in the given countries see it — Facebook’s audience geo-gating. Anyone else won’t see the post on the Page, in feed, or via a direct link. Enter the countries in the composer’s Facebook settings, or pass countries as ISO 3166-1 alpha-2 codes — an array or a comma-separated string, case-insensitive.

Terminal window
# Feed post visible only to people in the US, Canada, and the UK
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": { "body": "Our North America and UK stores are now open." },
"profiles": ["prof_abc123"],
"platforms": {
"facebook": {
"page_id": "123456789",
"countries": ["US", "CA", "GB"]
}
}
}'
  • Applies to the post format only; reels and stories ignore it.
  • The restriction is set at publish time and can’t be changed afterwards through the API.

Publish a feed post as a native link post: Facebook scrapes the URL’s Open Graph tags and renders its own preview card — image, title, domain — which clicks through to the link, with the post body as the message above it. Fill in “Link” in the composer’s Facebook settings, or pass link with an http(s) URL.

Terminal window
# Feed post published as a native link post
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": { "body": "Our guide to scheduling posts from your own app." },
"profiles": ["prof_abc123"],
"platforms": {
"facebook": {
"page_id": "123456789",
"link": "https://postproxy.dev/blog/schedule-social-media-posts-api/"
}
}
}'
  • Applies to the post format only; reels and stories ignore it.
  • Text-only — a link sent together with media is rejected with a 422 at create time, as is anything that isn’t an http(s) URL.
  • The preview card comes from the destination page’s Open Graph tags, so change those to change the card. If Facebook can’t fetch the URL, the post fails with a link error.
ParameterTypeRequiredDescription
formatstringYes"reel"
titlestringNoTitle of the Reel
page_idstringYesPage to publish to — fetch from placements
first_commentstringNoComment added after publishing
MediaMax sizeFormatsCountDuration
Video300 MBmp4, mov13 s – 90 s
  • A video is required.
Terminal window
# Reel
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": { "body": "Behind the scenes 🎬" },
"profiles": ["prof_abc123"],
"media": ["https://example.com/video.mp4"],
"platforms": {
"facebook": { "format": "reel", "title": "Launch BTS", "page_id": "123456789", "first_comment": "Link in bio!" }
}
}'
ParameterTypeRequiredDescription
formatstringYes"story"
page_idstringYesPage to publish to — fetch from placements
MediaMax sizeFormatsCountDuration
Image4 MBjpg, png1—
Video4 GBmp4, mov13 s – 60 s
  • Media is required; text captions are not supported.
  • Minimum dimensions: 500×500 px.
Terminal window
# Story (single image)
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": {},
"profiles": ["prof_abc123"],
"media": ["https://example.com/story.jpg"],
"platforms": { "facebook": { "format": "story", "page_id": "123456789" } }
}'

A Facebook profile can manage several Pages. List them with the placements endpoint — each placement’s id is the page_id you pass when creating a post.

page_id is required on every post — there is no default Page, even when the profile has only one. Pass the id of the Page you want to publish to.

Terminal window
# List the Pages (placements) for a Facebook profile
curl "https://api.postproxy.dev/api/profiles/prof_abc123/placements" \
-H "Authorization: Bearer YOUR_API_KEY"

Facebook has the most complete Comments API support. All comment writes are asynchronous and need the profile_id query parameter.

ActionSupported
List / readYes
ReplyYes
DeleteYes
EditYes
Hide / unhideYes
Like / unlikeYes
  • Comment attachments can carry photo, video, sticker, and share media.
  • Author name and avatar are fetched when Facebook returns them. Follow/verified metadata (is_verified_user, is_user_follow_business, is_business_follow_user, follower_count) is not available on Facebook — it’s Instagram-only.
Terminal window
# List comments on a post
curl "https://api.postproxy.dev/api/posts/post_abc123/comments?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
Terminal window
# Reply to a comment (omit parent_id to comment on the post itself)
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Appreciate the feedback!", "parent_id": "cmt_abc123" }'
Terminal window
# Like / unlike a comment
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123/like?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123/unlike?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
Terminal window
# Hide / unhide a comment
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123/hide?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123/unhide?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
Terminal window
# Delete a comment
curl -X DELETE "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"

Facebook Page recommendations live on the Page, not on a post, so they surface through the Profile Comments API — not the post-level Comments API. Recommendations are backfilled when the profile connects, then kept current in real time — new recommendations, edits, removals, and comments on them land within seconds.

ActionSupported
ListYes — recommendations on the Page
ReplyYes — a comment on the recommendation; several allowed
DeleteYes — removes your comment; the recommendation stays
  • platform_data.recommendation_type is "positive" or "negative". platform_data.star_rating only appears on legacy reviews left before Facebook replaced stars with recommendations (2018).
  • Meta does not expose who left a recommendation — author_username and author_avatar_url are always null.
  • placement_id is the Page ID; pass it to scope listings when the profile manages several Pages.
  • permalink points at the Page’s reviews tab (https://www.facebook.com/<page_id>/reviews) — Facebook has no per-review URLs.
Terminal window
# List recommendations (and your replies) for a profile
curl "https://api.postproxy.dev/api/profiles/prof_abc123/comments?placement_id=123456789" \
-H "Authorization: Bearer YOUR_API_KEY"
Terminal window
# Reply to a recommendation — parent_id is its Postproxy ID or story ID
curl -X POST "https://api.postproxy.dev/api/profiles/prof_abc123/comments" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"body": "Thanks for the kind words!",
"parent_id": "122219817518532572"
}'

A profile_comment.created webhook fires for each new recommendation (across all Pages of the profile).

Beyond publishing, Postproxy reads and updates the profile fields on a Facebook Page — the about text, description, website, phone, emails, and other contact and company details. This is separate from publishing to that Page.

Every endpoint is scoped to one connected profile and one Page:

/api/profiles/:profile_id/facebook/...

No extra connection or scope is needed: any Facebook profile connected to Postproxy can use these endpoints. Every request requires page_id — the Page’s Facebook ID, exactly the placement id from Placements. A page_id your profile can’t reach returns 404; a missing one returns 400.

MethodEndpointDescription
GET/api/profiles/:profile_id/facebook/pageGet the Page’s profile fields
PATCH/api/profiles/:profile_id/facebook/update_pageUpdate the Page’s profile fields

Payloads use the Graph API’s own field names, unchanged in both directions.

GET /api/profiles/:profile_id/facebook/page

ParameterTypeRequiredDescription
page_idstringYesPlacement ID (see above)
fieldsstringNoComma-separated Graph API Page fields. Defaults to the profile-oriented set below

Default fields: id, name, username, link, category, fan_count, followers_count, about, description, general_info, company_overview, mission, products, founded, website, phone, emails, impressum, price_range.

Terminal window
curl -X GET "https://api.postproxy.dev/api/profiles/prof_abc123/facebook/page?page_id=123456789" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"id": "123456789",
"name": "Acme Coffee",
"username": "acmecoffee",
"link": "https://www.facebook.com/acmecoffee",
"category": "Coffee shop",
"fan_count": 12840,
"followers_count": 13102,
"about": "Neighbourhood coffee since 2009.",
"description": "Roasted in-house. Open daily.",
"website": "https://acme.example",
"phone": "+1 555-0100",
"emails": ["[email protected]"],
"founded": "2009",
"price_range": "$$"
}

Facebook omits fields the Page has never set, so keys can be absent from the response.

PATCH /api/profiles/:profile_id/facebook/update_page

ParameterTypeRequiredDescription
page_idstringYesPlacement ID
aboutstringNoShort bio, up to 255 characters
descriptionstringNoLong-form description
general_infostringNoAdditional information
company_overviewstringNoCompany overview
missionstringNoMission statement
productsstringNoProducts
foundedstringNoFounding year or date
websitestringNoWebsite URL(s), space-separated for more than one
phonestringNoContact phone
emailsarrayNoContact email addresses
impressumstringNoLegal notice (Impressum)
price_rangestringNoOne of $, $$, $$$, $$$$, Unspecified

Send only the fields you want to change; anything omitted is left as-is. At least one field is required, or the request is a 400.

The response is the updated Page, in the same shape as Get Page.

Terminal window
curl -X PATCH "https://api.postproxy.dev/api/profiles/prof_abc123/facebook/update_page" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"page_id": "123456789",
"about": "Neighbourhood coffee since 2009. Now with a roastery.",
"website": "https://acme.example/coffee",
}'

Requires the connecting user to hold a Page role that allows editing Page info; otherwise Facebook rejects the write and the request returns 403.

StatusMeaning
400Missing page_id, no updatable field sent, or about over 255 characters
403The Page token lacks permission to edit the Page
404Profile not found, or page_id isn’t a placement of this profile
422Not a facebook profile, the connection needs re-authentication, or Facebook rejected the payload
429Facebook is rate-limiting; retry after the Retry-After header
502Facebook did not accept the connection

Facebook Messenger is supported through the Direct Messages API. Each send accepts either text or a single attachment.

CapabilitySupported
Send / receive textYes
AttachmentsYes — one per send
ReactionsYes — named reactions map to emoji (love → ❤, like → 👍, …)
Typing indicatorYes — start_typing / stop_typing
Edit outbound messageNo
Delivery / read receiptsYes
Private reply to a commentYes
Quick replies / buttonsYes — up to 13 chips, up to 3 buttons
m.me link referralsYes
Inbound deliveryWebhook
  • 24-hour window. Free-form messages are only allowed within 24 hours of the participant’s last message. Sending outside the window is not supported.
  • Private replies DM a commenter directly, bypass the 24-hour window (up to 7 days), and are allowed once per comment.
  • Quick replies and buttons. Send up to 13 quick-reply chips above the participant’s composer, or up to 3 buttons (web_url / postback) attached to the message, optionally with a card (subtitle, image_url, default_action). Buttons ship as a Meta generic template, so body is capped at 220 characters when they’re present, and they can’t be combined with media. Taps arrive as inbound messages with tapped_action carrying your payload.
  • m.me link referrals. When a user enters the thread via https://m.me/<page_username>?ref=..., the referral is stored on the chat (metadata.referral), a referral.received webhook fires, and the 24-hour window opens — you can reply before the user sends a message.
  • Start a chat with the participant’s Page-scoped ID (PSID) as participant_external_id.
Terminal window
# Create or find a chat by PSID
curl -X POST "https://api.postproxy.dev/api/profiles/prof_abc123/chats" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "participant_external_id": "7000000000000000", "participant_name": "Jane Doe" }'
Terminal window
# Send a text message
curl -X POST "https://api.postproxy.dev/api/chats/chat_abc123/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Hi! How can we help?" }'
Terminal window
# Send a single media attachment
curl -X POST "https://api.postproxy.dev/api/chats/chat_abc123/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "media": ["https://example.com/receipt.pdf"] }'
Terminal window
# Send quick-reply chips
curl -X POST "https://api.postproxy.dev/api/chats/chat_abc123/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "What can I help with?", "quick_replies": [
{ "title": "Track order", "payload": "TRACK" },
{ "title": "Talk to support", "payload": "HELP" }
] }'
Terminal window
# Send buttons attached to the message
curl -X POST "https://api.postproxy.dev/api/chats/chat_abc123/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Your order shipped", "buttons": [
{ "type": "web_url", "title": "Track", "url": "https://shop.example.com/orders/123" },
{ "type": "postback", "title": "Cancel", "payload": "CANCEL:123" }
] }'
Terminal window
# React to a message (named reaction -> emoji)
curl -X POST "https://api.postproxy.dev/api/messages/msg_abc123/react" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reaction": "like" }'
Terminal window
# Private reply to a commenter (bypasses the 24h window, once per comment)
curl -X POST "https://api.postproxy.dev/api/posts/post_abc123/comments/cmt_abc123/private_reply?profile_id=prof_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "Sent you a DM with the details!" }'

Postproxy records periodic stat snapshots you can pull as a timeseries; field names pass through from Facebook unchanged.

Page-level metrics, refreshed roughly every 23 hours and scoped per Page — pass the Page’s placement_id. See the Profile stats endpoint (GET /api/profiles/:id/stats).

Fields: fan_count, followers_count, plus daily Page insights (page_media_view, page_views_total, page_post_engagements, page_daily_follows, page_daily_unfollows, page_daily_follows_unique, page_daily_unfollows_unique)

page_media_view is Meta’s unified “Views” metric. Reach-style metrics are no longer exposed by the Facebook Page Insights API.

Terminal window
# Fetch the stats timeseries for one Page (placement_id is the page_id)
curl "https://api.postproxy.dev/api/profiles/prof_abc123/stats?placement_id=123456789" \
-H "Authorization: Bearer YOUR_API_KEY"

Per-post engagement, captured after publish via the Post stats endpoint (GET /api/posts/stats).

Fields: impressions, clicks, likes, comments, shares

Terminal window
# Fetch post stats
curl "https://api.postproxy.dev/api/posts/stats?post_ids=post_abc123&profiles=facebook" \
-H "Authorization: Bearer YOUR_API_KEY"

Subscribe with the Webhooks API. Create a subscription:

Terminal window
curl -X POST "https://api.postproxy.dev/api/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/postproxy",
"events": ["platform_post.published", "comment.created", "message.received", "reaction.received"]
}'

Events relevant to Facebook:

EventWhen
post.processedA post is ready to publish
platform_post.publishedA post was published to the platform
platform_post.failedA post failed to publish (retries exhausted)
platform_post.failed_waiting_for_retryA publish attempt failed; will retry
platform_post.insightsNew analytics snapshot
comment.createdA comment was synced on a Facebook post
profile_comment.createdA new Page recommendation was synced, or your reply was published
message.received / .sentInbound / outbound DM
message.delivered / .readDelivery and read receipts
message.editedAn inbound message was edited
reaction.receivedA reaction was added or removed on a message
referral.receivedA participant entered a DM thread via an m.me link
profile.connected / .disconnectedConnection state changed
profile.statsNew profile stats snapshot (once per Page)
media.failedA media attachment failed to process

Facebook does not report message deletions — message.deleted only fires for Instagram.

Facebook’s API doesn’t expose these features, so they aren’t available through Postproxy:

  • Demographic targeting (age, gender, or interests) — country restriction works today.
  • Going live.
  • Posting to a personal profile; publishing targets a Page only.
  • Interactive Story stickers.
  • Creating Events.
  • Posting to Groups, which Facebook has deprecated.
  • Publishing targets a Facebook Page, not a personal profile.
  • page_id is required on every post; fetch it from placements.
  • Reels are video-only and capped at 90 seconds; Stories require 500×500 px media.