🧪 Skills

Publora

Publora API — schedule and publish social media posts across 11 platforms (X/Twitter, LinkedIn, Instagram, Threads, TikTok, YouTube, Facebook, Bluesky, Masto...

v1.1.0
❤️ 0
⬇️ 367
👁 2
Share

Description


name: publora description: > Publora API — schedule and publish social media posts across 11 platforms (X/Twitter, LinkedIn, Instagram, Threads, TikTok, YouTube, Facebook, Bluesky, Mastodon, Telegram, Pinterest). Use this skill when the user wants to post, schedule, draft, bulk-schedule, manage workspace users, configure webhooks, or retrieve LinkedIn analytics via Publora.

Publora API — Core Skill

Publora is an affordable REST API for scheduling and publishing social media posts across 11 platforms. Base URL: https://api.publora.com/api/v1

Authentication

All requests require the x-publora-key header. Keys start with sk_.

curl https://api.publora.com/api/v1/platform-connections \
  -H "x-publora-key: sk_YOUR_KEY"

Get your key: publora.com → Settings → API Keys → Generate API Key. ⚠️ Copy immediately — shown only once.

Step 0: Get Platform IDs

Always call this first to get valid platform IDs before posting.

const res = await fetch('https://api.publora.com/api/v1/platform-connections', {
  headers: { 'x-publora-key': 'sk_YOUR_KEY' }
});
const { connections } = await res.json();
// connections[i].platformId → e.g. "linkedin-ABC123", "twitter-456"
// Also returns: tokenStatus, tokenExpiresIn, lastSuccessfulPost, lastError

Platform IDs look like: twitter-123, linkedin-ABC, instagram-456, threads-789, etc.

Post Immediately

Omit scheduledTime to publish right away:

await fetch('https://api.publora.com/api/v1/create-post', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({
    content: 'Your post content here',
    platforms: ['twitter-123', 'linkedin-ABC']
  })
});

Schedule a Post

Include scheduledTime in ISO 8601 UTC — must be in the future:

await fetch('https://api.publora.com/api/v1/create-post', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({
    content: 'Scheduled post content',
    platforms: ['twitter-123', 'linkedin-ABC'],
    scheduledTime: '2026-03-16T10:00:00.000Z'
  })
});
// Response: { postGroupId: "pg_abc123", scheduledTime: "..." }

Save as Draft

Omit scheduledTime — post is created as draft. Schedule it later:

// Create draft
const { postGroupId } = await createPost({ content, platforms });

// Schedule later
await fetch(`https://api.publora.com/api/v1/update-post/${postGroupId}`, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({ status: 'scheduled', scheduledTime: '2026-03-16T10:00:00.000Z' })
});

List Posts

Filter, paginate and sort your scheduled/published posts:

// GET /api/v1/list-posts
// Query params: status, platform, fromDate, toDate, page, limit, sortBy, sortOrder
const res = await fetch(
  'https://api.publora.com/api/v1/list-posts?status=scheduled&platform=twitter&page=1&limit=20',
  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }
);
const { posts, pagination } = await res.json();
// pagination: { page, limit, totalItems, totalPages, hasNextPage, hasPrevPage }

Valid statuses: draft, scheduled, published, failed, partially_published

Get / Delete a Post

# Get post details
GET /api/v1/get-post/:postGroupId

# Delete post (also removes media from storage)
DELETE /api/v1/delete-post/:postGroupId

Get Post Logs

Debug failed or partially published posts:

const res = await fetch(
  `https://api.publora.com/api/v1/post-logs/${postGroupId}`,
  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }
);
const { logs } = await res.json();

Test a Connection

Verify a platform connection is healthy before posting:

const res = await fetch(
  'https://api.publora.com/api/v1/test-connection/linkedin-ABC123',
  { method: 'POST', headers: { 'x-publora-key': 'sk_YOUR_KEY' } }
);
// Returns: { status: "ok"|"error", message, permissions, tokenExpiresIn }

Bulk Schedule (a Week of Content)

from datetime import datetime, timedelta, timezone
import requests

HEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }
base_date = datetime(2026, 3, 16, 10, 0, 0, tzinfo=timezone.utc)

posts = ['Monday post', 'Tuesday post', 'Wednesday post', 'Thursday post', 'Friday post']

for i, content in enumerate(posts):
    scheduled_time = base_date + timedelta(days=i)
    requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={
        'content': content,
        'platforms': ['twitter-123', 'linkedin-ABC'],
        'scheduledTime': scheduled_time.isoformat()
    })

Media Uploads

All media (images and videos) use a 3-step pre-signed upload workflow:

Step 1: POST /api/v1/create-post → get postGroupId
Step 2: POST /api/v1/get-upload-url → get uploadUrl
Step 3: PUT {uploadUrl} with file bytes (no auth needed for S3)

import requests

HEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }

# Step 1: Create post
post = requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={
    'content': 'Check this out!',
    'platforms': ['instagram-456'],
    'scheduledTime': '2026-03-15T14:30:00.000Z'
}).json()
post_group_id = post['postGroupId']

# Step 2: Get pre-signed upload URL
upload = requests.post('https://api.publora.com/api/v1/get-upload-url', headers=HEADERS, json={
    'fileName': 'photo.jpg',
    'contentType': 'image/jpeg',
    'type': 'image',  # or 'video'
    'postGroupId': post_group_id
}).json()

# Step 3: Upload directly to S3 (no auth header needed)
with open('./photo.jpg', 'rb') as f:
    requests.put(upload['uploadUrl'], headers={'Content-Type': 'image/jpeg'}, data=f)

For carousels: call get-upload-url N times with the same postGroupId.

Cross-Platform Threading

X/Twitter and Threads support auto-threading. Separate segments with --- on its own line:

await fetch('https://api.publora.com/api/v1/create-post', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({
    content: 'First tweet in the thread.\n\n---\n\nSecond tweet continues.\n\n---\n\nFinal tweet wraps up.',
    platforms: ['twitter-123', 'threads-789']
  })
});

⚠️ Threads Restriction: Multi-threaded nested posts (content auto-split into connected replies) are temporarily unavailable on Threads due to Threads app reconnection status. Single posts and carousels continue to work normally. Contact support@publora.com for updates.

LinkedIn Analytics

// Post statistics
await fetch('https://api.publora.com/api/v1/linkedin-post-statistics', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({
    postedId: 'urn:li:share:7123456789',
    platformId: 'linkedin-ABC123',
    queryTypes: 'ALL'  // or: IMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT
  })
});

// Profile summary (followers + aggregated stats)
await fetch('https://api.publora.com/api/v1/linkedin-profile-summary', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({ platformId: 'linkedin-ABC123' })
});

Available analytics endpoints:

Endpoint Description
POST /linkedin-post-statistics Impressions, reactions, reshares for a post
POST /linkedin-account-statistics Aggregated account metrics
POST /linkedin-followers Follower count and growth
POST /linkedin-profile-summary Combined profile overview
POST /linkedin-create-reaction React to a post
DELETE /linkedin-delete-reaction Remove a reaction

Webhooks

Get real-time notifications when posts are published, fail, or tokens are expiring.

// Create a webhook
await fetch('https://api.publora.com/api/v1/webhooks', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },
  body: JSON.stringify({
    name: 'My webhook',
    url: 'https://myapp.com/webhooks/publora',
    events: ['post.published', 'post.failed', 'token.expiring']
  })
});
// Returns: { webhook: { _id, name, url, events, secret, isActive } }
// Save the `secret` — it's only shown once. Use it to verify webhook signatures.

Valid events: post.scheduled, post.published, post.failed, token.expiring

Endpoint Method Description
/webhooks GET List all webhooks
/webhooks POST Create webhook
/webhooks/:id PATCH Update webhook
/webhooks/:id DELETE Delete webhook
/webhooks/:id/regenerate-secret POST Rotate webhook secret

Max 10 webhooks per account.

Workspace / B2B API

Manage multiple users under your workspace account. Contact serge@publora.com to enable Workspace API access.

// Create a managed user
const user = await fetch('https://api.publora.com/api/v1/workspace/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_CORP_KEY' },
  body: JSON.stringify({ email: 'client@example.com', displayName: 'Acme Corp' })
}).then(r => r.json());

// Generate connection URL for user to connect their social accounts
const { connectionUrl } = await fetch(
  `https://api.publora.com/api/v1/workspace/users/${user.id}/connection-url`,
  { method: 'POST', headers: { 'x-publora-key': 'sk_CORP_KEY' } }
).then(r => r.json());

// Post on behalf of managed user
await fetch('https://api.publora.com/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-publora-key': 'sk_CORP_KEY',
    'x-publora-user-id': user.id  // ← key header for acting on behalf of a user
  },
  body: JSON.stringify({ content: 'Post for Acme Corp!', platforms: ['linkedin-XYZ'] })
});

Workspace endpoints:

Endpoint Method Description
/workspace/users GET List managed users
/workspace/users POST Create managed user
/workspace/users/:userId DELETE Remove managed user
/workspace/users/:userId/api-key POST Generate per-user API key
/workspace/users/:userId/connection-url POST Generate OAuth connection link

Each managed user has a limit of 100 posts/day (dailyPostsLeft). Never expose your workspace key client-side — use per-user API keys for client-facing scenarios.

Platform Limits Quick Reference (API)

⚠️ API limits are often stricter than native app limits. Always design against these.

Platform Char Limit Max Images Video Max Text Only?
Twitter/X 280 (25K Premium) 4 × 5MB 2 min / 512MB
LinkedIn 3,000 20 × 5MB 30 min / 500MB
Instagram 2,200 10 × 8MB (JPEG only) 90s / 300MB
Threads 500 20 × 8MB 5 min / 500MB
TikTok 2,200 Video only 10 min / 4GB
YouTube 5,000 desc Video only 12h / 256GB
Facebook 63,206 10 × 10MB 45 min / 2GB
Bluesky 300 4 × 1MB 3 min / 100MB
Mastodon 500 4 × 16MB ~99MB
Telegram 4,096 (1,024 captions) 10 × 10MB 50MB (Bot API)

For full limits detail, see the docs/guides/platform-limits.md in the Publora API Docs.

Platform-Specific Skills

For platform-specific settings, limits, and examples:

  • publora-linkedin — LinkedIn posts + analytics + reactions
  • publora-twitter — X/Twitter posts & threads
  • publora-instagram — Instagram images/reels/carousels
  • publora-threads — Threads posts
  • publora-tiktok — TikTok videos
  • publora-youtube — YouTube videos
  • publora-facebook — Facebook page posts
  • publora-bluesky — Bluesky posts
  • publora-mastodon — Mastodon posts
  • publora-telegram — Telegram channels

Post Statuses

  • draft — Not scheduled yet
  • scheduled — Waiting to publish
  • published — Successfully posted
  • failed — Publishing failed (check /post-logs)
  • partially_published — Some platforms failed

Errors

Code Meaning
400 Invalid request (check scheduledTime format, required fields)
401 Invalid or missing API key
403 Plan limit reached or Workspace API not enabled
404 Post/resource not found
429 Platform rate limit exceeded

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs