Facebook API
Schedule and automate Facebook Page posts with Zernio API - Feed posts, Stories, multi-image, GIFs, and first comments
Quick Reference
| Property | Value |
|---|---|
| Character limit | 63,206 (truncated at ~480 with "See more") |
| Images per post | 10 |
| Videos per post | 1 |
| Image formats | JPEG, PNG, GIF (WebP auto-converted to JPEG) |
| Image max size | 4 MB (Facebook rejects larger in practice) |
| Video formats | MP4, MOV |
| Video max size | 4 GB |
| Video max duration | 240 min (feed), 120 sec (stories) |
| Post types | Feed (text/image/video/multi-image), Story |
| Scheduling | Yes |
| Inbox (DMs) | Yes (add-on) |
| Inbox (Comments) | Yes (add-on) |
| Inbox (Reviews) | Yes (add-on) |
| Analytics | Yes |
Before You Start
Facebook API only posts to Pages, not personal profiles. You must have a Facebook Page and admin access to it. Also: Facebook often rejects photos larger than 4 MB even though the stated limit is higher. Keep images under 4 MB and use JPEG or PNG format. WebP images are auto-converted to JPEG by Zernio.
- API posts to Pages only (not personal profiles)
- User must be Page Admin or Editor
- Facebook tokens expire frequently -- subscribe to the
account.disconnectedwebhook - Multiple Pages can be managed from one connected account
Quick Start
Post to a Facebook Page in under 60 seconds:
const { post } = await zernio.posts.createPost({
content: 'Hello from Zernio API!',
platforms: [
{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
});
console.log('Posted to Facebook!', post._id);result = client.posts.create(
content="Hello from Zernio API!",
platforms=[
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
publish_now=True
)
post = result.post
print(f"Posted to Facebook! {post['_id']}")curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from Zernio API!",
"platforms": [
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'Content Types
Draft Posts (Publishing Tools)
Create an unpublished Facebook draft that appears in Facebook Publishing Tools instead of publishing immediately. This is useful for review/approval workflows.
Note: Drafts are supported for feed posts (text, link, image, video) and reels. Drafts are not supported for stories.
firstCommentis skipped whendraft: true.
curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Draft post for review before publishing",
"platforms": [{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID"
}],
"publishNow": true,
"facebookSettings": {
"draft": true
}
}'const { post } = await zernio.posts.createPost({
content: 'Draft post for review before publishing',
platforms: [{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }],
publishNow: true,
facebookSettings: {
draft: true
}
});
console.log('Draft created!', post._id);result = client.posts.create(
content="Draft post for review before publishing",
platforms=[{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}],
publish_now=True,
facebook_settings={"draft": True}
)
post = result.post
print(f"Draft created! {post['_id']}")Text-Only Post
No media required. Facebook is one of the few platforms that supports text-only posts.
const { post } = await zernio.posts.createPost({
content: 'Just a text update for our followers.',
platforms: [
{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
});result = client.posts.create(
content="Just a text update for our followers.",
platforms=[
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Just a text update for our followers.",
"platforms": [
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'Single Image Post
const { post } = await zernio.posts.createPost({
content: 'Check out this photo!',
mediaItems: [
{ type: 'image', url: 'https://example.com/photo.jpg' }
],
platforms: [
{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
});result = client.posts.create(
content="Check out this photo!",
media_items=[
{"type": "image", "url": "https://example.com/photo.jpg"}
],
platforms=[
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out this photo!",
"mediaItems": [
{"type": "image", "url": "https://example.com/photo.jpg"}
],
"platforms": [
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'Multi-Image Post
Facebook supports up to 10 images in a single post. You cannot mix images and videos in the same post.
const { post } = await zernio.posts.createPost({
content: 'Photo dump from the weekend!',
mediaItems: [
{ type: 'image', url: 'https://example.com/photo1.jpg' },
{ type: 'image', url: 'https://example.com/photo2.jpg' },
{ type: 'image', url: 'https://example.com/photo3.jpg' }
],
platforms: [
{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
});result = client.posts.create(
content="Photo dump from the weekend!",
media_items=[
{"type": "image", "url": "https://example.com/photo1.jpg"},
{"type": "image", "url": "https://example.com/photo2.jpg"},
{"type": "image", "url": "https://example.com/photo3.jpg"}
],
platforms=[
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Photo dump from the weekend!",
"mediaItems": [
{"type": "image", "url": "https://example.com/photo1.jpg"},
{"type": "image", "url": "https://example.com/photo2.jpg"},
{"type": "image", "url": "https://example.com/photo3.jpg"}
],
"platforms": [
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'Video Post
A single video per post. For GIFs, use type: 'video' -- they are treated as videos internally, auto-play, and loop.
const { post } = await zernio.posts.createPost({
content: 'Watch our latest video!',
mediaItems: [
{ type: 'video', url: 'https://example.com/video.mp4' }
],
platforms: [
{ platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
});result = client.posts.create(
content="Watch our latest video!",
media_items=[
{"type": "video", "url": "https://example.com/video.mp4"}
],
platforms=[
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Watch our latest video!",
"mediaItems": [
{"type": "video", "url": "https://example.com/video.mp4"}
],
"platforms": [
{"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'Story (Image or Video)
Stories are 24-hour ephemeral content. Media is required. Text captions are not displayed on Stories, and interactive stickers are not supported via the API.
const { post } = await zernio.posts.createPost({
mediaItems: [
{ type: 'image', url: 'https://example.com/story.jpg' }
],
platforms: [{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
contentType: 'story'
}
}],
publishNow: true
});result = client.posts.create(
media_items=[
{"type": "image", "url": "https://example.com/story.jpg"}
],
platforms=[{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"contentType": "story"
}
}],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mediaItems": [
{"type": "image", "url": "https://example.com/story.jpg"}
],
"platforms": [{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"contentType": "story"
}
}],
"publishNow": true
}'Reel (Video)
Publish a Facebook Reel (short vertical video). Reels require a single vertical video.
Note:
contentis used as the Reel caption. UseplatformSpecificData.titleto set a separate Reel title.
curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Behind the scenes 🎬",
"mediaItems": [
{"type": "video", "url": "https://example.com/reel.mp4"}
],
"platforms": [{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"contentType": "reel",
"title": "Studio day"
}
}],
"publishNow": true
}'const { post } = await zernio.posts.createPost({
content: 'Behind the scenes 🎬',
mediaItems: [
{ type: 'video', url: 'https://example.com/reel.mp4' }
],
platforms: [{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
contentType: 'reel',
title: 'Studio day'
}
}],
publishNow: true
});
console.log('Posted Reel to Facebook!', post._id);result = client.posts.create(
content="Behind the scenes 🎬",
media_items=[
{"type": "video", "url": "https://example.com/reel.mp4"}
],
platforms=[{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"contentType": "reel",
"title": "Studio day"
}
}],
publish_now=True
)
post = result.post
print(f"Posted Reel to Facebook! {post['_id']}")Reel requirements
- Single video only (no images)
- Vertical video recommended (9:16)
- Duration: 3–60 seconds
First Comment
Auto-post a first comment immediately after your post is published. Does not work with Stories.
Note:
firstCommentis skipped whendraft: true.
const { post } = await zernio.posts.createPost({
content: 'New product launch!',
mediaItems: [
{ type: 'image', url: 'https://example.com/product.jpg' }
],
platforms: [{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
firstComment: 'Link to purchase: https://shop.example.com'
}
}],
publishNow: true
});result = client.posts.create(
content="New product launch!",
media_items=[
{"type": "image", "url": "https://example.com/product.jpg"}
],
platforms=[{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"firstComment": "Link to purchase: https://shop.example.com"
}
}],
publish_now=True
)curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "New product launch!",
"mediaItems": [
{"type": "image", "url": "https://example.com/product.jpg"}
],
"platforms": [{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"firstComment": "Link to purchase: https://shop.example.com"
}
}],
"publishNow": true
}'Geo-Restriction
Restrict who can see your Facebook post by country. This is a hard visibility restriction: users outside the specified countries cannot see the post at all. Supported for feed posts, videos, and reels. Not supported for stories.
curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "This post is only visible in the US and Spain",
"platforms": [{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"geoRestriction": {
"countries": ["US", "ES"]
}
}
}],
"publishNow": true
}'const { post } = await zernio.posts.createPost({
content: 'This post is only visible in the US and Spain',
platforms: [{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
geoRestriction: {
countries: ['US', 'ES']
}
}
}],
publishNow: true
});result = client.posts.create(
content="This post is only visible in the US and Spain",
platforms=[{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"geoRestriction": {
"countries": ["US", "ES"]
}
}
}],
publish_now=True
)geoRestriction.countries accepts up to 25 uppercase ISO 3166-1 alpha-2 country codes (e.g. "US", "GB", "DE").
Media Requirements
Images
| Property | Feed Post | Story |
|---|---|---|
| Max images | 10 | 1 |
| Formats | JPEG, PNG, GIF (WebP auto-converted) | JPEG, PNG |
| Max file size | 4 MB | 4 MB |
| Recommended | 1200 x 630 px | 1080 x 1920 px |
Videos
| Property | Feed Video | Story |
|---|---|---|
| Max videos | 1 | 1 |
| Formats | MP4, MOV | MP4, MOV |
| Max file size | 4 GB | 4 GB |
| Max duration | 240 minutes | 120 seconds |
| Min duration | 1 second | 1 second |
| Recommended resolution | 1280 x 720 px min | 1080 x 1920 px |
| Frame rate | 30 fps recommended | 30 fps |
| Codec | H.264 | H.264 |
Platform-Specific Fields
All fields below go inside platformSpecificData for the Facebook platform entry.
| Field | Type | Description |
|---|---|---|
draft | boolean | When true, creates an unpublished draft in Facebook Publishing Tools instead of publishing immediately. Not supported for Stories. |
contentType | "story" | "reel" | Set to "story" for Page Stories (24h ephemeral) or "reel" for Reels (short vertical video). Defaults to feed post if omitted. |
title | string | Reel title (only for contentType="reel"). Separate from the content caption. |
firstComment | string | Auto-posted as the first comment after publish. Feed posts and Reels (not Stories). Skipped when draft is true. |
pageId | string | Post to a specific Page when the connected account manages multiple Pages. Get available pages with GET /v1/accounts/{accountId}/facebook-page. |
geoRestriction | object | Restrict post visibility to specific countries. See Geo-Restriction below. |
Multi-Page Posting
If your connected Facebook account manages multiple Pages, you can list them, set a default, or override per post.
List Available Pages
const pages = await zernio.accounts.getFacebookPage('YOUR_ACCOUNT_ID');
console.log('Available pages:', pages);pages = client.accounts.get_facebook_page("YOUR_ACCOUNT_ID")
print("Available pages:", pages)curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/facebook-page \
-H "Authorization: Bearer YOUR_API_KEY"Post to Multiple Pages
Use the same accountId multiple times with different pageId values:
const { post } = await zernio.posts.createPost({
content: 'Exciting news from all our brands!',
platforms: [
{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: { pageId: '111111111' }
},
{
platform: 'facebook',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: { pageId: '222222222' }
}
],
publishNow: true
});
console.log('Posted to Facebook!', post._id);result = client.posts.create(
content="Exciting news from all our brands!",
platforms=[
{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {"pageId": "111111111"}
},
{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {"pageId": "222222222"}
}
],
publish_now=True
)
post = result.post
print(f"Posted to Facebook! {post['_id']}")curl -X POST https://zernio.com/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Exciting news from all our brands!",
"platforms": [
{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"pageId": "111111111"
}
},
{
"platform": "facebook",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"pageId": "222222222"
}
}
],
"publishNow": true
}'You can also set a default page with POST /v1/accounts/{accountId}/facebook-page so you don't need to pass pageId on every request.
Media URL Requirements
- URLs must be publicly accessible via HTTPS
- No redirects, no authentication
- Cloud storage sharing links (Google Drive, Dropbox) may not work -- use direct download URLs
- WebP images are auto-converted to JPEG before upload
Analytics
Requires Analytics add-on
Available metrics via the Analytics API:
| Metric | Available |
|---|---|
| Impressions | ✅ |
| Likes | ✅ |
| Comments | ✅ |
| Shares | ✅ |
| Clicks | ✅ |
| Views | ✅ |
const analytics = await zernio.analytics.getAnalytics({
platform: 'facebook',
fromDate: '2024-01-01',
toDate: '2024-01-31'
});
console.log(analytics.posts);analytics = client.analytics.get(
platform="facebook",
from_date="2024-01-01",
to_date="2024-01-31"
)
print(analytics["posts"])curl "https://zernio.com/api/v1/analytics?platform=facebook&fromDate=2024-01-01&toDate=2024-01-31" \
-H "Authorization: Bearer YOUR_API_KEY"What You Can't Do
- Post to personal profiles (Pages only)
- Create Events
- Post to Groups (deprecated by Facebook)
- Go Live (requires the separate Facebook Live API)
- Add interactive story stickers
- Target audiences by demographics (age, gender, interests) for organic posts (country-level geo-restriction IS supported, see Geo-Restriction)
Common Errors
| Error | Meaning | Fix |
|---|---|---|
| "Photos should be smaller than 4MB and saved as JPG or PNG." | Image exceeds actual size limit or unsupported format | Reduce to under 4 MB. Use JPEG or PNG. |
| "Missing or invalid image file" | Facebook couldn't process image -- corrupt, wrong format, or inaccessible URL | Verify URL in an incognito browser. Ensure JPEG/PNG under 4 MB. |
| "Unable to fetch video file from URL." | Facebook's servers couldn't download the video | Use a direct, publicly accessible URL. Avoid cloud storage sharing links. |
| "Facebook tokens expired. Please reconnect." | OAuth token expired | Reconnect the account. Facebook tokens have shorter lifespans. Subscribe to the account.disconnected webhook. |
| "Confirm your identity before you can publish as this Page." | Facebook security check triggered | Log into Facebook, go to the Page, and complete identity verification. |
| "Publishing failed due to max retries reached" | All 3 retry attempts failed | Usually temporary. Retry manually or wait and try again. |
Inbox
Requires Inbox add-on — Build: +$10/mo · Accelerate: +$50/unit · Unlimited: +$1,000/mo
Facebook has the most complete inbox support across DMs, comments, and reviews.
Direct Messages
| Feature | Supported |
|---|---|
| List conversations | ✅ |
| Fetch messages | ✅ |
| Send text messages | ✅ |
| Send attachments | ✅ (images, videos, audio, files) |
| Quick replies | ✅ (up to 13, Meta quick_replies) |
| Buttons | ✅ (up to 3, generic template) |
| Carousels | ✅ (generic template, up to 10 elements) |
| Message tags | ✅ (4 types) |
| Archive/unarchive | ✅ |
Message tags: Use messagingType: "MESSAGE_TAG" with one of: CONFIRMED_EVENT_UPDATE, POST_PURCHASE_UPDATE, ACCOUNT_UPDATE, or HUMAN_AGENT to send messages outside the 24-hour messaging window.
Webhooks
Messenger emits the full set of message lifecycle webhooks:
| Event | When it fires |
|---|---|
message.received | New incoming DM |
message.sent | Outgoing DM is sent |
message.edited | The sender edits a previously-sent message (up to 5 edits per Meta) |
message.delivered | An outgoing DM is delivered to the recipient |
message.read | The recipient reads an outgoing DM |
Messages are stored locally via webhooks. See the Webhooks page for payload details.
Note: Messenger does not expose incoming-message unsend via webhook, so message.deleted is not emitted for Facebook. This is a platform limitation.
Persistent Menu
Manage the persistent menu shown in Facebook Messenger conversations. Max 3 top-level items, max 5 nested items.
See Account Settings for the GET/PUT/DELETE /v1/accounts/{accountId}/messenger-menu endpoints.
Comments
| Feature | Supported |
|---|---|
| List comments on posts | ✅ |
| Reply to comments | ✅ |
| Delete comments | ✅ |
| Like comments | ✅ |
| Hide/unhide comments | ✅ |
Reviews (Pages)
| Feature | Supported |
|---|---|
| List reviews | ✅ |
| Reply to reviews | ✅ |
Related Endpoints
- Connect Facebook Account - OAuth flow
- Create Post - Post creation and scheduling
- Upload Media - Image and video uploads
- Analytics - Post performance metrics
- Messages, Comments, and Reviews
- Account Settings - Persistent menu configuration