C# HttpClient POST JSON with a Bearer token

Set Authorization with AuthenticationHeaderValue("Bearer", key), send a JSON body, then read the status and body before EnsureSuccessStatusCode.

5 min readSume
All posts

To POST JSON with a Bearer token in C#, set request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token) (or the same on HttpClient.DefaultRequestHeaders), send the body as StringContent with the media type application/json or with PostAsJsonAsync, then read the status code and the body. Read the body before you call EnsureSuccessStatusCode(), because that call throws on any status outside 200-299 and the API's error message is in the body.

HttpClient facts come from Microsoft Learn: Make HTTP requests with HttpClient, PostAsJsonAsync, HttpClient.Timeout and AuthenticationHeaderValue. The example API is Sume's, from Authentication, Video Generation, Errors and rate limits and Jobs and results, all read on 2026-09-29. Sume's only documented client library is the TypeScript SDK, so from C# this is a plain HTTPS call.

How do I send a JSON POST with a Bearer token in HttpClient?

Build an HttpRequestMessage when a header changes per request, such as an Idempotency-Key. This example starts a Sume video job. The key comes from an environment variable on the server; Sume's docs say not to place API keys in frontend JavaScript or mobile apps.

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) }; // reuse one instance

var json = JsonSerializer.Serialize(new {
    model = "sume/auto",
    prompt = "A vertical product clip on a desk, natural light",
    aspect_ratio = "9:16",
    duration = 5
});
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.sume.com/v1/videos") {
    Content = new StringContent(json, Encoding.UTF8, "application/json")
};
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("SUME_API_KEY"));
request.Headers.Add("Idempotency-Key", "order-8823-clip-v1");

using var response = await http.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
    throw new InvalidOperationException($"{(int)response.StatusCode}: {body}");
// 202: body has id, polling_url and status "pending"

Should I use PostAsJsonAsync or StringContent?

Either works. PostAsJsonAsync is an extension method in the System.Net.Http.Json package that serializes a value as JSON into the request body, and ReadFromJsonAsync<T>() deserializes the reply. It takes a URL and a value, so per-request headers need HttpRequestMessage and SendAsync as above. When the token is the same for every call, set it once on DefaultRequestHeaders.Authorization and use PostAsJsonAsync.

Microsoft recommends reusing HttpClient instances for the application's lifetime, as a static field or through the HTTP client factory, rather than creating one per call.

What status codes should my C# code handle?

IsSuccessStatusCode is true for 200-299, which covers 202 Accepted. Sume's error bodies share one envelope, { "error": { "code", "message", "request_id", "details" } }, and the request id is safe to share with support. Log error.code and error.request_id rather than only the status.

From Video Generation, Authentication and Errors and rate limits, read 2026-09-29.
StatusCodeWhat it means for the C# caller
202—The job was accepted: store id and poll polling_url
400invalid_requestBody, query, path or headers are invalid; fix the call
401unauthorizedKey missing or invalid, or both Authorization and x-api-key were sent
402insufficient_creditsThe balance can't cover the generation
415unsupported_media_typeThe body wasn't application/json
429rate_limited or queue_fullBack off; use retry-after when present

Why does my HttpClient POST time out?

HttpClient.Timeout defaults to 100 seconds and applies to every request on that instance; a CancellationTokenSource can set a shorter one per request. On timeout, HttpClient throws a TaskCanceledException whose inner exception is a TimeoutException.

For a job API, don't raise the timeout to the length of the work. Sume's video create returns the job id at once, and generation runs asynchronously; you poll GET /v1/videos/{id} until the status is completed. A client-side timeout does not cancel a job: it keeps running and still bills. Polly retry for HttpClient POST covers retrying safely.

How do I stop a retry from paying twice?

Send an Idempotency-Key on every paid create and build it from the thing being made, such as an order id plus a version, not from Guid.NewGuid() per attempt. On /v1/videos, a replay with the key returns the original job. Sume's docs say not to retry unsafe submit requests without one. Idempotency keys for AI video APIs covers the replay answers, and curl bearer token shows the same call from a shell.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume