Posts
Publish text, photo, multi-photo, carousel, video, Reels, drafts, and Stories on Facebook Pages.
Publish to Facebook Pages through the shared posts surface. Text, photos, albums, carousels, videos, Reels, drafts, and Stories all go through the same endpoint.
Quick Start
Post to a Facebook Page in under 60 seconds:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"destinations": [
{
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}The response is 202 Accepted with status: "queued". Poll GET /v1/posts/:id until the destination is published or failed, or receive webhooks instead of polling.
Content Types
mediaKind | Content | Media count | Notes |
|---|---|---|---|
text | Text-only post | No media | Unlike Instagram, text alone works |
image | Single photo | Exactly 1 URL | Optional caption |
multi-image | Photo album | 2 to 10 URLs | Images only. Video URLs fail with INVALID_MEDIA. Add carouselCards for a multi-link carousel |
video | Single video | Exactly 1 URL | Text becomes the video description |
reel | Reel | Exactly 1 URL | Single vertical video. Optional reelTitle |
stories | Page story | Exactly 1 URL | Photo or video. No caption. Video max 100 MB |
Text Post
No media required. Facebook is one of the few platforms that supports text-only posts. Keep the punch above the ~480-character "See more" fold:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Big news: we just launched!",
"mediaUrls": [],
"mediaKind": "text",
"destinations": [
{
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Big news: we just launched!",
"mediaUrls": [],
"mediaKind": "text",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Link Post
Attach a URL to a text post with link. Either text or link must be present:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Read the announcement",
"mediaUrls": [],
"mediaKind": "text",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"link": "https://example.com/announcement"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Read the announcement",
"mediaUrls": [],
"mediaKind": "text",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Single Photo
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"destinations": [
{
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Multi-Photo
Requires between 2 and 10 image URLs. Video URLs are rejected:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Photo dump from the weekend!",
"mediaUrls": [
"https://cdn.example.com/photo1.jpg",
"https://cdn.example.com/photo2.jpg",
"https://cdn.example.com/photo3.jpg"
],
"mediaKind": "multi-image",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Photo dump from the weekend!",
"mediaUrls": [
"https://cdn.example.com/photo1.jpg",
"https://cdn.example.com/photo2.jpg",
"https://cdn.example.com/photo3.jpg"
],
"mediaKind": "multi-image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Video
A single video per post. text becomes the video description:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Watch the launch film",
"mediaUrls": [
"https://cdn.onepostly.com/media/demo.mp4"
],
"mediaKind": "video",
"destinations": [
{
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Watch the launch film",
"mediaUrls": [
"https://cdn.onepostly.com/media/demo.mp4"
],
"mediaKind": "video",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Videos keep processing
Facebook may keep processing the video after the destination is marked published. The post appears on the Page as soon as processing finishes.
Reel (Video)
Publishes a Facebook Reel (short vertical video). text is the caption; reelTitle sets a separate title:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Behind the scenes",
"mediaUrls": [
"https://cdn.example.com/reel.mp4"
],
"mediaKind": "reel",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"reelTitle": "Studio day"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Behind the scenes",
"mediaUrls": [
"https://cdn.example.com/reel.mp4"
],
"mediaKind": "reel",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Draft Post
Set draft: true to create an unpublished draft in Facebook Publishing Tools instead of publishing immediately, useful for review workflows. Drafts work for feed posts and videos, not stories or Reels. firstComment is skipped for drafts:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Draft for review",
"mediaUrls": [],
"mediaKind": "text",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"draft": true
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Draft for review",
"mediaUrls": [],
"mediaKind": "text",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Multi-Link Carousel
Add carouselCards (one card per image, in order) to render a 2-10 card carousel where each image has its own click-through link. Optionally set carouselLink for the end-card "See more" destination:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "New inventory",
"mediaUrls": [
"https://cdn.example.com/car-1.jpg",
"https://cdn.example.com/car-2.jpg"
],
"mediaKind": "multi-image",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"carouselLink": "https://example.com/inventory",
"carouselCards": [
{
"link": "https://example.com/inventory/car-1",
"name": "2024 Sedan"
},
{
"link": "https://example.com/inventory/car-2",
"name": "2023 SUV"
}
]
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "New inventory",
"mediaUrls": [
"https://cdn.example.com/car-1.jpg",
"https://cdn.example.com/car-2.jpg"
],
"mediaKind": "multi-image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Large Text Background
Render a text-only post as large text on a colored background with textFormatPresetId (text-only feed posts only):
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "Big announcement coming soon",
"mediaUrls": [],
"mediaKind": "text",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"textFormatPresetId": "123"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Big announcement coming soon",
"mediaUrls": [],
"mediaKind": "text",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Geo-Restriction
Restrict visibility by country with geoRestriction (up to 25 uppercase ISO codes). A hard restriction: users outside these countries cannot see the post. Text, image, and multi-image posts:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "US and Spain only",
"mediaUrls": [],
"mediaKind": "text",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"geoRestriction": [
"US",
"ES"
]
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "US and Spain only",
"mediaUrls": [],
"mediaKind": "text",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Story (Image Or Video)
Stories are 24-hour ephemeral Page content. Media is required, captions are not applied, and story videos are capped at 100 MB:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.createPost({
createPostBody: {
"text": "",
"mediaUrls": [
"https://cdn.example.com/story.jpg"
],
"mediaKind": "stories",
"destinations": [
{
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID"
}
]
},
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "",
"mediaUrls": [
"https://cdn.example.com/story.jpg"
],
"mediaKind": "stories",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "YOUR_FACEBOOK_ACCOUNT_ID",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Destination Parameters
Every Facebook destination accepts the following parameters.
Prop
Type
Media Requirements
Images
| Property | Requirement |
|---|---|
| Max images | 10 per album (1 for a single photo) |
| Formats | JPEG, PNG, GIF |
| Max file size | 4 MB in practice |
| Recommended | 1200 × 630 px (feed), 1080 × 1920 px (story) |
Videos
| Property | Feed Video | Story |
|---|---|---|
| Max videos | 1 | 1 |
| Formats | MP4, MOV | MP4 |
| Max file size | 4 GB | 100 MB |
| Max duration | 240 minutes | 120 seconds |
| Recommended resolution | 1280 × 720 px or higher | 1080 × 1920 px |
Media URL Requirements
These do not work as media URLs:
- Google Drive returns an HTML download page, not the file
- Dropbox returns an HTML preview page
- OneDrive / SharePoint returns HTML
- iCloud returns HTML
Test your URL in an incognito browser window. If you see a webpage instead of the raw image or video, the URL will not work.
Media URLs must be publicly accessible, return actual media bytes with the correct Content-Type, and use HTTPS.
Schedule
Add scheduledFor (local wall time) and timezone to the same call. The request shape is otherwise identical to an immediate post.
timezonedefaults toUTC. Use an IANA name likeEurope/Istanbul- Do not append
Zor an offset toscheduledFor. It is local wall time - Scheduled posts return
201 Createdand publish automatically at the scheduled moment - Max 1 year ahead
- Cancel with
posts.cancelPost({ id })/posts.cancel_post(id=…), orDELETE /v1/posts/:id, any time before then
Remote Delete
Removes the post from the Page and emits the post.platform.deleted webhook:
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.deletePostDestination({
destinationId: "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
});
console.log(response);{
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "facebook",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://www.facebook.com/page/posts/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Publish Errors
| Code | Cause | Fix |
|---|---|---|
TEXT_REQUIRED | Text-only post with empty message and no link | Add text or link |
MEDIA_REQUIRED | Image / video / stories without a URL | Add at least one entry to mediaUrls |
INVALID_MULTI_IMAGE | Fewer than 2 or more than 10 URLs | Adjust the count |
INVALID_MEDIA | Video in a multi-photo post, non-video reel, or invalid media format | Use images only for multi-photo; vertical video for Reels |
INVALID_CAROUSEL | carouselCards length differs from the image count, or cards sent without multi-image | Send one card per image |
TEXT_FORMAT_UNSUPPORTED | textFormatPresetId on a non-text post | Use text-only posts for backgrounds |
DRAFT_UNSUPPORTED | draft: true on stories or Reels | Draft feed posts and videos only |
REEL_UPLOAD_FAILED | Reel upload session failed | Retry. Check the video URL |
MEDIA_UNFETCHABLE | Media URL could not be downloaded | Use a public HTTPS URL |
STORY_UPLOAD_FAILED | Story upload failed | Retry. Check story video is under 100 MB |
TOKEN_INVALID | Connection token expired or revoked | Reconnect the Page |
PERMISSION_DENIED | Connection lacks pages_manage_posts | Reconnect the Page |
RATE_LIMITED | Facebook throttled the request | Retry later |