Skip to main content

Quick reference

FieldValue
Caption limit63,206 characters
Photos per post1–10
Videos per post1
Mixed media❌ Not on organic Page posts
Image formatsJPG, PNG, GIF, WebP, HEIC, HEIF
Video formatsMP4, MOV, AVI, WebM
Max image size5 GB
Max video size5 GB
Reel duration3–90 seconds
Feed video durationup to 240 minutes
Reels per Page30 per 24 hours
Post typesFeed, Photo, Photo Carousel, Video, Reel
First comment✅ Auto-posts after publish (not on Reels)
Account typesFacebook Pages (not personal profiles)
The image/video size caps above are Postbreeze’s server-side ingest limits. Facebook’s own publish-time caps are tighter (Reels must be 9:16 and 3–90s, max 30 Reels per Page per 24h, etc.) — exceed those and the publish step returns an error from Meta even though the upload succeeded.

Before you start

You can only publish to Facebook Pages you administer. Personal-profile publishing isn’t exposed by the Graph API. The OAuth flow enumerates the Pages on your account during connect — pick the Page you want to publish to from that list.
Required scopes:
  • pages_show_list — read the list of Pages you administer.
  • pages_manage_posts — create posts on your Pages.
  • pages_manage_engagement + pages_read_engagement — first-comment + inbox replies.
  • pages_read_user_content — inbox comments-on-your-posts feed.
Reels require Meta App Review with the Reels use-case enabled. Before review approval, video posts work fine but kind: "REEL" will return FB_REEL_NOT_APPROVED.

Quick start

Workspace is inferred from your API key — no workspaceId argument and no workspace in the URL. Use the flat shape (content + platforms) for the common case; drop to the nested shape at the bottom of this page only when a power-user workflow demands it.
import Postbreeze from "@postbreeze/node";

const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY });

const post = await postbreeze.posts.create({
  content: "Our new feature drop — read more 👇",
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: {
      platform: "FACEBOOK_PAGE",
      kind: "FEED",
      link: "https://example.com/blog/new-feature",
    },
  }],
});
See Platform settings → Facebook for the full platformOptions reference and Media uploads for the two ways to attach images and video.

Content types

kind: "FEED". Pure text post, optionally with a link whose OG preview Facebook auto-generates. When you attach media to a FEED post, the server auto-normalizes it to PHOTO (1 image) or PHOTO_CAROUSEL (2–10) — you don’t have to set kind yourself.

Photo (single image)

kind: "PHOTO". Send exactly one image, either by mediaIds (the presign flow) or by mediaItems (URL ingest).
Node.js — pre-uploaded media
await postbreeze.posts.create({
  content: "New drop today",
  mediaIds: ["med_abc123"],
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: { platform: "FACEBOOK_PAGE", kind: "PHOTO" },
  }],
});
Node.js — URL ingest (alternative)
await postbreeze.posts.create({
  content: "New drop today",
  mediaItems: [
    { type: "image", url: "https://cdn.example.com/drop.jpg", altText: "Product hero shot" },
  ],
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: { platform: "FACEBOOK_PAGE", kind: "PHOTO" },
  }],
});
mediaItems accepts any reachable URL — Postbreeze fetches the asset through an SSRF-guarded ingest path. Use mediaIds when you’ve already uploaded the file via the presign flow. kind: "PHOTO_CAROUSEL". Mixed photo + video is not allowed on organic Page posts — that surface is ads-only.
const post = await postbreeze.posts.create({
  content: "Highlights from the event",
  mediaIds: ["med_1", "med_2", "med_3", "med_4", "med_5"],
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: {
      platform: "FACEBOOK_PAGE",
      kind: "PHOTO_CAROUSEL",
    },
  }],
});

Video

kind: "VIDEO". Single video file. Up to 4 hours of runtime; the server-side ingest cap is 5 GB.
Node.js
await postbreeze.posts.create({
  content: "Behind the scenes of the launch",
  mediaIds: ["med_video_abc"],
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: { platform: "FACEBOOK_PAGE", kind: "VIDEO" },
  }],
});

Reel

kind: "REEL". 9:16, 3–90 seconds, video-only. Optionally pass reelTitle — it’s prepended to the description on the Reel surface (not the Feed).
Node.js
await postbreeze.posts.create({
  content: "How we shipped this in a week",
  mediaIds: ["med_reel_abc"],
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: {
      platform: "FACEBOOK_PAGE",
      kind: "REEL",
      reelTitle: "Shipping in a week",
    },
  }],
});

Media requirements

Images

PropertyRequirement
Max items10 per carousel, 1 per Photo post
FormatsJPG, PNG, GIF, WebP, HEIC, HEIF
Max file size5 GB (server-side ingest)
Aspect ratioAny (Facebook crops to 1.91:1 in Feed by default)
Min resolution600 × 315

Videos

PropertyRequirement
FormatsMP4, MOV, AVI, WebM
Max file size5 GB (server-side ingest)
Max duration (Feed)240 minutes
Max duration (Reel)90 seconds
Min duration (Reel)3 seconds
Aspect ratio (Feed)16:9 to 1:1
Aspect ratio (Reel)9:16
CodecsH.264 + AAC

Platform-specific fields

FieldTypeRequiredDescription
platform"FACEBOOK_PAGE"YesDiscriminator.
kindenumNo (default FEED)FEED, PHOTO, PHOTO_CAROUSEL, VIDEO, or REEL. With media attached, FEED auto-normalizes to PHOTO / PHOTO_CAROUSEL.
linkURLNoOutbound link. Auto-generates a preview card when kind: "FEED".
reelTitlestringNoReel-only title prepended to the description (≤ 2,200 chars).
That’s the complete schema — there is no pageId, geoRestriction, or story support on Facebook today. The firstComment field is not inside platformOptions; it lives one level up as a sibling of platformOptions on each platforms entry (see below).

First comment

Pass firstComment on the platforms entry — sibling of platformOptions, not nested inside it. Facebook publishes the comment as the Page (not as a personal user). No delay needed — Facebook accepts immediate comments after publish.
Node.js
await postbreeze.posts.create({
  content: "Our new feature drop",
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  platforms: [{
    accountId: "soc_facebook_…",
    platformOptions: { platform: "FACEBOOK_PAGE", kind: "FEED" },
    firstComment: "More details on our blog → https://example.com/blog/new-feature",
  }],
});
Limit: 8,000 characters per Page comment. First comments are supported on every Facebook surface except Reels — Meta’s Reels API rejects programmatic first comments.

Analytics

MetricAvailable
Page follows
Page media views
Page post engagements
Page video views
Page views total
Reach
Reactions breakdown
Post clicks
Comments
Shares
Refresh cadence: every 14 days. The 2026 metric names above (page_follows, page_media_view, etc.) replaced the pre-June-2026 page_impressions family. Postbreeze emits the new names directly — no migration needed.

Common errors

ErrorMeaningFix
FB_PAGE_TOKEN_EXPIREDThe Page access token is invalidReconnect the account.
FB_REEL_NOT_APPROVEDYour Meta App hasn’t been reviewed for the Reels use-caseSubmit for Meta App Review.
FB_MIXED_MEDIAPhotos + videos in mediaIdsPick one type.
FB_CAROUSEL_SIZECarousel had fewer than 2 or more than 10 photosTrim to 2–10.
FB_RATE_LIMITEDPage or app rate limit hitPostbreeze retries with backoff.
FB_LINK_INVALIDThe link URL didn’t resolveVerify the URL works in an incognito browser.
FB_VIDEO_TOO_LARGEVideo > 5 GBCompress before uploading.
FB_REEL_QUOTAMore than 30 Reels published on this Page in 24hWait for the rolling window to clear; Postbreeze retries with backoff.

What you can’t do

  • ❌ Publish to personal Facebook profiles
  • ❌ Schedule via Facebook’s native scheduler (we use our own queue)
  • ❌ Mix video and image in one carousel (ads-only feature)
  • ❌ Live video / Live Reels
  • ❌ Stories (planned for v2)
  • ❌ Tag specific users in caption text
  • ❌ Add location tags (Meta removed this from the Graph API)
  • ❌ Cross-post to Instagram in the same call (use two entries in platforms)
  • ❌ First comment on Reels (Meta API limitation)

Full control: nested shape

The flat shape (content + platforms) above is the canonical entry point and covers every Facebook use case. Postbreeze also accepts an equivalent nested shape (caption + targets) for callers porting from older internal tooling — the two shapes produce identical posts.
import Postbreeze from "@postbreeze/node";

const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY });

const post = await postbreeze.posts.create({
  caption: "Our new feature drop — read more 👇",
  scheduledAt: new Date(Date.now() + 30_000).toISOString(),
  mediaIds: ["med_hero_image"],
  targets: [{
    socialAccountId: "soc_facebook_…",
    platformOptions: {
      platform: "FACEBOOK_PAGE",
      kind: "PHOTO",
      link: "https://example.com/blog/new-feature",
    },
    firstComment: "More details on our blog →",
  }],
});
Field mapping flat ↔ nested:
FlatNested
contentcaption
scheduledForscheduledAt
platforms[]targets[]
platforms[].accountIdtargets[].socialAccountId
mediaItems / mediaIdsmediaIds (URL ingest is flat-only)
Prefer the flat shape for all new integrations.