.NET SDK
The official .NET client for the FoPost API, with no third-party dependencies.
FoPost.Sdk is the official .NET client. It needs .NET 8 or newer and has no third-party dependencies.
dotnet add package FoPost.SdkThis is a 0.x release. The surface is still settling and a minor version may break something. Pin an exact version if that matters to you.
Published on NuGet. Source and issues: github.com/fopost/fopost-dotnet. MIT licensed.
Quick start
using FoPost;
using var client = new FoPostClient(Environment.GetEnvironmentVariable("FOPOST_API_KEY")!);
// List your accounts
var accounts = await client.Accounts.ListAsync("9b2f6c1e-…");
// Create a post, then publish it immediately
var post = await client.Posts.CreateAsync(
workspaceId: "9b2f6c1e-…",
text: "Hello from the SDK",
accounts: accounts.Select(account => account.Id));
await client.Posts.PublishAsync(post.Id);
// Schedule for later
await client.Posts.CreateAsync(new CreatePostOptions
{
WorkspaceId = "9b2f6c1e-…",
Status = PostStatuses.Scheduled,
ScheduleAt = new DateTimeOffset(2026, 9, 1, 10, 0, 0, TimeSpan.Zero),
Content = new List<PostContent> { new("Scheduled with the SDK") },
Accounts = new List<string> { accounts[0].Id },
});PublishAsync returns once delivery is queued, not once it is live. Read Posts.DeliveriesAsync or subscribe to webhooks for the result. A key is limited to the scopes granted at creation; the calls above need posts and accounts.
Configuration
using var client = new FoPostClient(new FoPostClientOptions
{
ApiKey = "fp_…", // defaults to $FOPOST_API_KEY
BaseUrl = "https://api.fopost.com", // override for another deployment
Timeout = TimeSpan.FromSeconds(30),
MaxRetries = 3, // total attempts on a 429, including the first
HttpClient = httpClientFromFactory, // optional; the SDK will not dispose it
});FoPostClient is thread-safe and meant to be long-lived, so register it as a singleton rather than constructing one per request. With IHttpClientFactory:
services.AddHttpClient("fopost");
services.AddSingleton(provider => new FoPostClient(new FoPostClientOptions
{
HttpClient = provider.GetRequiredService<IHttpClientFactory>().CreateClient("fopost"),
}));Paging
ListAsync returns one page plus its Meta. ListAllAsync walks every page for you:
await foreach (var post in client.Posts.ListAllAsync(new ListPostsOptions
{
WorkspaceId = "9b2f6c1e-…",
Status = PostStatuses.Scheduled,
}))
{
Console.WriteLine($"{post.ScheduleAt:u} {post.Content.FirstOrDefault()?.Text}");
}Partial updates
Every field on UpdatePostOptions is an Optional<T>, so "leave this alone" and "clear this" stay different things:
await client.Posts.UpdateAsync(post.Id, new UpdatePostOptions
{
Title = "New title", // sent
Summary = Optional<string?>.Of(null), // sent as null, clearing it
// ScheduleAt is untouched, not sent at all
});AI features
var caption = await client.Ai.GenerateCaptionAsync(new GenerateCaptionOptions
{
CurrentCaption = "shipping a new feature",
Platforms = new List<string> { Platforms.Twitter, Platforms.LinkedIn },
});
var balance = await client.Ai.CreditsAsync();
Console.WriteLine($"{balance.CreditsRemaining} of {balance.CreditsTotal} credits left");RewriteAsync and RepurposeUrlAsync reach dashboard-session endpoints: they
need a BearerToken rather than an API key, and answer 401 to a key. Use the
composer for those until a later release.
What is on the client
| Namespace | Methods |
|---|---|
Posts | ListAsync, ListAllAsync, GetAsync, CreateAsync, UpdateAsync, DeleteAsync, PublishAsync, CancelAsync, RetryAsync, PreflightAsync, DuplicateAsync, DeliveriesAsync |
Accounts | ListAsync, GetAsync, HealthAsync |
Workspaces | ListAsync, GetAsync |
Labels | ListAsync |
Ai | CreditsAsync, GenerateCaptionAsync, RewriteAsync, RepurposeUrlAsync |
The API has more endpoints than this client wraps: analytics, webhooks, automations, media, and communities among them. RequestAsync reaches any of them with the same auth, retries, and error handling:
var overview = await client.RequestAsync(
HttpMethod.Get,
"/v1/analytics/overview",
query: new Dictionary<string, object?> { ["workspace_id"] = "9b2f6c1e-…" });Errors
Every non-2xx response raises a FoPostException carrying the API's status, error code, and body.
| Status | Exception |
|---|---|
| 400, 422 | FoPostValidationException |
| 401 | FoPostAuthenticationException |
| 402 | FoPostPaymentRequiredException |
| 403 | FoPostPermissionDeniedException |
| 404 | FoPostNotFoundException |
| 429 | FoPostRateLimitException |
| anything else | FoPostException |
try
{
await client.Posts.PublishAsync(post.Id);
}
catch (FoPostRateLimitException error)
{
Console.WriteLine($"Slow down for {error.RetryAfter}");
}
catch (FoPostPaymentRequiredException error)
{
Console.WriteLine($"Upgrade at {error.UpgradeUrl}");
}
catch (FoPostException error)
{
Console.WriteLine($"API {error.Status} ({error.Code}): {error.Message}");
}A 429 is retried automatically, up to MaxRetries attempts, waiting for the interval the API asks for in Retry-After. The exception is raised only once the retries are spent.
Next
Related documentation
- SDKs & Integrations
Official FoPost clients for TypeScript, Python, PHP, Ruby, Go, Rust, Java, .NET, Swift, Kotlin, Dart, and Elixir, framework integrations from Laravel to Next.js, and tooling for the CLI, CI, Terraform, and the automation platforms.
- SDKs Overview
Every official FoPost client, what it covers, and how to pick one.
- TypeScript SDK
The official TypeScript and Node.js client for the FoPost API.
- Python SDK
The official Python client for the FoPost API.
- PHP SDK
The official PHP client for the FoPost API, with no framework and no HTTP library.
- Ruby SDK
The official Ruby client for the FoPost API, with no runtime dependencies.
- Go SDK
The official Go client for the FoPost API.
- Rust SDK
The official Rust client for the FoPost API, async and built on reqwest.