Skip to content

Facebook API

Schedule and automate Facebook Page posts with Zernio API - Feed posts, Stories, multi-image, GIFs, and first comments

Quick Reference

PropertyValue
Character limit63,206 (truncated at ~480 with "See more")
Images per post10
Videos per post1
Image formatsJPEG, PNG, GIF (WebP auto-converted to JPEG)
Image max size4 MB (Facebook rejects larger in practice)
Video formatsMP4, MOV
Video max size4 GB
Video max duration240 min (feed), 120 sec (stories)
Post typesFeed (text/image/video/multi-image), Story
SchedulingYes
Inbox (DMs)Yes
Inbox (Comments)Yes
Inbox (Reviews)Yes
AnalyticsYes

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.disconnected webhook
  • Multiple Pages can be managed from one connected account

Quick Start

Post to a Facebook Page in under 60 seconds:

typescript
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);
python
result = client.posts.create_post(
    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']}")
bash
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. firstComment is skipped when draft: true.

bash
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
    }
  }'
typescript
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);
python
result = client.posts.create_post(
    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.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Just a text update for our followers.',
  platforms: [
    { platform: 'facebook', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Just a text update for our followers.",
    platforms=[
        {"platform": "facebook", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
bash
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

typescript
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
});
python
result = client.posts.create_post(
    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
)
bash
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.

typescript
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
});
python
result = client.posts.create_post(
    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
)
bash
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.

typescript
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
});
python
result = client.posts.create_post(
    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
)
bash
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.

typescript
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
});
python
result = client.posts.create_post(
    media_items=[
        {"type": "image", "url": "https://example.com/story.jpg"}
    ],
    platforms=[{
        "platform": "facebook",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "contentType": "story"
        }
    }],
    publish_now=True
)
bash
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: content is used as the Reel caption. Use platformSpecificData.title to set a separate Reel title.

bash
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
  }'
typescript
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);
python
result = client.posts.create_post(
    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: firstComment is skipped when draft: true.

typescript
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
});
python
result = client.posts.create_post(
    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
)
bash
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.

bash
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
  }'
typescript
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
});
python
result = client.posts.create_post(
    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

PropertyFeed PostStory
Max images101
FormatsJPEG, PNG, GIF (WebP auto-converted)JPEG, PNG
Max file size4 MB4 MB
Recommended1200 x 630 px1080 x 1920 px

Videos

PropertyFeed VideoStory
Max videos11
FormatsMP4, MOVMP4, MOV
Max file size4 GB4 GB
Max duration240 minutes120 seconds
Min duration1 second1 second
Recommended resolution1280 x 720 px min1080 x 1920 px
Frame rate30 fps recommended30 fps
CodecH.264H.264

Platform-Specific Fields

All fields below go inside platformSpecificData for the Facebook platform entry.

FieldTypeDescription
draftbooleanWhen 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.
titlestringReel title (only for contentType="reel"). Separate from the content caption.
firstCommentstringAuto-posted as the first comment after publish. Feed posts and Reels (not Stories). Skipped when draft is true.
pageIdstringPost to a specific Page when the connected account manages multiple Pages. Get available pages with GET /v1/accounts/{accountId}/facebook-page.
geoRestrictionobjectRestrict 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

typescript
const pages = await zernio.connect.getFacebookPages('YOUR_ACCOUNT_ID');
console.log('Available pages:', pages);
python
pages = client.connect.get_facebook_pages("YOUR_ACCOUNT_ID")
print("Available pages:", pages)
bash
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:

typescript
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);
python
result = client.posts.create_post(
    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']}")
bash
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

> Included — Analytics is bundled with every paid account on the Usage plan.

Available metrics via the Analytics API:

MetricAvailable
Impressions
Likes
Comments
Shares
Clicks
Views

Facebook also provides a dedicated Page Insights API for page-level aggregates (media views, post engagements, video metrics, follower count). Uses current (post-November 2025) Meta metric names; the legacy page_impressions / page_fans / page_fan_adds / page_fan_removes metrics were deprecated by Meta and are rejected by this endpoint. followers_gained and followers_lost are synthesized from Zernio's daily follower snapshotter.

typescript
const analytics = await zernio.analytics.getAnalytics({
  platform: 'facebook',
  fromDate: '2024-01-01',
  toDate: '2024-01-31'
});
console.log(analytics.posts);
python
analytics = client.analytics.get_analytics(
    platform="facebook",
    from_date="2024-01-01",
    to_date="2024-01-31"
)
print(analytics["posts"])
bash
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

ErrorMeaningFix
"Photos should be smaller than 4MB and saved as JPG or PNG."Image exceeds actual size limit or unsupported formatReduce to under 4 MB. Use JPEG or PNG.
"Missing or invalid image file"Facebook couldn't process image -- corrupt, wrong format, or inaccessible URLVerify 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 videoUse a direct, publicly accessible URL. Avoid cloud storage sharing links.
"Facebook tokens expired. Please reconnect."OAuth token expiredReconnect 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 triggeredLog into Facebook, go to the Page, and complete identity verification.
"Publishing failed due to max retries reached"All 3 retry attempts failedUsually temporary. Retry manually or wait and try again.

Inbox

> Included — Inbox (DMs, comments, reviews) is bundled with every paid account on the Usage plan.

Facebook has the most complete inbox support across DMs, comments, and reviews.

Direct Messages

FeatureSupported
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:

EventWhen it fires
message.receivedNew incoming DM
message.sentOutgoing DM is sent
message.editedThe sender edits a previously-sent message (up to 5 edits per Meta)
message.deliveredAn outgoing DM is delivered to the recipient
message.readThe 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

FeatureSupported
List comments on posts
Reply to comments
Delete comments
Like comments
Hide/unhide comments
Send private reply (DM after comment)✅ (text + up to 13 quick replies OR 1-3 inline buttons, 7-day window, one per comment)

Reviews (Pages)

FeatureSupported
List reviews
Reply to reviews
  • 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