Platforms
Connect a Facebook Page, publish text, photo, album, video, Reel and Story posts, manage comments and Messenger conversations, and read post and Page analytics.
Publish to a Page, answer comments and Messenger, and read Page analytics.
Quick Reference
| Property | Value |
|---|---|
| Account types | Facebook Pages |
| Connection method | OAuth, one account per Page |
| Post types | text, image, multi_image, video, reel, stories |
| Max media per post | 10 |
| Text limit | 63,206 characters, title 100 (Reels) |
| Threads | No |
| Quotes | No |
| Remote delete | |
| Comments | |
| Likes | |
| Reposts | No |
| Bookmarks | No |
| Direct messages | Messenger |
| Post analytics | |
| Account analytics | Stats and insights |
Each account is one Page. Profiles and Groups cannot connect. Posts made on the Page show up on their own, usually within a minute. Older ones need a sync.
Connect
Connect a Page in Dashboard → Connections, or with a connect session.
Each Page is its own account. Your Facebook role on the Page sets what works.
| Feature | Page access needed |
|---|---|
| Publishing | Content |
| Inbox | Messages |
| Analytics | Insights |
Facebook shows a Page picker during connect. To show your own picker instead, add headless: true. See Pick a Page for the steps.
Permissions
| Scope | Used for |
|---|---|
pages_show_list, business_management | Listing your Pages |
pages_manage_posts | Publishing and deleting |
pages_read_engagement | Reading posts and counts |
pages_read_user_content | Comments and recommendations |
pages_manage_engagement | Commenting, hiding and liking |
read_insights | Analytics |
pages_messaging | Messenger and private replies |
pages_manage_metadata | Webhooks for comments, messages and posts |
public_profile | Facebook sign-in |
Staying Connected
Access renews itself, so canAutoRenew is true.
Access ends on a password change, an app removal or a lost Page role. Then reconnect. The id stays the same. Do the same on a *_PERMISSION code.
Publish
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Our spring menu is live.",
"accounts": [
{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"facebook": {
"link": "https://example.com/spring-menu"
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"source": "onepostly",
"status": "publishing",
"text": "Our spring menu is live.",
"mediaKind": "text",
"media": [],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"facebook": {
"link": "https://example.com/spring-menu"
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}Once published, url links to the post. Stories have no url.
Post Types
mediaKind | Publishes | Media |
|---|---|---|
text | Feed post | None, text or link required |
image | Photo post | 1 image |
multi_image | Album or link carousel | 2 to 10 images |
video | Feed video | 1 video |
reel | Reel | 1 vertical video |
stories | Story, for 24 hours | 1 image or video |
- One video without
mediaKindis a feedvideo. Setreelorstories. - Text goes up to 63,206 characters. Stories show no text.
- An album with a video fails with
INVALID_MEDIA.
A feed video is published once Facebook accepts it. Its platformPostId and url later switch to the post, with a post.updated.
Reel
facebook.reelTitle adds a title, apart from the caption.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Behind the counter on a Saturday morning.",
"mediaKind": "reel",
"media": [
{
"url": "https://cdn.onepostly.com/media/saturday.mp4"
}
],
"accounts": [
{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"facebook": {
"reelTitle": "Saturday rush"
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"source": "onepostly",
"status": "publishing",
"text": "Behind the counter on a Saturday morning.",
"mediaKind": "reel",
"media": [
{
"url": "https://cdn.onepostly.com/media/saturday.mp4"
}
],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"facebook": {
"reelTitle": "Saturday rush"
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}A Story takes no options, not even firstComment or geoRestriction. Sending either one on a Story returns 400.
Options
Options go under facebook. On the wrong media kind, they return 400. The exceptions are carouselCards, carouselLink and textFormatPresetId, which are checked when the post publishes.
Prop
Type
If Facebook refuses firstComment, the post still publishes.
Link Carousel
carouselCards gives each image of a multi_image post its own link.
Send one card per image, in media order. Each card's name and description can be up to 255 characters long.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "New arrivals this week.",
"mediaKind": "multi_image",
"media": [
{
"url": "https://cdn.onepostly.com/media/chair.jpg"
},
{
"url": "https://cdn.onepostly.com/media/lamp.jpg"
}
],
"accounts": [
{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"facebook": {
"carouselLink": "https://example.com/new",
"carouselCards": [
{
"link": "https://example.com/new/chair",
"name": "Oak chair",
"description": "Solid oak, hand finished"
},
{
"link": "https://example.com/new/lamp",
"name": "Brass lamp"
}
]
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"source": "onepostly",
"status": "publishing",
"text": "New arrivals this week.",
"mediaKind": "multi_image",
"media": [
{
"url": "https://cdn.onepostly.com/media/chair.jpg"
},
{
"url": "https://cdn.onepostly.com/media/lamp.jpg"
}
],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"facebook": {
"carouselLink": "https://example.com/new",
"carouselCards": [
{
"link": "https://example.com/new/chair",
"name": "Oak chair",
"description": "Solid oak, hand finished"
},
{
"link": "https://example.com/new/lamp",
"name": "Brass lamp"
}
]
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}A wrong media kind or card count fails with INVALID_CAROUSEL. Without cards, carouselLink is ignored and the post is a plain album.
Text Background
textFormatPresetId shows a text post as large text on a color. On other kinds, the post fails with TEXT_FORMAT_UNSUPPORTED.
Geo Restriction
geoRestriction shows the post only in the listed countries. It works on text and multi_image. Other kinds return 400.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Free shipping this week in the US and Canada.",
"mediaKind": "multi_image",
"media": [
{
"url": "https://cdn.onepostly.com/media/ship-1.jpg"
},
{
"url": "https://cdn.onepostly.com/media/ship-2.jpg"
}
],
"accounts": [
{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"facebook": {
"geoRestriction": [
"US",
"CA"
]
}
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"source": "onepostly",
"status": "publishing",
"text": "Free shipping this week in the US and Canada.",
"mediaKind": "multi_image",
"media": [
{
"url": "https://cdn.onepostly.com/media/ship-1.jpg"
},
{
"url": "https://cdn.onepostly.com/media/ship-2.jpg"
}
],
"thread": [],
"scheduledFor": null,
"timezone": null,
"options": {
"facebook": {
"geoRestriction": [
"US",
"CA"
]
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T09:00:00.000Z"
}
]
}Media Requirements
These are Facebook's limits, on top of the Media URL Requirements.
| Media | Formats | Max size |
|---|---|---|
| Images | JPEG, PNG and GIF | 4 MB |
| Videos | MP4, MOV, AVI and WebM | 1 GB |
| Reels | MP4, MOV, AVI and WebM | 4 GB |
| Story videos | MP4, MOV, AVI and WebM | 100 MB |
A file outside these formats and sizes is refused. See Platform Limits.
Images
| Property | Requirement |
|---|---|
| Per post | 1, or 2 to 10 on multi_image |
Videos
| Property | Feed video | Reel | Story |
|---|---|---|---|
| Duration | Facebook's limit | 3 to 90 s | 3 to 60 s |
| Aspect ratio | Any | 9:16 | 9:16 |
| Resolution | Any | 1080 × 1920, min 540 × 960 | Same |
| Frame rate | Any | 24 to 60 fps | 24 to 60 fps |
A URL ending in .mp4, .mov, .m4v, .webm or .avi is a video.
Scheduling, Editing and Deleting
Scheduling and editing work as usual. Published posts cannot be edited.
Deleting removes a post from the Page, including one made on Facebook. A post already gone counts as deleted.
A post deleted on the Page itself turns deleted and sends post.deleted, usually within a minute.
Engagement
Act as the Page. See Comments and Likes and Reposts.
| Action | Supported | Notes |
|---|---|---|
| Like or unlike a post | Repeats succeed | |
| List comments | Up to 25 replies each. Not on Stories | |
| Comment or reply | Up to 8,000 characters | |
| Delete a comment | When Facebook allows it | |
| Hide or unhide | The author and friends still see it | |
| Like a comment | Repeats succeed | |
| Private reply | One per comment, 7 days, 2,000 characters | |
| Repost, quote, bookmark | No | UNSUPPORTED_PLATFORM |
Like a post by its pst_ id or Facebook id. Another Page's pst_ id fails.
username on a comment is the commenter's name, or null. A comment removed on Facebook sends a comment.deleted webhook.
Inbox
The Page's Messenger inbox. See Inbox for the requests.
| Action | Supported | Notes |
|---|---|---|
| List and read | muted is always false | |
| Start a conversation | By Page-scoped id (PSID) | |
| Send text | Up to 2,000 characters, within 24 hours | |
| Rich messages | Attachments, quick replies, buttons, cards | |
| Ice breakers and menu | See Messenger Setup | |
| Automations | Up to 20 rules per Page | |
| Message tags | No | No sends after 24 hours |
Facebook has no username lookup. Find a PSID in members or a senderId.
import { Configuration, InboxApi } from "@onepostly/sdk";
const inboxApi = new InboxApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await inboxApi.createConversation({
id: "acc_YOUR_FACEBOOK_ACCOUNT_ID",
createConversationBody: {
"recipient": "8261937452018734"
},
});
console.log(response);{
"id": "cnv_9Xc4Vb7Nm2Lk5Jh8Gf1Ds6Az3Qw0Er9Ty4Ui7Op2As5Df8Gh1Jk6Lz3Xc0Vb9Nm4Qw7Er2Ty5Ui8Op1As6Df3Gh0Jk",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"members": [
{
"id": "104987654321098",
"username": null,
"displayName": null
},
{
"id": "8261937452018734",
"username": null,
"displayName": null
}
],
"muted": false,
"unreadCount": 0,
"lastMessage": null
}Until a new conversation has its first message, you get CONVO_NOT_FOUND.
With attachmentUrl or cards, no text shows. With buttons, text stops at 640. Inbox webhooks include message.delivered, but not unsent messages.
Messenger Setup
A Page can show up to 4 ice breakers and a menu of up to 3 items. See Messenger Profile for the full request and its fields.
import { Configuration, AccountsApi } from "@onepostly/sdk";
const accountsApi = new AccountsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await accountsApi.setMessengerProfile({
id: "acc_YOUR_FACEBOOK_ACCOUNT_ID",
setMessengerProfileBody: {
"iceBreakers": [
{
"question": "What are your opening hours?",
"payload": "HOURS"
},
{
"question": "Do you deliver?",
"payload": "DELIVERY"
}
],
"persistentMenu": [
{
"type": "web_url",
"title": "Order online",
"url": "https://example.com/order"
}
]
},
});
console.log(response);{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"iceBreakers": [
{
"question": "What are your opening hours?",
"payload": "HOURS"
},
{
"question": "Do you deliver?",
"payload": "DELIVERY"
}
],
"persistentMenu": [
{
"type": "web_url",
"title": "Order online",
"url": "https://example.com/order",
"payload": null
}
]
}A tap on an ice breaker or postback item sends message.postback.
Analytics
Metrics
| Field | Facebook metric | Notes |
|---|---|---|
impressions | post_media_view | |
reach | post_total_media_view_unique | |
views | post_video_views | Only video and stories |
likes | Likes | Else total reactions |
comments | Comments | |
shares | Shares | |
saves | None | Always null |
engagement | Likes + comments + shares | A count |
Read Post Analytics
import { Configuration, AnalyticsApi } from "@onepostly/sdk";
const analyticsApi = new AnalyticsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await analyticsApi.getPostAnalytics({
id: "pst_YOUR_FACEBOOK_POST",
});
console.log(response);{
"postId": "pst_YOUR_FACEBOOK_POST",
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "ready",
"metrics": {
"impressions": 1234,
"reach": 987,
"likes": 123,
"comments": 12,
"shares": 4,
"saves": null,
"views": null,
"engagement": 139
},
"fetchedAt": "2027-03-15T10:00:00.000Z",
"error": null
}A fresh post often reads pending. A deleted one reads unavailable, for good. Stories update about hourly while live, then keep their last values.
Account Stats
Get Account Stats returns live Page numbers. followerCount falls back to likes. followingCount and videoCount are null.
Page Reviews
List Page Reviews lists recommendations, newest first.
import { Configuration, AccountsApi } from "@onepostly/sdk";
const accountsApi = new AccountsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await accountsApi.listAccountReviews({
id: "acc_YOUR_FACEBOOK_ACCOUNT_ID",
limit: 20,
});
console.log(response);{
"data": [
{
"id": "1093847561029384",
"recommendation": "positive",
"rating": null,
"text": "Friendly staff and the best sourdough in the neighborhood.",
"reviewerName": "Jane Doe",
"createdAt": "2027-03-14T16:20:00.000Z"
},
{
"id": "1093847561027710",
"recommendation": "negative",
"rating": null,
"text": "Waited 20 minutes for a coffee on Saturday.",
"reviewerName": null,
"createdAt": "2027-03-09T11:05:00.000Z"
},
{
"id": "1093847561015526",
"recommendation": null,
"rating": 5,
"text": null,
"reviewerName": "Sam Lee",
"createdAt": "2018-06-02T08:45:00.000Z"
}
],
"nextCursor": "QVFIUjRtNWdLbGd6UXBVZAk1jY0FfTnJ3dkx2S0RxZAXhOQmZAPNnV5dE1CSmVvUHg",
"hasMore": true
}recommendation is positive or negative, and rating is the legacy 1 to 5 stars. Any field can be null.
A new review sends a review.received webhook.
Account Insights
Get Account Analytics reads Page Insights live.
import { Configuration, AnalyticsApi } from "@onepostly/sdk";
const analyticsApi = new AnalyticsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await analyticsApi.getAccountAnalytics({
id: "acc_YOUR_FACEBOOK_ACCOUNT_ID",
metrics: "page_media_view",
period: "day",
});
console.log(response);{
"accountId": "acc_YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"metrics": [
{
"name": "page_media_view",
"period": "day",
"values": [
{
"value": 3412,
"endTime": "2027-03-14T07:00:00.000Z"
},
{
"value": 2987,
"endTime": "2027-03-15T07:00:00.000Z"
}
]
}
],
"demographics": {}
}| Parameter | Facebook values |
|---|---|
metrics | Up to 20 Page Insights metric names |
period | day (default), week, days_28, lifetime |
since, until | A time window |
- The defaults are
page_media_view,page_total_media_view_unique,page_post_engagements,page_followsandpage_daily_follows_unique. demographicsis always empty. Instagram-only parameters are ignored.- A retired metric fails the read with
400. The message names it.
Recent Posts
List Account Media lists recent posts. Its metrics are null.
Limitations
Not available for Facebook through Onepostly.
- Profiles and Groups
- Editing published posts, sharing and reposting
- Live and Events
- Story captions, links, stickers and comments
- Message tags after 24 hours
- Saves and demographics
Errors
Breaking a rule on this page returns 400 VALIDATION_ERROR and creates nothing. Every code is listed in Errors, with its cause and fix.
Publish Errors
| Code | Cause | Fix |
|---|---|---|
TEXT_REQUIRED | A text post with no text or link | Add one |
INVALID_MULTI_IMAGE | Over 10 images | Send 2 to 10 |
INVALID_MEDIA | A video in an album, or a non-video Reel | See Media Requirements |
INVALID_CAROUSEL | Cards without multi_image, or a wrong count | One card per image |
TEXT_FORMAT_UNSUPPORTED | A text background on a media post | Use it on text posts |
REEL_UPLOAD_FAILED, STORY_UPLOAD_FAILED | Facebook did not open the upload | Post again |
PERMISSION_DENIED | No publish permission or Page role | Reconnect, or get content access |
DELETE_PERMISSION | Facebook refused the delete | Reconnect with every permission |
Engagement and Inbox Errors
| Code | Cause | Fix |
|---|---|---|
POST_ACCOUNT_MISMATCH | Another Page's pst_ id | Use the Facebook id |
UNSUPPORTED_MEDIA | Comments on a Story | Stories have none |
COMMENTS_PERMISSION | The Page may not act on the comment | Reconnect with every permission |
PRIVATE_REPLY_FAILED | A second reply, or after 7 days | One per comment, within 7 days |
RECIPIENT_NOT_FOUND | Not a valid PSID | Use a numeric PSID |
MESSAGE_WINDOW_EXPIRED | Over 24 hours since their message | Wait for them to write |
INBOX_PERMISSION | No messaging permission or role | Reconnect, or get message access |
PROFILE_FAILED | Ice breakers or menu did not save | Read the message |
Analytics Errors
| Code | Cause | Fix |
|---|---|---|
ANALYTICS_PENDING | No metrics yet | Read again later |
POST_NOT_FOUND | Deleted on Facebook | None, this is final |
ANALYTICS_PERMISSION | No insights permission or role | Reconnect, or get insights access |
REVIEWS_FAILED | Reviews did not load | Read again later |
Connect an Instagram professional account, publish photos, carousels, Reels and Stories, moderate comments, answer direct messages and read post and account analytics.
Threads
Connect a Threads profile, publish posts, reply chains, replies and quotes, manage replies and reposts, and read Threads post metrics.