List Posts

View as Markdown

Retrieve a paginated list of all your scheduled, draft, published, and failed posts.

Endpoint

GET https://api.publora.com/api/v1/list-posts

Headers

Header Required Description
x-publora-key Yes Your API key
x-publora-user-id No Managed user ID (workspace only)
x-publora-client No Client identifier (e.g., mcp for MCP integrations). Affects which access controls are checked.

Query Parameters

Parameter Type Default Description
page number 1 Page number (1-indexed). Values less than 1 are silently clamped to 1.
limit number 20 Items per page. Values are clamped to the range 1–100, with falsy values (0, NaN, empty) defaulting to 20.
status string all Filter by status: draft, scheduled, published, failed, partially_published. Note: pending and processing are valid per-platform ScheduledPost statuses (not ScheduledPostGroup statuses) and cannot be used as filter values here.
platform string all Filter by platform: twitter, linkedin, instagram, threads, tiktok, youtube, facebook, bluesky, mastodon, telegram, pinterest. Internally, the filter applies a case-insensitive regex against stored compound platform IDs (e.g., twitter-123). The filter uses a regex anchored to the start of the string (^platform-), so passing a bare platform name like twitter matches all Twitter connections. Passing a full compound ID like twitter-123 would generate regex ^twitter-123- which requires a trailing dash and will likely return no results — use bare platform names instead. No validation is performed on the value — invalid names (e.g., ?platform=foobar) silently return an empty result set rather than an error.
sortBy string createdAt Sort field: createdAt, scheduledTime, updatedAt
sortOrder string desc Sort order: asc or desc. Invalid values silently default to desc.
fromDate string - Filter posts scheduled on or after this date (inclusive, uses $gte). Accepts any date string parseable by JavaScript's new Date(), though ISO 8601 is recommended for consistency. Filters on scheduledTime only — drafts without a scheduledTime are excluded when this parameter is used.
toDate string - Filter posts scheduled on or before this date (inclusive, uses $lte). Accepts any date string parseable by JavaScript's new Date(), though ISO 8601 is recommended for consistency. Filters on scheduledTime only — drafts without a scheduledTime are excluded when this parameter is used.

Response

{
  "success": true,
  "posts": [
    {
      "postGroupId": "507f1f77bcf86cd799439011",
      "content": "Excited to share our new product launch!",
      "status": "scheduled",
      "scheduledTime": "2026-03-15T14:30:00.000Z",
      "createdAt": "2026-03-10T09:00:00.000Z",
      "updatedAt": "2026-03-10T09:00:00.000Z",
      "platforms": [
        {
          "platformId": "twitter-123456789",
          "platform": "twitter",
          "status": "scheduled"
        },
        {
          "platformId": "linkedin-ABC123",
          "platform": "linkedin",
          "status": "scheduled"
        }
      ],
      "mediaUrls": ["https://your-media-url.example.com/images/abc123.jpg"]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalItems": 47,
    "totalPages": 3,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

Note: The platformId field in list-posts uses a compound format (e.g., "twitter-123456789"), combining the platform name with the raw ID. This differs from the get-post endpoint, which returns only the raw platform ID (e.g., "123456789").

Note: Filtering by ?status=published returns only posts where all platforms published successfully. It does not include partially_published posts. This is an exact match and differs from dashboard behavior, which may group these statuses together.

Pagination Fields

Field Type Description
page number Current page number
limit number Items per page
totalItems number Total number of posts matching filters
totalPages number Total number of pages
hasNextPage boolean Whether more pages exist after current
hasPrevPage boolean Whether pages exist before current

Examples

JavaScript - Fetch All Scheduled Posts with Pagination

async function fetchAllScheduledPosts(apiKey) {
  const posts = [];
  let page = 1;
  let hasMore = true;
 
  while (hasMore) {
    const url = new URL('https://api.publora.com/api/v1/list-posts');
    url.searchParams.set('page', page);
    url.searchParams.set('limit', 100);
    url.searchParams.set('status', 'scheduled');
    url.searchParams.set('sortBy', 'scheduledTime');
    url.searchParams.set('sortOrder', 'asc');
 
    const response = await fetch(url, {
      headers: { 'x-publora-key': apiKey }
    });
    const data = await response.json();
 
    posts.push(...data.posts);
    hasMore = data.pagination.hasNextPage;
    page++;
 
    console.log(`Fetched page ${data.pagination.page} of ${data.pagination.totalPages}`);
  }
 
  console.log(`Total scheduled posts: ${posts.length}`);
  return posts;
}
 
// Usage
const scheduledPosts = await fetchAllScheduledPosts('YOUR_API_KEY');

JavaScript - Paginated List with UI Controls

class PostListPaginator {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://api.publora.com/api/v1/list-posts';
    this.currentPage = 1;
    this.limit = 20;
    this.filters = {};
  }
 
  setFilters({ status, platform, fromDate, toDate }) {
    this.filters = { status, platform, fromDate, toDate };
    this.currentPage = 1; // Reset to first page when filters change
  }
 
  async fetchPage(page = this.currentPage) {
    const url = new URL(this.baseUrl);
    url.searchParams.set('page', page);
    url.searchParams.set('limit', this.limit);
 
    // Apply filters
    if (this.filters.status) url.searchParams.set('status', this.filters.status);
    if (this.filters.platform) url.searchParams.set('platform', this.filters.platform);
    if (this.filters.fromDate) url.searchParams.set('fromDate', this.filters.fromDate);
    if (this.filters.toDate) url.searchParams.set('toDate', this.filters.toDate);
 
    const response = await fetch(url, {
      headers: { 'x-publora-key': this.apiKey }
    });
    const data = await response.json();
 
    this.currentPage = data.pagination.page;
    return data;
  }
 
  async nextPage() {
    return this.fetchPage(this.currentPage + 1);
  }
 
  async prevPage() {
    return this.fetchPage(Math.max(1, this.currentPage - 1));
  }
 
  async goToPage(page) {
    return this.fetchPage(page);
  }
}
 
// Usage
const paginator = new PostListPaginator('YOUR_API_KEY');
 
// Get first page of scheduled posts
paginator.setFilters({ status: 'scheduled' });
const firstPage = await paginator.fetchPage();
 
console.log(`Showing ${firstPage.posts.length} of ${firstPage.pagination.totalItems} posts`);
 
// Navigate pages
if (firstPage.pagination.hasNextPage) {
  const secondPage = await paginator.nextPage();
}

Python - Fetch All Posts with Pagination

import requests
from typing import Generator, Dict, Any, Optional
 
def fetch_posts_paginated(
    api_key: str,
    status: Optional[str] = None,
    platform: Optional[str] = None,
    limit: int = 100
) -> Generator[Dict[str, Any], None, None]:
    """
    Generator that yields posts one by one, handling pagination automatically.
 
    Args:
        api_key: Your Publora API key
        status: Filter by status (draft, scheduled, published, failed, partially_published)
        platform: Filter by platform (twitter, linkedin, etc.)
        limit: Items per page (max 100)
 
    Yields:
        Individual post objects
    """
    base_url = 'https://api.publora.com/api/v1/list-posts'
    headers = {'x-publora-key': api_key}
    page = 1
 
    while True:
        params = {
            'page': page,
            'limit': limit,
            'sortBy': 'scheduledTime',
            'sortOrder': 'asc'
        }
 
        if status:
            params['status'] = status
        if platform:
            params['platform'] = platform
 
        response = requests.get(base_url, headers=headers, params=params)
        response.raise_for_status()
        data = response.json()
 
        for post in data['posts']:
            yield post
 
        if not data['pagination']['hasNextPage']:
            break
 
        page += 1
 
 
def fetch_all_scheduled_posts(api_key: str) -> list:
    """Fetch all scheduled posts into a list."""
    posts = list(fetch_posts_paginated(api_key, status='scheduled'))
    print(f"Fetched {len(posts)} scheduled posts")
    return posts
 
 
# Usage
api_key = 'YOUR_API_KEY'
 
# Iterate through all scheduled posts
for post in fetch_posts_paginated(api_key, status='scheduled'):
    print(f"{post['postGroupId']}: {post['content'][:50]}...")
    print(f"  Scheduled for: {post['scheduledTime']}")
    print(f"  Platforms: {[p['platform'] for p in post['platforms']]}")
 
# Or fetch all at once
all_posts = fetch_all_scheduled_posts(api_key)

Python - Filter Posts by Date Range

import requests
from datetime import datetime, timedelta, timezone
 
def get_posts_for_week(api_key: str, start_date: datetime) -> list:
    """Get all posts scheduled for a specific week."""
    end_date = start_date + timedelta(days=7)
 
    params = {
        'status': 'scheduled',
        'fromDate': start_date.isoformat(),
        'toDate': end_date.isoformat(),
        'sortBy': 'scheduledTime',
        'sortOrder': 'asc',
        'limit': 100
    }
 
    response = requests.get(
        'https://api.publora.com/api/v1/list-posts',
        headers={'x-publora-key': api_key},
        params=params
    )
 
    return response.json()['posts']
 
 
# Usage: Get posts scheduled for next week
next_monday = datetime.now(timezone.utc).replace(
    hour=0, minute=0, second=0, microsecond=0
)
# Adjust to next Monday
days_until_monday = (7 - next_monday.weekday()) % 7
next_monday += timedelta(days=days_until_monday)
 
posts = get_posts_for_week('YOUR_API_KEY', next_monday)
print(f"Posts scheduled for next week: {len(posts)}")

Node.js (axios) - Paginated Fetch with Async Iterator

const axios = require('axios');
 
async function* paginatedPosts(apiKey, options = {}) {
  const { status, platform, limit = 100 } = options;
  let page = 1;
  let hasMore = true;
 
  const api = axios.create({
    baseURL: 'https://api.publora.com/api/v1',
    headers: { 'x-publora-key': apiKey }
  });
 
  while (hasMore) {
    const params = { page, limit, sortBy: 'scheduledTime', sortOrder: 'asc' };
    if (status) params.status = status;
    if (platform) params.platform = platform;
 
    const { data } = await api.get('/list-posts', { params });
 
    for (const post of data.posts) {
      yield post;
    }
 
    hasMore = data.pagination.hasNextPage;
    page++;
  }
}
 
// Usage
(async () => {
  // Iterate through all scheduled posts
  for await (const post of paginatedPosts('YOUR_API_KEY', { status: 'scheduled' })) {
    console.log(`${post.postGroupId}: ${post.content.slice(0, 50)}...`);
    console.log(`  Scheduled: ${post.scheduledTime}`);
  }
})();

cURL - Basic Pagination

# Get first page of scheduled posts
curl "https://api.publora.com/api/v1/list-posts?page=1&limit=20&status=scheduled" \
  -H "x-publora-key: YOUR_API_KEY"
 
# Get second page
curl "https://api.publora.com/api/v1/list-posts?page=2&limit=20&status=scheduled" \
  -H "x-publora-key: YOUR_API_KEY"
 
# Filter by platform and date range
curl "https://api.publora.com/api/v1/list-posts?status=scheduled&platform=twitter&fromDate=2026-03-01T00:00:00Z&toDate=2026-03-31T23:59:59Z" \
  -H "x-publora-key: YOUR_API_KEY"
 
# Get all posts sorted by scheduled time
curl "https://api.publora.com/api/v1/list-posts?sortBy=scheduledTime&sortOrder=asc&limit=100" \
  -H "x-publora-key: YOUR_API_KEY"

Bash - Fetch All Pages

#!/bin/bash
 
API_KEY="YOUR_API_KEY"
BASE_URL="https://api.publora.com/api/v1/list-posts"
PAGE=1
LIMIT=100
ALL_POSTS="[]"
 
while true; do
  RESPONSE=$(curl -s "${BASE_URL}?page=${PAGE}&limit=${LIMIT}&status=scheduled" \
    -H "x-publora-key: ${API_KEY}")
 
  POSTS=$(echo "$RESPONSE" | jq '.posts')
  HAS_NEXT=$(echo "$RESPONSE" | jq '.pagination.hasNextPage')
  TOTAL=$(echo "$RESPONSE" | jq '.pagination.totalItems')
 
  # Merge posts
  ALL_POSTS=$(echo "$ALL_POSTS" "$POSTS" | jq -s 'add')
 
  echo "Fetched page $PAGE (Total items: $TOTAL)"
 
  if [ "$HAS_NEXT" = "false" ]; then
    break
  fi
 
  PAGE=$((PAGE + 1))
done
 
echo "Total posts fetched: $(echo "$ALL_POSTS" | jq 'length')"
echo "$ALL_POSTS" > scheduled_posts.json

Errors

Status Error Cause
400 "Invalid status. Must be one of: draft, scheduled, published, failed, partially_published" Invalid status filter value
400 "Invalid sortBy. Must be one of: createdAt, updatedAt, scheduledTime" Invalid sortBy field
400 "Invalid fromDate format" fromDate is not a valid date string (not parseable by new Date())
400 "Invalid toDate format" toDate is not a valid date string (not parseable by new Date())
400 "Invalid x-publora-user-id" The x-publora-user-id header value is not a valid ObjectId format
401 "API key is required" Missing x-publora-key header
401 "Invalid API key" The provided API key is not valid
401 "Invalid API key owner" API key exists but the associated workspace/user could not be resolved
403 "API access is not enabled for this account" The account does not have API access enabled
403 "MCP access is not enabled for this account" Returned when x-publora-client: mcp is set but MCP access is not enabled
403 "Workspace access is not enabled for this key" The API key does not have workspace/managed-user permissions
403 "User is not managed by key" The x-publora-user-id references a user not managed by this API key
500 "Failed to list posts" Internal server error

Note: The page and limit parameters do not return errors for out-of-range values. Instead, they are silently clamped to valid ranges: page is clamped to a minimum of 1, and limit is clamped to the range 1–100 (with falsy values like 0 defaulting to 20).

Best Practices

  1. Use reasonable page sizes. For UI pagination, 20-50 items is typical. For bulk exports, use the max of 100.

  2. Always handle pagination. Don't assume all posts fit on one page. Check hasNextPage or loop until page >= totalPages.

  3. Cache total counts. The totalItems count is useful for UI but may be expensive. Cache it for paginated UIs.

  4. Filter server-side. Use query parameters to filter by status and platform rather than fetching all posts and filtering client-side.

  5. Use date ranges for calendars. When building calendar views, use fromDate and toDate to fetch only the visible range.


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.