Rate Limits and Optimal Posting Times
This guide covers platform-specific rate limits, Publora API rate limits, and strategies for scheduling posts at optimal engagement times.
Quick Start: High-Volume Multi-Platform Scheduling
For scheduling hundreds of posts across all 10 platforms simultaneously, use this production-ready approach:
Installation
# JavaScript/Node.js
npm install axios
# Python
pip install requestsEnvironment Setup
# Set your API key (get it from publora.com → API in sidebar)
export PUBLORA_API_KEY="sk_YOUR_API_KEY"Minimal Working Example
JavaScript (Node.js):
// high-volume-scheduler.js
// Run: PUBLORA_API_KEY=sk_YOUR_API_KEY node high-volume-scheduler.js
const axios = require('axios');
const API_KEY = process.env.PUBLORA_API_KEY;
const BASE_URL = 'https://api.publora.com/api/v1';
// Optional user-supplied throttles. Publora defines no per-platform quotas.
const PLATFORM_LIMITS = {};
async function schedulePost(content, platforms, scheduledTime) {
const response = await axios.post(`${BASE_URL}/create-post`, {
content,
platforms,
scheduledTime
}, {
headers: {
'x-publora-key': API_KEY,
'Content-Type': 'application/json'
}
});
return response.data;
}
async function main() {
// Your platform connection IDs (from /platform-connections endpoint)
const platforms = ['twitter-123456', 'linkedin-ABC123', 'threads-789012'];
// Schedule 10 posts, 1 hour apart
const now = new Date();
for (let i = 0; i < 10; i++) {
const scheduledTime = new Date(now.getTime() + (i + 1) * 3600000);
const result = await schedulePost(
`Post ${i + 1}: Your content here`,
platforms,
scheduledTime.toISOString()
);
console.log(`Scheduled post ${i + 1}: ${result.postGroupId}`);
}
}
main().catch(console.error);Python:
#!/usr/bin/env python3
# high_volume_scheduler.py
# Run: PUBLORA_API_KEY=sk_YOUR_API_KEY python high_volume_scheduler.py
import os
import requests
from datetime import datetime, timedelta, timezone
API_KEY = os.environ['PUBLORA_API_KEY']
BASE_URL = 'https://api.publora.com/api/v1'
# Optional user-supplied throttles. Publora defines no per-platform quotas.
PLATFORM_LIMITS = {}
def schedule_post(content: str, platforms: list, scheduled_time: str) -> dict:
response = requests.post(
f'{BASE_URL}/create-post',
json={
'content': content,
'platforms': platforms,
'scheduledTime': scheduled_time
},
headers={
'x-publora-key': API_KEY,
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return response.json()
def main():
# Your platform connection IDs (from /platform-connections endpoint)
platforms = ['twitter-123456', 'linkedin-ABC123', 'threads-789012']
# Schedule 10 posts, 1 hour apart
now = datetime.now(timezone.utc)
for i in range(10):
scheduled_time = now + timedelta(hours=i + 1)
result = schedule_post(
f'Post {i + 1}: Your content here',
platforms,
scheduled_time.isoformat()
)
print(f"Scheduled post {i + 1}: {result['postGroupId']}")
if __name__ == '__main__':
main()Publora Plan Limits
Each Publora plan enforces limits on monthly posts, scheduled (pending) posts, and how far in advance you can schedule.
| Plan | Monthly Posts | Connections | Post Limit Scope | Scheduled Posts | Schedule Horizon | API/MCP Access | Platforms |
|---|---|---|---|---|---|---|---|
| Starter (Free) | 15 | 3 | Account-wide | 3 pending | 7 days | Yes | All 10 |
| Pro Monthly ($5.99/mo) | 100 | Per seat* | Per connection | 100 pending | Unlimited | Yes | All |
| Pro Yearly ($2.99/mo) | 100 | Per seat* | Per connection | 100 pending | Unlimited | Yes | All |
| Premium Monthly ($9.99/mo) | 500 | Per seat* | Per connection | 500 pending | Unlimited | Yes | All |
| Premium Yearly ($5.99/mo) | 500 | Per seat* | Per connection | 500 pending | Unlimited | Yes | All |
*Paid plan connection limits are based on subscription quantity (seat count), not truly unlimited. Each seat in your subscription adds to your total allowed connections.
Key details:
- Monthly-post scope: Starter's 15 monthly posts are account-wide; paid-plan
monthlyPostsare counted per connection (for example, 100 for one LinkedIn connection and 100 for one Twitter connection). The activescheduledPostsqueue limit is workspace-wide on every plan, not per connection. Connection count and schedule horizon are separate plan limits. - Starter plan includes API and MCP access (
apiAccess: true,mcpAccess: true) — the free tier can use the REST API and MCP server (3 connected accounts, 15 posts/month account-wide). - Starter plan can post to any of the 10 platforms (no platform restriction).
Note: Per-request API rate limiting (requests/minute) is planned but not currently enforced. The limits above (monthly posts, scheduled posts, schedule horizon) are the enforced plan-based limits. Future API rate limiting will be documented here when implemented.
Platform-Specific Rate Limits (Advisory)
Platform-side posting limits are advisory, account-dependent, and may change without notice. Publora does not treat unsourced posts-per-hour/day figures as an API contract; consult the destination platform for current quotas.
What Publora enforces
- Plan entitlements shown above (monthly posts, pending posts, connections, and scheduling horizon).
- Media-URL ingestion: 60 URLs per fixed one-hour window;
429 MEDIA_URL_RATE_LIMITEDincludesRetry-After. - MCP session limits documented by the MCP endpoint.
Platform-side rate-limit failures are reported on the post. Do not assume Publora automatically retries or redistributes a failed platform publication.
API Reference for Scheduling
The scheduler examples use these Publora API endpoints:
Create Post
POST https://api.publora.com/api/v1/create-post
Content-Type: application/json
x-publora-key: sk_YOUR_API_KEY
{
"content": "Your post content",
"platforms": ["twitter-123456", "linkedin-ABC123"],
"scheduledTime": "<FUTURE_ISO_8601_UTC>"
}Response:
{
"success": true,
"postGroupId": "67a1b2c3d4e5f6a7b8c9d0e1",
"scheduledTime": "<FUTURE_ISO_8601_UTC>"
}Get Post Status
GET https://api.publora.com/api/v1/get-post/{postGroupId}
x-publora-key: sk_YOUR_API_KEYResponse:
{
"success": true,
"postGroupId": "67a1b2c3d4e5f6a7b8c9d0e1",
"status": "published",
"scheduledTime": "2026-03-15T14:00:00.000Z",
"platformSettings": {},
"platforms": ["twitter-123456", "linkedin-ABC123"],
"posts": [
{
"platform": "twitter",
"platformId": "123456",
"status": "published",
"postedId": "1234567890123456789",
"permalink": "https://twitter.com/user/status/123456"
},
{
"platform": "linkedin",
"platformId": "ABC123",
"status": "published",
"postedId": "urn:li:share:7000000000000000000",
"permalink": "https://linkedin.com/posts/..."
}
],
"media": []
}Note: The
get-postendpoint returns the rawplatformId(e.g.,"123456"), not the composite format (e.g.,"twitter-123456"). The compositeplatform-platformIdformat is only returned by thelist-postsandplatform-connectionsendpoints.
Get Platform Connections
GET https://api.publora.com/api/v1/platform-connections
x-publora-key: sk_YOUR_API_KEYResponse:
{
"success": true,
"connections": [
{ "platformId": "twitter-123456789", "username": "myaccount", "displayName": "My Account", "tokenStatus": "valid" },
{ "platformId": "linkedin-ABC123", "username": "My Company", "displayName": "My Company", "tokenStatus": "valid" },
{ "platformId": "threads-789012", "username": "mythreads", "displayName": "My Threads", "tokenStatus": "valid" }
]
}Use the platformId field from connections as the platform identifier when creating posts.
Examples
JavaScript - Publora API Client
class PubloraClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.publora.com/api/v1';
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
'x-publora-key': this.apiKey,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || `HTTP ${response.status}`);
}
return response.json();
}
async createPost(content, platforms, scheduledTime) {
return this.request('/create-post', {
method: 'POST',
body: JSON.stringify({ content, platforms, scheduledTime })
});
}
async listPosts(params = {}) {
const query = new URLSearchParams(params).toString();
return this.request(`/list-posts?${query}`);
}
}
// Usage
const client = new PubloraClient('YOUR_API_KEY');
for (let i = 0; i < 100; i++) {
await client.createPost(
`Post ${i + 1}`,
['twitter-123'],
new Date(Date.now() + i * 3600000).toISOString()
);
}Python - Publora API Client with Retry
import requests
import time
from typing import Dict, Any, Optional
class PubloraClient:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = 'https://api.publora.com/api/v1'
def _request(
self,
method: str,
endpoint: str,
data: Optional[Dict] = None,
params: Optional[Dict] = None,
max_retries: int = 3
) -> Dict[str, Any]:
"""Make a request with retry on failure."""
headers = {
'x-publora-key': self.api_key,
'Content-Type': 'application/json'
}
for attempt in range(max_retries):
response = requests.request(
method,
f'{self.base_url}{endpoint}',
headers=headers,
json=data,
params=params
)
if response.status_code >= 500:
backoff = 2 ** attempt
print(f"Server error. Retrying in {backoff}s (attempt {attempt + 1})")
time.sleep(backoff)
continue
response.raise_for_status()
return response.json()
raise Exception("Max retries exceeded")
def create_post(
self,
content: str,
platforms: list,
scheduled_time: Optional[str] = None
) -> Dict[str, Any]:
data = {'content': content, 'platforms': platforms}
if scheduled_time:
data['scheduledTime'] = scheduled_time
return self._request('POST', '/create-post', data=data)
def list_posts(self, **params) -> Dict[str, Any]:
return self._request('GET', '/list-posts', params=params)
# Usage
client = PubloraClient('YOUR_API_KEY')
# Bulk create
from datetime import datetime, timedelta, timezone
base_time = datetime.now(timezone.utc) + timedelta(hours=1)
for i in range(100):
scheduled = (base_time + timedelta(hours=i)).isoformat()
result = client.create_post(
f"Scheduled post {i + 1}",
['twitter-123', 'linkedin-ABC'],
scheduled
)
print(f"Created post {i + 1}: {result['postGroupId']}")Peak Engagement Times
Optimal posting times vary by platform and audience. Use these guidelines as a starting point, then analyze your own analytics.
Platform-Specific Best Times (UTC)
| Platform | Best Days | Best Hours (UTC) | Peak Engagement |
|---|---|---|---|
| X/Twitter | Tue-Thu | 13:00-16:00 | Wednesday 12:00 |
| Tue-Thu | 10:00-12:00 | Tuesday 10:00 | |
| Mon, Wed | 11:00-14:00, 19:00-21:00 | Wednesday 11:00 | |
| Threads | Tue-Thu | 12:00-15:00 | Wednesday 13:00 |
| TikTok | Tue-Thu | 15:00-21:00 | Thursday 19:00 |
| YouTube | Thu-Sat | 14:00-18:00 | Friday 15:00 |
| Wed-Fri | 13:00-16:00 | Wednesday 13:00 | |
| Bluesky | Mon-Fri | 14:00-17:00 | Tuesday 15:00 |
JavaScript - Optimal Time Scheduler
const OPTIMAL_TIMES = {
twitter: {
bestDays: [2, 3, 4], // Tue, Wed, Thu (0 = Sunday)
bestHours: [13, 14, 15, 16],
peakDay: 3,
peakHour: 12
},
linkedin: {
bestDays: [2, 3, 4],
bestHours: [10, 11, 12],
peakDay: 2,
peakHour: 10
},
instagram: {
bestDays: [1, 3], // Mon, Wed
bestHours: [11, 12, 13, 14, 19, 20, 21],
peakDay: 3,
peakHour: 11
},
threads: {
bestDays: [2, 3, 4],
bestHours: [12, 13, 14, 15],
peakDay: 3,
peakHour: 13
},
tiktok: {
bestDays: [2, 3, 4],
bestHours: [15, 16, 17, 18, 19, 20, 21],
peakDay: 4,
peakHour: 19
},
facebook: {
bestDays: [3, 4, 5], // Wed, Thu, Fri
bestHours: [13, 14, 15, 16],
peakDay: 3,
peakHour: 13
}
};
function getNextOptimalTime(platform, fromDate = new Date()) {
const config = OPTIMAL_TIMES[platform];
if (!config) throw new Error(`Unknown platform: ${platform}`);
const result = new Date(fromDate);
result.setUTCMinutes(0, 0, 0);
// Find next best day
let daysToAdd = 0;
while (!config.bestDays.includes(result.getUTCDay()) || daysToAdd === 0) {
result.setUTCDate(result.getUTCDate() + 1);
daysToAdd++;
if (daysToAdd > 7) break;
}
// Set to best hour
result.setUTCHours(config.bestHours[0]);
return result;
}
function getPeakTime(platform, weekStart = new Date()) {
const config = OPTIMAL_TIMES[platform];
if (!config) throw new Error(`Unknown platform: ${platform}`);
const result = new Date(weekStart);
result.setUTCHours(0, 0, 0, 0);
// Move to peak day
const currentDay = result.getUTCDay();
const daysUntilPeak = (config.peakDay - currentDay + 7) % 7;
result.setUTCDate(result.getUTCDate() + daysUntilPeak);
// Set peak hour
result.setUTCHours(config.peakHour);
return result;
}
function distributePostsOptimally(platforms, count, startDate = new Date()) {
const schedule = [];
const date = new Date(startDate);
for (let i = 0; i < count; i++) {
const platform = platforms[i % platforms.length];
const optimalTime = getNextOptimalTime(platform, date);
schedule.push({
index: i,
platform,
scheduledTime: optimalTime.toISOString()
});
// Move forward for next post
date.setTime(optimalTime.getTime() + 3600000); // +1 hour minimum gap
}
return schedule;
}
// Usage
const schedule = distributePostsOptimally(
['twitter', 'linkedin', 'instagram'],
9, // 3 posts per platform
new Date()
);
console.log('Optimal posting schedule:');
schedule.forEach(item => {
console.log(`${item.platform}: ${item.scheduledTime}`);
});Python - Queue-Based Scheduler with Rate Limits
from datetime import datetime, timedelta, timezone
from typing import List, Dict, Optional
from dataclasses import dataclass
from collections import defaultdict
import heapq
@dataclass
class PlatformConfig:
posts_per_hour: int
posts_per_day: int
best_hours: List[int] # UTC hours
best_days: List[int] # 0=Monday, 6=Sunday
PLATFORM_LIMITS = {
'twitter': PlatformConfig(
posts_per_hour=10,
posts_per_day=50,
best_hours=[13, 14, 15, 16],
best_days=[1, 2, 3] # Tue, Wed, Thu
),
'linkedin': PlatformConfig(
posts_per_hour=5,
posts_per_day=20,
best_hours=[10, 11, 12],
best_days=[1, 2, 3]
),
'instagram': PlatformConfig(
posts_per_hour=3,
posts_per_day=10,
best_hours=[11, 12, 13, 14, 19, 20, 21],
best_days=[0, 2] # Mon, Wed
),
'tiktok': PlatformConfig(
posts_per_hour=2,
posts_per_day=10,
best_hours=[15, 16, 17, 18, 19, 20, 21],
best_days=[1, 2, 3]
),
}
class OptimalScheduler:
"""
Schedule posts optimally across platforms considering:
- Platform-specific rate limits
- Peak engagement times
- Even distribution across time slots
"""
def __init__(self):
self.platform_queues: Dict[str, List] = defaultdict(list)
self.posts_per_hour: Dict[str, Dict[str, int]] = defaultdict(lambda: defaultdict(int))
self.posts_per_day: Dict[str, Dict[str, int]] = defaultdict(lambda: defaultdict(int))
def get_next_optimal_slot(
self,
platform: str,
after: datetime
) -> datetime:
"""Find the next available optimal time slot for a platform."""
config = PLATFORM_LIMITS.get(platform)
if not config:
# Unknown platform - just space by 1 hour
return after + timedelta(hours=1)
candidate = after.replace(minute=0, second=0, microsecond=0)
for _ in range(168): # Check up to 1 week ahead
candidate += timedelta(hours=1)
day_key = candidate.strftime('%Y-%m-%d')
hour_key = candidate.strftime('%Y-%m-%d-%H')
# Check rate limits
if self.posts_per_day[platform][day_key] >= config.posts_per_day:
# Skip to next day
candidate = candidate.replace(hour=0) + timedelta(days=1)
continue
if self.posts_per_hour[platform][hour_key] >= config.posts_per_hour:
continue
# Check if this is an optimal time
if (candidate.weekday() in config.best_days and
candidate.hour in config.best_hours):
return candidate
# If no optimal slot found, return next available
return after + timedelta(hours=1)
def schedule_post(
self,
platform: str,
after: datetime = None
) -> datetime:
"""Schedule a single post and return its time."""
if after is None:
after = datetime.now(timezone.utc)
scheduled_time = self.get_next_optimal_slot(platform, after)
# Update counters
day_key = scheduled_time.strftime('%Y-%m-%d')
hour_key = scheduled_time.strftime('%Y-%m-%d-%H')
self.posts_per_day[platform][day_key] += 1
self.posts_per_hour[platform][hour_key] += 1
return scheduled_time
def schedule_batch(
self,
posts: List[Dict],
start_after: datetime = None
) -> List[Dict]:
"""
Schedule multiple posts across platforms optimally.
Args:
posts: List of {'content': str, 'platforms': List[str]}
start_after: Earliest scheduling time
Returns:
List of posts with scheduledTime added
"""
if start_after is None:
start_after = datetime.now(timezone.utc)
scheduled_posts = []
platform_last_time: Dict[str, datetime] = {}
for post in posts:
platforms = post['platforms']
# Find the latest "next optimal time" across all platforms
scheduled_time = start_after
for platform in platforms:
platform_name = platform.split('-')[0]
after = platform_last_time.get(platform_name, start_after)
optimal_time = self.schedule_post(platform_name, after)
if optimal_time > scheduled_time:
scheduled_time = optimal_time
platform_last_time[platform_name] = scheduled_time
scheduled_posts.append({
**post,
'scheduledTime': scheduled_time.isoformat()
})
return scheduled_posts
# Usage example
scheduler = OptimalScheduler()
# Posts to schedule
posts = [
{'content': 'Morning motivation!', 'platforms': ['twitter-123', 'linkedin-ABC']},
{'content': 'Product update announcement', 'platforms': ['twitter-123', 'linkedin-ABC', 'instagram-456']},
{'content': 'Behind the scenes', 'platforms': ['instagram-456', 'tiktok-789']},
{'content': 'Weekly tips thread', 'platforms': ['twitter-123']},
{'content': 'Customer success story', 'platforms': ['linkedin-ABC']},
]
scheduled = scheduler.schedule_batch(posts)
print("Optimally scheduled posts:")
for post in scheduled:
print(f" {post['scheduledTime']}: {post['content'][:30]}...")
print(f" Platforms: {post['platforms']}")Node.js - Complete Rate-Limited Queue System
const axios = require('axios');
class PubloraQueueScheduler {
constructor(apiKey, platformLimits = {}) {
this.apiKey = apiKey;
this.api = axios.create({
baseURL: 'https://api.publora.com/api/v1',
headers: { 'x-publora-key': apiKey }
});
// User-supplied throttles. Publora does not define per-platform posting quotas.
this.platformLimits = platformLimits;
this.peakHours = {
twitter: [13, 14, 15, 16],
linkedin: [10, 11, 12],
instagram: [11, 12, 13, 14, 19, 20, 21],
tiktok: [15, 16, 17, 18, 19, 20, 21],
facebook: [13, 14, 15, 16],
};
this.postCounts = { hourly: {}, daily: {} };
}
getPlatformName(platformId) {
return platformId.split('-')[0];
}
getHourKey(date) {
return date.toISOString().slice(0, 13);
}
getDayKey(date) {
return date.toISOString().slice(0, 10);
}
canPostAt(platform, date) {
const limits = this.platformLimits[platform];
if (!limits) return true;
const hourKey = `${platform}-${this.getHourKey(date)}`;
const dayKey = `${platform}-${this.getDayKey(date)}`;
const hourlyCount = this.postCounts.hourly[hourKey] || 0;
const dailyCount = this.postCounts.daily[dayKey] || 0;
return hourlyCount < limits.perHour && dailyCount < limits.perDay;
}
recordPost(platform, date) {
const hourKey = `${platform}-${this.getHourKey(date)}`;
const dayKey = `${platform}-${this.getDayKey(date)}`;
this.postCounts.hourly[hourKey] = (this.postCounts.hourly[hourKey] || 0) + 1;
this.postCounts.daily[dayKey] = (this.postCounts.daily[dayKey] || 0) + 1;
}
isPeakTime(platform, date) {
const hours = this.peakHours[platform];
return hours ? hours.includes(date.getUTCHours()) : true;
}
findNextOptimalSlot(platforms, after) {
let candidate = new Date(after);
candidate.setUTCMinutes(0, 0, 0);
for (let i = 0; i < 168; i++) { // Check up to 1 week
candidate = new Date(candidate.getTime() + 3600000); // +1 hour
const allCanPost = platforms.every(p =>
this.canPostAt(this.getPlatformName(p), candidate)
);
if (!allCanPost) continue;
const anyPeak = platforms.some(p =>
this.isPeakTime(this.getPlatformName(p), candidate)
);
if (anyPeak) {
return candidate;
}
}
// Fallback: return next hour
return new Date(after.getTime() + 3600000);
}
async scheduleOptimally(posts) {
const results = [];
let lastTime = new Date();
for (const post of posts) {
const scheduledTime = this.findNextOptimalSlot(post.platforms, lastTime);
// Record for rate limiting
post.platforms.forEach(p => {
this.recordPost(this.getPlatformName(p), scheduledTime);
});
try {
const { data } = await this.api.post('/create-post', {
content: post.content,
platforms: post.platforms,
scheduledTime: scheduledTime.toISOString()
});
results.push({
success: true,
postGroupId: data.postGroupId,
scheduledTime: scheduledTime.toISOString(),
content: post.content.slice(0, 50)
});
console.log(`Scheduled: ${post.content.slice(0, 30)}... at ${scheduledTime.toISOString()}`);
} catch (error) {
results.push({
success: false,
error: error.response?.data?.message || error.message,
content: post.content.slice(0, 50)
});
}
lastTime = scheduledTime;
// Small delay between API calls
await new Promise(r => setTimeout(r, 100));
}
return results;
}
}
// Usage
const scheduler = new PubloraQueueScheduler('YOUR_API_KEY');
const posts = [
{ content: 'Morning motivation!', platforms: ['twitter-123', 'linkedin-ABC'] },
{ content: 'Product update', platforms: ['twitter-123', 'linkedin-ABC', 'instagram-456'] },
{ content: 'Behind the scenes', platforms: ['instagram-456', 'tiktok-789'] },
];
const results = await scheduler.scheduleOptimally(posts);
console.log(`Scheduled ${results.filter(r => r.success).length} of ${results.length} posts`);Complete Queue-Based Scheduling System
This section provides a production-ready implementation that combines all the concepts: rate limiting, optimal scheduling, error handling, retry logic, and failed post recovery.
Architecture Overview
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Content Queue │───►│ Rate Limiter │───►│ Publora API │
│ (your posts) │ │ + Scheduler │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ Retry Queue │ │ Status Checker │
│ (failed posts) │ │ (polling) │
└──────────────────┘ └─────────────────┘JavaScript - Production Queue Scheduler
const axios = require('axios');
/**
* Production-ready queue-based scheduler for Publora API
* Features:
* - Platform-specific rate limiting
* - Peak engagement time optimization
* - Exponential backoff retry logic
* - Failed post recovery
* - Comprehensive error handling
*/
class PubloraQueueScheduler {
constructor(apiKey, options = {}) {
this.apiKey = apiKey;
this.baseUrl = options.baseUrl || 'https://api.publora.com/api/v1';
this.maxRetries = options.maxRetries || 3;
this.retryDelay = options.retryDelay || 1000;
// API rate limit tracking
this.rateLimitRemaining = Infinity;
this.rateLimitReset = 0;
// Optional user-supplied throttles; Publora defines no per-platform quotas.
this.platformLimits = options.platformLimits || {};
// Peak engagement hours (UTC) - all 10 platforms
this.peakHours = {
twitter: [13, 14, 15, 16],
linkedin: [10, 11, 12],
instagram: [11, 12, 13, 14, 19, 20, 21],
threads: [12, 13, 14, 15],
tiktok: [15, 16, 17, 18, 19, 20, 21],
facebook: [13, 14, 15, 16],
youtube: [14, 15, 16, 17, 18],
bluesky: [14, 15, 16, 17],
mastodon: [14, 15, 16, 17],
telegram: [9, 10, 11, 12, 18, 19, 20],
};
// Track scheduled posts per platform
this.postCounts = { hourly: {}, daily: {} };
// Failed posts queue for retry
this.failedQueue = [];
// Results tracking
this.results = [];
}
// ... (rest of the implementation)
}Publora is built by Creative Content Crafts, Inc. Need AI-powered content creation for LinkedIn, Threads, and X? Try Co.Actor -- the best AI service for authentic thought leadership at scale.