PostPost

PHP

Integrate PostPost API with PHP using cURL and modern PHP practices.

Requirements

  • PHP 7.4+ (8.0+ recommended)
  • cURL extension enabled

Basic Client Class

<?php
// src/PostPostClient.php

class PostPostException extends Exception
{
    public int $statusCode;
    public array $body;

    public function __construct(int $statusCode, string $message, array $body = [])
    {
        parent::__construct($message);
        $this->statusCode = $statusCode;
        $this->body = $body;
    }
}

class PostPostClient
{
    private const BASE_URL = 'https://api.postpost.dev/api/v1';

    private string $apiKey;
    private ?string $userId;

    public function __construct(string $apiKey, ?string $userId = null)
    {
        $this->apiKey = $apiKey;
        $this->userId = $userId;
    }

    private function request(string $method, string $endpoint, ?array $data = null): array
    {
        $url = self::BASE_URL . $endpoint;

        $headers = [
            'Content-Type: application/json',
            'x-api-key: ' . $this->apiKey,
        ];

        if ($this->userId) {
            $headers[] = 'x-postpost-user-id: ' . $this->userId;
        }

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_TIMEOUT => 30,
        ]);

        if ($data !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        $response = curl_exec($ch);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new PostPostException(0, 'cURL error: ' . $error);
        }

        $body = json_decode($response, true) ?? [];

        if ($statusCode >= 400) {
            $message = $body['error'] ?? $body['message'] ?? 'Unknown API error';
            throw new PostPostException($statusCode, $message, $body);
        }

        return $body;
    }

    public function getConnections(): array
    {
        $result = $this->request('GET', '/platform-connections');
        return $result['connections'] ?? [];
    }

    public function createPost(array $data): array
    {
        return $this->request('POST', '/create-post', $data);
    }

    public function getPost(string $postGroupId): array
    {
        return $this->request('GET', '/get-post/' . $postGroupId);
    }

    public function updatePost(string $postGroupId, array $updates): array
    {
        return $this->request('PUT', '/update-post/' . $postGroupId, $updates);
    }

    public function deletePost(string $postGroupId): array
    {
        return $this->request('DELETE', '/delete-post/' . $postGroupId);
    }

    public function getUploadUrl(string $fileName, string $contentType, string $postGroupId): array
    {
        return $this->request('POST', '/get-upload-url', [
            'fileName' => $fileName,
            'contentType' => $contentType,
            'postGroupId' => $postGroupId,
        ]);
    }

    public function getLinkedInPostStats(string $platformId, string $postedId): array
    {
        return $this->request('POST', '/linkedin-post-statistics', [
            'platformId' => $platformId,
            'postedId' => $postedId,
            'queryTypes' => 'ALL',
        ]);
    }

    public function getLinkedInAccountStats(string $platformId): array
    {
        return $this->request('POST', '/linkedin-account-statistics', [
            'platformId' => $platformId,
        ]);
    }
}

Usage Examples

Initialize Client

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

List Platform Connections

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

try {
    $connections = $client->getConnections();

    echo "Found " . count($connections) . " connected accounts:\n";
    foreach ($connections as $conn) {
        echo "  - {$conn['platform']}: {$conn['username']} ({$conn['platformId']})\n";
    }
} catch (PostPostException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Create a Simple Post

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

try {
    $response = $client->createPost([
        'content' => 'Hello from PHP! Posting with PostPost API.',
        'platforms' => ['twitter-123456789', 'linkedin-ABC123DEF'],
    ]);

    echo "Post created: {$response['postGroupId']}\n";
    foreach ($response['posts'] as $post) {
        echo "  - {$post['platform']}: {$post['status']}\n";
    }
} catch (PostPostException $e) {
    echo "Failed to create post: " . $e->getMessage() . "\n";
}

Schedule a Post

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

// Schedule for tomorrow at 10 AM UTC
$scheduledTime = (new DateTime('+1 day'))
    ->setTime(10, 0, 0)
    ->format(DateTime::ATOM);

try {
    $response = $client->createPost([
        'content' => 'This post will go live tomorrow at 10 AM UTC!',
        'platforms' => ['twitter-123456789'],
        'scheduledTime' => $scheduledTime,
    ]);

    echo "Post scheduled: {$response['postGroupId']}\n";
    echo "Will publish at: $scheduledTime\n";
} catch (PostPostException $e) {
    echo "Failed to schedule: " . $e->getMessage() . "\n";
}

Schedule Multiple Posts (Week of Content)

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

$posts = [
    ['content' => 'Monday motivation: Start your week with purpose!', 'dayOffset' => 0],
    ['content' => 'Tech tip Tuesday: Always version your APIs.', 'dayOffset' => 1],
    ['content' => 'Wednesday wisdom: Ship fast, iterate faster.', 'dayOffset' => 2],
    ['content' => 'Throwback Thursday: How we grew to 10K users.', 'dayOffset' => 3],
    ['content' => 'Feature Friday: Check out our new dashboard!', 'dayOffset' => 4],
];

$baseTime = new DateTime('tomorrow 09:00:00', new DateTimeZone('UTC'));

foreach ($posts as $post) {
    $scheduledTime = (clone $baseTime)
        ->modify("+{$post['dayOffset']} days")
        ->format(DateTime::ATOM);

    try {
        $response = $client->createPost([
            'content' => $post['content'],
            'platforms' => ['twitter-123456789', 'linkedin-ABC123DEF'],
            'scheduledTime' => $scheduledTime,
        ]);

        echo "Scheduled: " . substr($post['content'], 0, 30) . "... -> {$response['postGroupId']}\n";
        usleep(200000); // 200ms rate limiting
    } catch (PostPostException $e) {
        echo "Failed: " . $e->getMessage() . "\n";
    }
}

Create a Post (Media Auto-Attaches via postGroupId)

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

try {
    $response = $client->createPost([
        'content' => 'Check out this amazing screenshot!',
        'platforms' => ['twitter-123456789', 'linkedin-ABC123DEF'],
    ]);

    echo "Post created: {$response['postGroupId']}\n";
} catch (PostPostException $e) {
    echo "Failed: " . $e->getMessage() . "\n";
}

Post with Platform-Specific Settings

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

// Instagram Reel (upload media first, it auto-attaches via postGroupId)
try {
    $response = $client->createPost([
        'content' => 'Behind the scenes! #buildinpublic',
        'platforms' => ['instagram-789012345'],
        'platformSettings' => [
            'instagram' => [
                'videoType' => 'REELS',
            ],
        ],
    ]);

    echo "Instagram Reel scheduled: {$response['postGroupId']}\n";
} catch (PostPostException $e) {
    echo "Failed: " . $e->getMessage() . "\n";
}

// Telegram with Markdown
try {
    $response = $client->createPost([
        'content' => '*Bold* and _italic_ text with [link](https://example.com)',
        'platforms' => ['telegram-1001234567890'],
        'platformSettings' => [
            'telegram' => [
                'parseMode' => 'MarkdownV2',
                'disableWebPagePreview' => false,
            ],
        ],
    ]);

    echo "Telegram message scheduled: {$response['postGroupId']}\n";
} catch (PostPostException $e) {
    echo "Failed: " . $e->getMessage() . "\n";
}

Upload Media and Post

<?php
require_once 'src/PostPostClient.php';

function getMimeType(string $filename): string
{
    $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    $mimeTypes = [
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'png' => 'image/png',
        'gif' => 'image/gif',
        'mp4' => 'video/mp4',
        'webp' => 'image/webp',
    ];
    return $mimeTypes[$ext] ?? 'application/octet-stream';
}

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);
$filePath = './screenshot.png';

try {
    // Step 1: Create post first to get postGroupId
    $response = $client->createPost([
        'content' => 'Check out this screenshot!',
        'platforms' => ['twitter-123456789', 'linkedin-ABC123DEF'],
    ]);

    $postGroupId = $response['postGroupId'];
    echo "Post created: $postGroupId\n";

    // Step 2: Get upload URL (media auto-attaches via postGroupId)
    $fileName = basename($filePath);
    $contentType = getMimeType($fileName);

    $uploadResult = $client->getUploadUrl($fileName, $contentType, $postGroupId);

    // Step 3: Upload file to S3
    $fileContent = file_get_contents($filePath);

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $uploadResult['uploadUrl'],
        CURLOPT_CUSTOMREQUEST => 'PUT',
        CURLOPT_POSTFIELDS => $fileContent,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: ' . $contentType,
        ],
    ]);

    $uploadResponse = curl_exec($ch);
    $uploadStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($uploadStatus >= 400) {
        throw new Exception("Upload failed with status $uploadStatus");
    }

    echo "Media uploaded: {$uploadResult['mediaId']}\n";
    echo "File URL: {$uploadResult['fileUrl']}\n";
    echo "Post with uploaded media created: $postGroupId\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Error Handling

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

try {
    $response = $client->createPost([
        'content' => 'Test post',
        'platforms' => ['twitter-123456789'],
    ]);

    echo "Success: {$response['postGroupId']}\n";
} catch (PostPostException $e) {
    switch ($e->statusCode) {
        case 401:
            echo "Authentication failed. Check your API key.\n";
            break;
        case 403:
            echo "Access denied: " . $e->getMessage() . "\n";
            // Handle subscription or limit issues
            break;
        case 400:
            echo "Bad request: " . $e->getMessage() . "\n";
            // Handle validation errors
            break;
        case 404:
            echo "Not found: " . $e->getMessage() . "\n";
            break;
        case 429:
            echo "Rate limited. Retry after delay.\n";
            break;
        case 500:
            echo "Server error. Retry later.\n";
            break;
        default:
            echo "API error ({$e->statusCode}): " . $e->getMessage() . "\n";
    }
} catch (Exception $e) {
    echo "Network or unexpected error: " . $e->getMessage() . "\n";
}

Monitor Post Status

<?php
require_once 'src/PostPostClient.php';

function waitForPublish(PostPostClient $client, string $postGroupId, int $maxAttempts = 30): array
{
    for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
        $postGroup = $client->getPost($postGroupId);
        $status = $postGroup['status'];

        echo "[$attempt/$maxAttempts] Status: $status\n";

        switch ($status) {
            case 'published':
                echo "All platforms published successfully!\n";
                return $postGroup;

            case 'partially_published':
                echo "Partial success:\n";
                foreach ($postGroup['posts'] as $post) {
                    if ($post['status'] === 'failed') {
                        echo "  - {$post['platform']}: FAILED - {$post['error']}\n";
                    } else {
                        echo "  - {$post['platform']}: {$post['status']}\n";
                    }
                }
                return $postGroup;

            case 'failed':
                echo "All platforms failed:\n";
                foreach ($postGroup['posts'] as $post) {
                    echo "  - {$post['platform']}: {$post['error']}\n";
                }
                return $postGroup;
        }

        sleep(10);
    }

    throw new Exception("Timeout waiting for post $postGroupId to publish");
}

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

try {
    $response = $client->createPost([
        'content' => 'Publishing now!',
        'platforms' => ['twitter-123456789'],
    ]);

    $postGroup = waitForPublish($client, $response['postGroupId']);
    echo "Final status: {$postGroup['status']}\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Import from CSV

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

$csvFile = 'posts.csv';
$handle = fopen($csvFile, 'r');

// Skip header
fgetcsv($handle);

$results = [];

while (($row = fgetcsv($handle)) !== false) {
    [$content, $platforms, $scheduledTime] = $row;

    $platformIds = array_map('trim', explode(';', $platforms));

    $postData = [
        'content' => $content,
        'platforms' => $platformIds,
        'scheduledTime' => $scheduledTime,
    ];

    try {
        $response = $client->createPost($postData);
        $results[] = [
            'content' => substr($content, 0, 30) . '...',
            'success' => true,
            'postGroupId' => $response['postGroupId'],
        ];
        echo "✓ " . substr($content, 0, 40) . "...\n";
    } catch (PostPostException $e) {
        $results[] = [
            'content' => substr($content, 0, 30) . '...',
            'success' => false,
            'error' => $e->getMessage(),
        ];
        echo "✗ " . substr($content, 0, 40) . "... - {$e->getMessage()}\n";
    }

    usleep(200000); // Rate limiting
}

fclose($handle);

$successful = count(array_filter($results, fn($r) => $r['success']));
echo "\nImported $successful/" . count($results) . " posts\n";

LinkedIn Analytics

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

$platformId = 'linkedin-ABC123DEF';
$postedIds = [
    'urn:li:share:7123456789012345678',
    'urn:li:share:7234567890123456789',
];

echo "=== LinkedIn Analytics Report ===\n\n";

$totalImpressions = 0;
$totalEngagement = 0;

foreach ($postedIds as $postedId) {
    try {
        $result = $client->getLinkedInPostStats($platformId, $postedId);

        $metrics = $result['metrics'];
        $engagement = $metrics['REACTION'] + $metrics['COMMENT'] + $metrics['RESHARE'];
        $totalImpressions += $metrics['IMPRESSION'];
        $totalEngagement += $engagement;

        echo "Post: $postedId\n";
        echo "  Impressions: " . number_format($metrics['IMPRESSION']) . "\n";
        echo "  Members Reached: " . number_format($metrics['MEMBERS_REACHED']) . "\n";
        echo "  Engagement: $engagement ({$metrics['REACTION']} reactions, {$metrics['COMMENT']} comments, {$metrics['RESHARE']} reshares)\n\n";
    } catch (PostPostException $e) {
        echo "Post: $postedId - Error: {$e->getMessage()}\n\n";
    }
}

echo "=== TOTALS ===\n";
echo "Total Impressions: " . number_format($totalImpressions) . "\n";
echo "Total Engagement: $totalEngagement\n";
if ($totalImpressions > 0) {
    echo "Avg Engagement Rate: " . number_format(($totalEngagement / $totalImpressions) * 100, 2) . "%\n";
}

Batch Delete Posts

<?php
require_once 'src/PostPostClient.php';

$client = new PostPostClient($_ENV['PUBLORA_API_KEY']);

$postGroupIds = ['pg_abc123', 'pg_def456', 'pg_ghi789'];

$results = [];

foreach ($postGroupIds as $id) {
    try {
        $client->deletePost($id);
        $results[] = ['id' => $id, 'success' => true];
        echo "✓ Deleted $id\n";
    } catch (PostPostException $e) {
        $results[] = ['id' => $id, 'success' => false, 'error' => $e->getMessage()];
        echo "✗ Failed to delete $id: {$e->getMessage()}\n";
    }

    usleep(100000); // 100ms rate limiting
}

$successful = count(array_filter($results, fn($r) => $r['success']));
echo "\nDeleted $successful/" . count($postGroupIds) . " posts\n";

Laravel Integration

<?php
// config/services.php
return [
    // ...
    'postpost' => [
        'api_key' => env('PUBLORA_API_KEY'),
    ],
];

// app/Services/PostPostService.php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class PostPostService
{
    private const BASE_URL = 'https://api.postpost.dev/api/v1';
    private string $apiKey;

    public function __construct()
    {
        $this->apiKey = config('services.postpost.api_key');
    }

    public function createPost(string $content, array $platforms, ?string $scheduledTime = null): array
    {
        $response = Http::withHeaders([
            'x-api-key' => $this->apiKey,
        ])->post(self::BASE_URL . '/create-post', [
            'content' => $content,
            'platforms' => $platforms,
            'scheduledTime' => $scheduledTime,
        ]);

        if ($response->failed()) {
            throw new \Exception($response->json('error') ?? 'Failed to create post');
        }

        return $response->json();
    }

    public function getConnections(): array
    {
        $response = Http::withHeaders([
            'x-api-key' => $this->apiKey,
        ])->get(self::BASE_URL . '/platform-connections');

        return $response->json('connections', []);
    }
}

// Usage in controller
use App\Services\PostPostService;

class SocialController extends Controller
{
    public function schedulePost(Request $request, PostPostService $postpost)
    {
        $validated = $request->validate([
            'content' => 'required|string|max:3000',
            'platforms' => 'required|array',
            'scheduled_time' => 'nullable|date|after:now',
        ]);

        $result = $postpost->createPost(
            $validated['content'],
            $validated['platforms'],
            $validated['scheduled_time'] ?? null
        );

        return response()->json([
            'success' => true,
            'post_group_id' => $result['postGroupId'],
        ]);
    }
}

PostPost — Social media API with free tier, paid plans from $2.99/account

On this page