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.

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.
| Status | Code | What it means for the C# caller |
|---|---|---|
202 | — | The job was accepted: store id and poll polling_url |
400 | invalid_request | Body, query, path or headers are invalid; fix the call |
401 | unauthorized | Key missing or invalid, or both Authorization and x-api-key were sent |
402 | insufficient_credits | The balance can't cover the generation |
415 | unsupported_media_type | The body wasn't application/json |
429 | rate_limited or queue_full | Back 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
- Authentication
- Video Generation
- Errors and rate limits
- Jobs and results
- Microsoft Learn: Make HTTP requests with the HttpClient class (read 2026-09-29)
- Microsoft Learn: HttpClientJsonExtensions.PostAsJsonAsync (read 2026-09-29)
- Microsoft Learn: HttpClient.Timeout (read 2026-09-29)
- Microsoft Learn: AuthenticationHeaderValue (read 2026-09-29)
Related posts
More in Integrations
- EventBridge API destinations: call a paid API without Lambda
An EventBridge API destination calls an HTTPS API straight from a rule, with a 5-second timeout and retries on 409, 429 and 5xx. Key every call.
- Golang HTTP POST JSON with headers and a Bearer token
http.Post can't set headers. Marshal the JSON, build it with http.NewRequest, set headers, send with a Client that has a Timeout, and close the body.
- Guzzle retry middleware: retry a paid POST without paying twice
Guzzle's Middleware::retry takes a decider and a delay in milliseconds. Retry only transient answers, honor Retry-After, and keep one idempotency key.
- HubSpot custom coded actions: call an outside API safely
A HubSpot custom code action runs Node.js or Python in a workflow for 20 seconds, with secrets as env vars. Start slow API jobs there; don't wait.
Written by Sume