The official PHP SDK for the FoPost API. Connect social accounts once, then compose, schedule, and publish from your own application.
Requires PHP 8.1 or newer, plus ext-curl and ext-json. Nothing else: no framework, no HTTP library.
composer require fopost/sdkCreate a key at fopost.com/dashboard/api-keys. The full API reference lives at fopost.com/docs.
<?php
require __DIR__ . '/vendor/autoload.php';
use Fopost\Sdk\Client;
$client = new Client('fp_...'); // or set FOPOST_API_KEY
$workspace = $client->workspaces()->list()[0];
$accounts = $client->accounts()->list($workspace->id);
$post = $client->posts()->create(
workspaceId: $workspace->id,
content: 'Hello from PHP',
accounts: array_map(fn ($a) => $a->id, $accounts),
);
$client->posts()->publish($post->id);The key falls back to the FOPOST_API_KEY environment variable, so new Client() works when it is set.
$client = new Client(
apiKey: 'fp_...',
baseUrl: 'https://api.fopost.com', // a bare host gets /v1 appended
timeout: 30.0, // seconds
maxRetries: 3, // attempts, on 429 only
);use DateTimeImmutable;
// List one page. The result iterates over its items directly.
$page = $client->posts()->list(workspaceId: $workspaceId, status: 'scheduled');
foreach ($page as $post) {
echo $post->id, ' ', $post->status, PHP_EOL;
}
echo $page->meta->total;
// Walk every matching post, one page at a time.
foreach ($client->posts()->iterate(workspaceId: $workspaceId) as $post) {
echo $post->text(), PHP_EOL;
}
$post = $client->posts()->get('p_123');
// Create a draft, a thread, or a scheduled post.
$draft = $client->posts()->create(
workspaceId: $workspaceId,
content: ['First post', 'The reply'],
accounts: ['acc_1', 'acc_2'],
);
$scheduled = $client->posts()->create(
workspaceId: $workspaceId,
content: 'Going out on Monday',
accounts: ['acc_1'],
status: 'scheduled',
scheduleAt: new DateTimeImmutable('2026-03-01T09:00:00Z'),
);
// Partial update: only the fields you name are sent.
$client->posts()->update($draft->id, title: 'A better title');
$client->posts()->schedule($draft->id, new DateTimeImmutable('+1 day'));
$client->posts()->unschedule($draft->id);
$client->posts()->publish($draft->id);
$client->posts()->preflight($draft->id);
$client->posts()->retry($draft->id);
$client->posts()->cancel($draft->id);
$client->posts()->delete($draft->id);
foreach ($client->posts()->deliveries($draft->id) as $delivery) {
echo $delivery->platform, ' ', $delivery->status, ' ', $delivery->externalUrl, PHP_EOL;
}$accounts = $client->accounts()->list($workspaceId);
$account = $client->accounts()->get('acc_1');
$health = $client->accounts()->health('acc_1');
$client->accounts()->disconnect('acc_1');$workspaces = $client->workspaces()->list();
$workspace = $client->workspaces()->get($workspaceId);
echo $workspace->name, ' ', $workspace->timezone, PHP_EOL;$labels = $client->labels()->list($workspaceId);
$label = $client->labels()->create($workspaceId, 'Product launch', '#0070f3');
$client->labels()->update($label->id, name: 'Launch week');
$client->labels()->delete($label->id);Every AI call spends AI credits.
$balance = $client->ai()->credits();
echo $balance->creditsRemaining, ' of ', $balance->creditsTotal, PHP_EOL;
$caption = $client->ai()->generateCaption(
currentCaption: 'new feature is live',
platforms: ['linkedin', 'bluesky'],
charLimit: 280,
);
$rewrite = $client->ai()->rewrite('One draft, many networks', ['linkedin', 'bluesky'], tone: 'friendly');
foreach ($rewrite->results as $variant) {
echo $variant->platform, ': ', $variant->content, PHP_EOL;
}
$repurposed = $client->ai()->repurposeUrl('https://example.com/blog/launch', ['linkedin', 'threads']);Every non-2xx response raises an exception under Fopost\Sdk\Exception.
| Status | Exception |
|---|---|
| 400, 422 | ValidationException |
| 401 | AuthenticationException |
| 402 | PaymentRequiredException |
| 403 | PermissionDeniedException |
| 404 | NotFoundException |
| 429 | RateLimitException |
| anything else | ApiException |
All of them extend FopostException, which carries getStatus(), getErrorCode(), getMessage(), and getBody().
use Fopost\Sdk\Exception\FopostException;
use Fopost\Sdk\Exception\RateLimitException;
use Fopost\Sdk\Exception\ValidationException;
try {
$client->posts()->publish('p_123');
} catch (ValidationException $e) {
print_r($e->getErrors());
} catch (RateLimitException $e) {
echo 'retry in ', $e->getRetryAfter(), 's', PHP_EOL;
} catch (FopostException $e) {
echo $e->getStatus(), ' ', $e->getMessage(), PHP_EOL;
}A 429 is retried automatically, up to maxRetries attempts, waiting for the interval the API asks for in Retry-After (capped at 60 seconds). The exception is raised only when the last attempt still comes back rate limited.
$body = $client->request('GET', '/some/new/endpoint', params: ['workspace_id' => $workspaceId]);The transport is an interface, so nothing has to reach the network in your test suite.
use Fopost\Sdk\Client;
use Fopost\Sdk\Http\Response;
use Fopost\Sdk\Http\Transport;
$fake = new class implements Transport {
public function send(string $method, string $url, array $headers, ?string $body): Response
{
return new Response(200, [], json_encode(['data' => []]));
}
};
$client = new Client('fop_test_key', Client::DEFAULT_BASE_URL, 30.0, 3, $fake);Questions and issues: fopost.com/contact or the issue tracker.
MIT. Copyright Porter Bridge, LLC.