PostPost

Platform Connections

List all connected social media accounts.

Endpoint

GET https://api.postpost.dev/api/v1/platform-connections

Headers

HeaderRequiredDescription
x-api-keyYesYour API key
x-postpost-user-idNoManaged user ID (workspace only)
x-postpost-clientNoClient identifier (e.g., mcp). Used for MCP access gating.

Server-to-server only: The x-api-key, x-postpost-user-id, and x-postpost-client headers are not included in the server's CORS Access-Control-Allow-Headers. Browser-based requests that send these custom headers will fail the CORS preflight check. Use this API from a backend server or serverless function, not directly from client-side JavaScript.

Response

{
  "success": true,
  "connections": [
    {
      "platformId": "twitter-123456789",
      "username": "yourhandle",
      "displayName": "Your Name",
      "profileImageUrl": "https://pbs.twimg.com/profile_images/...",
      "profileUrl": "https://twitter.com/yourhandle",
      "accessTokenExpiresAt": null,
      "tokenStatus": "unknown",
      "tokenExpiresIn": null,
      "lastSuccessfulPost": "2026-02-20T14:30:00.000Z",
      "lastError": null
    },
    {
      "platformId": "linkedin-Tz9W5i6ZYG",
      "username": "John Doe",
      "displayName": "John Doe",
      "profileImageUrl": "https://media.licdn.com/...",
      "profileUrl": "https://linkedin.com/in/johndoe",
      "accessTokenExpiresAt": "2026-05-15T10:30:00.000Z",
      "tokenStatus": "valid",
      "tokenExpiresIn": "82d 4h",
      "lastSuccessfulPost": "2026-02-22T09:15:00.000Z",
      "lastError": null
    },
    {
      "platformId": "instagram-17841412345678",
      "username": "yourinstagram",
      "displayName": "Your Brand",
      "profileImageUrl": "https://...",
      "profileUrl": null,
      "accessTokenExpiresAt": "2026-02-25T08:00:00.000Z",
      "tokenStatus": "expiring_soon",
      "tokenExpiresIn": "2d 12h",
      "lastSuccessfulPost": null,
      "lastError": {
        "message": "Media upload failed: Invalid image format",
        "occurredAt": "2026-02-21T16:45:00.000Z"
      }
    }
  ]
}

Connection Fields

FieldTypeDescription
platformIdstringUnique ID in format platform-id. Use this in the platforms array when creating posts.
usernamestringPlatform username or handle. Note: the stored username may or may not include a @ prefix depending on what was saved during OAuth.
displayNamestring/nullDisplay name on the platform. Returns null if not set during OAuth.
profileImageUrlstring/nullProfile image URL. Returns null if not available.
profileUrlstring/nullURL to the user's profile on the platform. Can be null if not available for the platform.
accessTokenExpiresAtstring/nullToken expiration timestamp (null = no expiration)
tokenStatusstringCurrent token health: valid, expiring_soon, expired, or unknown
tokenExpiresInstring/nullHuman-readable time until expiration. Possible formats: "7d 3h" (days and hours), "5h" (hours only, when less than one day remains), "< 1h" (less than one hour remaining), "expired" (token already expired), or null (no expiration).
lastSuccessfulPoststring/nullTimestamp of last successful post to this platform
lastErrorobject/nullLast error that occurred when posting to this platform. When present, contains: message (string — error description) and occurredAt (string — ISO 8601 timestamp of when the error occurred).

Token Status Values

StatusMeaning
validToken is valid and won't expire in less than 7 days
expiring_soonToken expires in less than 7 days (strictly less than) - consider reconnecting
expiredToken has expired - reconnect required
unknownPlatform doesn't use expiring tokens (e.g., Twitter, Bluesky)

Note: Pinterest has OAuth connection routes in the dashboard, but no test-connection validator is implemented. Calling test-connection for a Pinterest connection will return "Unknown platform: pinterest".

Platform ID Format

The platformId follows the pattern platform-id:

PlatformExample platformId
X / Twittertwitter-123456789
LinkedInlinkedin-Tz9W5i6ZYG
Instagraminstagram-17841412345678
Threadsthreads-17841412345678
TikToktiktok-7123456789
YouTubeyoutube-UCxxxxxxxxxxxx
Facebookfacebook-112233445566
Blueskybluesky-did:plc:abc123
Mastodonmastodon-109876543210
Telegramtelegram--1001234567890

Note: Bluesky platform IDs containing colons (e.g., bluesky-did:plc:abc123) are returned by this endpoint but cannot be used directly with create-post, which validates against a stricter regex that does not allow colons. Use the test-connection endpoint instead, which accepts these IDs.

Examples

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/platform-connections', {
  headers: { 'x-api-key': 'YOUR_API_KEY' }
});
const data = await response.json();

// Filter by platform
const twitterAccounts = data.connections.filter(c => c.platformId.startsWith('twitter-'));
const linkedinAccounts = data.connections.filter(c => c.platformId.startsWith('linkedin-'));

console.log(`Twitter accounts: ${twitterAccounts.length}`);
console.log(`LinkedIn accounts: ${linkedinAccounts.length}`);

Python (requests)

import requests

response = requests.get(
    'https://api.postpost.dev/api/v1/platform-connections',
    headers={'x-api-key': 'YOUR_API_KEY'}
)
connections = response.json()['connections']

# Get all platform IDs for cross-platform posting
all_platform_ids = [c['platformId'] for c in connections]
print(f"Connected to {len(all_platform_ids)} accounts")

# Check for expiring tokens
from datetime import datetime, timezone
for conn in connections:
    if conn.get('accessTokenExpiresAt'):
        expires = datetime.fromisoformat(conn['accessTokenExpiresAt'].replace('Z', '+00:00'))
        if expires < datetime.now(timezone.utc):
            print(f"⚠️  {conn['platformId']} token expired! Reconnect in dashboard.")

cURL

curl https://api.postpost.dev/api/v1/platform-connections \
  -H "x-api-key: YOUR_API_KEY"

Node.js (axios)

const axios = require('axios');

const { data } = await axios.get(
  'https://api.postpost.dev/api/v1/platform-connections',
  { headers: { 'x-api-key': 'YOUR_API_KEY' } }
);

// Build a map of platform → connection IDs
const platforms = {};
for (const conn of data.connections) {
  const platform = conn.platformId.split('-')[0];
  if (!platforms[platform]) platforms[platform] = [];
  platforms[platform].push(conn.platformId);
}
console.log(platforms);
// { twitter: ["twitter-123"], linkedin: ["linkedin-ABC"], ... }

With Error Handling

async function getConnections() {
  try {
    const response = await fetch('https://api.postpost.dev/api/v1/platform-connections', {
      headers: { 'x-api-key': process.env.PUBLORA_API_KEY }
    });

    if (response.status === 401) {
      throw new Error('Invalid API key. Check your PUBLORA_API_KEY.');
    }

    if (!response.ok) {
      const data = await response.json();
      throw new Error(data.error || `HTTP ${response.status}`);
    }

    const data = await response.json();

    // Check for expiring tokens
    const now = new Date();
    for (const conn of data.connections) {
      if (conn.accessTokenExpiresAt) {
        const expires = new Date(conn.accessTokenExpiresAt);
        const daysUntilExpiry = Math.floor((expires - now) / (1000 * 60 * 60 * 24));
        if (daysUntilExpiry < 7) {
          console.warn(`⚠️  ${conn.platformId} token expires in ${daysUntilExpiry} days`);
        }
      }
    }

    return data.connections;
  } catch (error) {
    console.error('Failed to fetch connections:', error.message);
    throw error;
  }
}
import os
import requests
from datetime import datetime, timezone

def get_connections():
    """Get all connected platforms with error handling and token expiry check."""
    try:
        response = requests.get(
            'https://api.postpost.dev/api/v1/platform-connections',
            headers={'x-api-key': os.environ['PUBLORA_API_KEY']}
        )

        if response.status_code == 401:
            raise ValueError('Invalid API key. Check your PUBLORA_API_KEY.')

        response.raise_for_status()
        data = response.json()

        # Check for expiring tokens
        now = datetime.now(timezone.utc)
        for conn in data['connections']:
            if conn.get('accessTokenExpiresAt'):
                expires = datetime.fromisoformat(
                    conn['accessTokenExpiresAt'].replace('Z', '+00:00')
                )
                days_until_expiry = (expires - now).days
                if days_until_expiry < 7:
                    print(f"⚠️  {conn['platformId']} token expires in {days_until_expiry} days")

        return data['connections']

    except requests.RequestException as e:
        print(f'Failed to fetch connections: {e}')
        raise


# Usage
connections = get_connections()
platform_ids = [c['platformId'] for c in connections]
print(f"Connected to {len(platform_ids)} platforms: {platform_ids}")

Errors

StatusErrorCause
400"Invalid x-postpost-user-id"x-postpost-user-id header contains an invalid ObjectId
401"API key is required"Missing x-api-key header
401"Invalid API key"x-api-key value is not a valid key
401"Invalid API key owner"The API key's owner account could not be found
403"API access is not enabled for this account"Account does not have API access enabled
403"MCP access is not enabled for this account"MCP access is not enabled when x-postpost-client: mcp is sent
403"Workspace access is not enabled for this key"API key does not have workspace permissions
403"User is not managed by key"The x-postpost-user-id user is not managed by this API key
500"Internal server error"Unexpected error in middleware
500"Failed to fetch platform connections"Server error while fetching connections

Connecting Accounts

Social accounts are connected via the PostPost dashboard (OAuth flow). The API does not support connecting new accounts programmatically -- use the dashboard at app.postpost.dev.

For workspace users, generate a connection URL via the Workspace API.


On this page