menu_book API Reference

Posbly API Documentation

Post to X, Instagram, Facebook, Pinterest, Threads, LinkedIn, Bluesky, YouTube, TikTok, and Mastodon from a single endpoint.

Overview

Posbly is an API-first social media broadcasting service for automation builders, AI agents, and developers. Publish to 9 platforms simultaneously from one request.

Base URLs

PROD https://api.posbly.com
DEV http://localhost:3000

Supported Platforms

XInstagramFacebookPinterestThreadsLinkedInBlueskyYouTubeTikTokMastodon

Authentication

All protected endpoints require a pk_... API key as a Bearer token. Generate and manage your keys from the dashboard.

Authorization: Bearer pk_your_api_key_here
curl -H "Authorization: Bearer pk_abc123" \
  https://api.posbly.com/v1/broadcast

Account

GET /v1/account Returns authenticated user profile

Returns basic profile info for the authenticated user.

{
  "id": "uuid-here",
  "email": "user@example.com",
  "name": "Jane Doe"
}

Platform Connections

Manage OAuth tokens for each social platform. Connect accounts via the OAuth flow, then use the connection ID when broadcasting.

GET /v1/connections List all connections
PATCH /v1/connections/:id Update metadata (e.g. Pinterest board_id)
GET /v1/connections/:id/boards List Pinterest boards

Connection Object

{
  "id": "uuid",
  "platform": "INSTAGRAM",
  "platform_user_id": "123456",
  "display_name": "@janedoe",
  "username": "janedoe",
  "created_at": "2026-03-13T00:00:00Z",
  "token_expires_at": "2026-06-13T00:00:00Z",
  "metadata": {}
}

Broadcasts

The core endpoint. Publish content to one or more platforms in a single API call. Returns 202 Accepted immediately — jobs run asynchronously.

How targeting works: platforms is an object mapping platform slugs to arrays of account names. Account names must exactly match the account_name field on your connected accounts (from GET /v1/connections). Matching is case-insensitive.
POST /v1/broadcast 202 Accepted

JSON Request Body

{
  "platforms": {                          // object, not an array
    "instagram": ["@brand_account"],      // slug → array of account names
    "x": ["@myhandle"],
    "linkedin": ["@My Company Page"]      // can include multiple accounts per platform
  },
  "content": {
    "text": "Hello from Posbly! 🚀",
    "media_url": "https://example.com/your-image.jpg",             // optional, any public URL
    "media_type": "image",                // "image" or "video" — required if media_url set
    "carousel_items": [                   // optional, Instagram only (2–10 items)
      { "media_url": "https://...", "media_type": "image" },
      { "media_url": "https://...", "media_type": "image" }
    ],
    "link": "https://example.com"         // optional, Facebook only
  },
  "schedule_at": "2026-04-01T09:00:00Z", // optional, ISO 8601, up to 30 days out
  "clientReferenceId": "run-abc-123",     // optional, idempotency key (24h TTL)
  "metadata": { "campaign": "spring" }   // optional, echoed in webhooks
}

Platform slugs (lowercase)

x · instagram · facebook · pinterest · threads · linkedin · bluesky · youtube · tiktok · mastodon

Response (202 Accepted)

{
  "broadcast_id": "brd_...",
  "status": "PENDING",
  "idempotent": false,
  "scheduled_for": null,
  "created_at": "2026-03-13T12:00:00Z",
  "metadata": { "campaign": "spring" },
  "jobs": [
    { "job_id": "job_...", "platform": "INSTAGRAM", "status": "QUEUED", "connection_id": "uuid" },
    { "job_id": "job_...", "platform": "X",         "status": "QUEUED", "connection_id": "uuid" },
    { "job_id": "job_...", "platform": "LINKEDIN",  "status": "QUEUED", "connection_id": "uuid" }
  ]
}

Multipart Mode (attach files directly)

Send Content-Type: multipart/form-data with a data field (JSON payload without media_url) plus a media field for a single file or carousel fields for multiple files.

GET /v1/broadcast List broadcasts
GET /v1/broadcast/:id Get broadcast status
DELETE /v1/broadcast/:id Cancel scheduled (204)

Query parameters for GET /v1/broadcast

?limit=20&offset=0&scheduled_only=true

Jobs

Each platform post is a separate Job within a Broadcast. Poll for per-platform results.

GET /v1/jobs/:job_id Get individual job details
GET /v1/jobs/:job_id/insights Platform engagement metrics (likes, comments, views) — available after COMPLETED
{
  "job_id": "job_...",
  "platform": "X",
  "status": "COMPLETED",
  "post_url": "https://x.com/user/status/1234",
  "external_post_id": "1234567890",
  "published_at": "2026-03-13T12:01:00Z",
  "completed_at": "2026-03-13T12:01:05Z",
  "attempts": 1,
  "error_code": null,
  "error_message": null
}

Job Statuses

PENDINGQUEUEDPROCESSINGCOMPLETEDFAILEDCANCELLED

Webhooks

Register a webhook URL to receive real-time event notifications. All payloads are signed with HMAC-SHA256.

POST /v1/webhooks Register a webhook (secret returned once)
{
  "url": "https://your-server.com/webhooks/posbly",
  "events": ["JOB_COMPLETED", "JOB_FAILED", "BROADCAST_COMPLETED", "BROADCAST_FAILED"]
}

// Response
{
  "id": "uuid",
  "url": "https://your-server.com/webhooks/posbly",
  "events": ["JOB_COMPLETED", "JOB_FAILED"],
  "secret": "whsec_abc123...",  // shown once — store securely
  "created_at": "2026-03-13T00:00:00Z"
}
GET /v1/webhooks List registered webhooks
DELETE /v1/webhooks/:id Delete a webhook (204)
// Signature verification
const signature = req.headers['x-posbly-signature'];
// Format: sha256=<hex-digest>

const expected = 'sha256=' + crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(JSON.stringify(payload))
  .digest('hex');

if (signature !== expected) throw new Error('Invalid signature');

Webhook Events

JOB_COMPLETEDJOB_FAILEDBROADCAST_COMPLETEDBROADCAST_FAILED

Billing & Credits

Posbly uses a credit-based system. Each broadcast job consumes credits. Top up via Stripe.

GET /v1/billing/balance Get credit balance (USD)
GET /v1/billing/transactions Transaction history
GET /v1/billing/daily-usage Per-day usage for current period
GET /v1/billing/monthly-usage Per-month usage summary
GET /v1/billing/spending-limit Get current monthly spending limit
POST /v1/billing/checkout Create Stripe checkout session to top up
GET /v1/billing/checkout/verify Verify checkout completion after Stripe redirect
// POST /v1/billing/checkout
{ "amount": 5000 }  // in cents ($50.00)

// Response
{ "url": "https://checkout.stripe.com/..." }

User Settings

PATCH /v1/users/me/timezone Update timezone preference
{ "timezone": "America/New_York" }

OAuth Flows

Connect social media accounts via OAuth. Redirect users to the connect URL, then handle the callback.

Standard OAuth Platforms

Redirect users to:

https://api.posbly.com/v1/oauth/:platform/connect?token=<session_token>

Platform slugs:

X x
Instagram instagram
Facebook meta
Pinterest pinterest
Threads threads
LinkedIn linkedin
YouTube youtube
TikTok tiktok

After OAuth, redirects back to: /dashboard/accounts?platform=...&status=connected

OAuth Callback URLs (register on each platform)
X:         https://api.posbly.com/v1/oauth/x/callback
Meta/FB:   https://api.posbly.com/v1/oauth/meta/callback
Instagram: https://api.posbly.com/v1/oauth/instagram/callback
Pinterest: https://api.posbly.com/v1/oauth/pinterest/callback
Threads:   https://api.posbly.com/v1/oauth/threads/callback
LinkedIn:  https://api.posbly.com/v1/oauth/linkedin/callback
YouTube:   https://api.posbly.com/v1/oauth/youtube/callback
TikTok:    https://api.posbly.com/v1/oauth/tiktok/callback

Error Handling

All errors return a consistent JSON structure.

{
  "error": {
    "code": "MISSING_API_KEY",
    "message": "Authorization header with Bearer token is required"
  }
}
HTTP Status Error Code Meaning
400 VALIDATION_ERROR Invalid request body
401 MISSING_API_KEY No Bearer token provided
401 INVALID_API_KEY Token is invalid or revoked
402 INSUFFICIENT_CREDITS Not enough credits for broadcast
404 NOT_FOUND Resource not found
429 RATE_LIMIT_EXCEEDED Too many requests
500 INTERNAL_ERROR Server error

Rate Limiting

Rate limits are enforced per API key. Exceeding limits returns 429 Too Many Requests.

10 req/s

Per second limit

1,000 req/min

Per minute limit

Facebook Pages

Manage your Facebook Page content beyond broadcasting — list posts, read and reply to comments, view page and post-level insights.

Posts & Comments

GET /v1/connections/:id/posts List recent page posts with engagement counts
GET /v1/connections/:id/posts/:postId/comments List comments on a post
POST /v1/connections/:id/posts/:postId/comments Reply to a post (comment as your page)
DELETE /v1/connections/:id/posts/:postId/comments/:commentId Delete a comment (204)
Reply to a post
// POST /v1/connections/:id/posts/:postId/comments
{
  "message": "Thanks for the feedback!"
}

// Response (201 Created)
{
  "commentId": "123456789_987654321"
}

Insights & Analytics

GET /v1/connections/:id/insights Page-level analytics (impressions, fans, views)
GET /v1/jobs/:job_id/insights Post-level insights (reach, reactions, shares) — via Jobs API
Page insights query parameters
GET /v1/connections/:id/insights?period=week&since=2026-03-01&until=2026-03-25

// period: day | week | days_28 | month (default: week)
// Metrics: page_impressions, page_engaged_users, page_fans,
//          page_views_total, page_post_engagements
Required permissions: Your Facebook app needs pages_read_engagement, pages_manage_engagement, pages_read_user_content, and read_insights approved.

Platform Guides

Key requirements and limits for each platform.

X (Twitter)

280 chars

OAuth 1.0a. Chunked media upload for videos.

Instagram

2,200 chars

Standalone Instagram Login. Supports carousel (up to 10 images) and Reels.

Facebook

63,206 chars

Posts to Facebook Pages. Comment on posts, view page & post insights. Long-lived token exchange required.

Pinterest

500 chars

Requires board_id in connection metadata. Image URL required.

Threads

500 chars

Text, image, and video posts. Separate Meta app.

LinkedIn

3,000 chars

UGC Posts API. Supports image uploads via registerUpload.

Bluesky

300 chars

AT Protocol. Uses App Password (not OAuth). Blob upload for media.

YouTube

5,000 chars

First line of text = video title (≤100 chars). Resumable video upload. Auto-refresh token.

TikTok

2,200 chars

Content Posting API v2. PULL_FROM_URL method. openId stored in metadata.