C# HttpClient: submit a Seedance 2.5 job and poll until completed

A .NET top-level-statements script that posts a seedance-2.5 job to Sume, polls every 30 seconds, and stops on completed, failed or cancelled.

4 min readSume
All posts

In C#, use one HttpClient with a Bearer header, POST JSON to https://api.sume.com/v1/videos, then poll the polling_url from the 202 response until the status is terminal. The script below uses only the .NET base library (6 or newer) and seedance-2.5, which takes 4 to 30 seconds at 480p, 720p or 1080p.

Seedance 2.5 on Sume bills about $0.27 per second at 480p (9:16), so the 8-second job below is roughly $2.15. A 4-second 480p clip is about $1.07.

The script

Save it as Program.cs in a console project with top-level statements. The key comes from SUME_API_KEY, and the loop sleeps 30 seconds between polls.

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

var key = Environment.GetEnvironmentVariable("SUME_API_KEY")
    ?? throw new InvalidOperationException("Set SUME_API_KEY");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);

var body = JsonSerializer.Serialize(new {
    model = "seedance-2.5",
    prompt = "A barista pours latte art, close-up, warm morning light",
    duration = 8,
    resolution = "480p",
    aspect_ratio = "9:16"
});
var res = await http.PostAsync("https://api.sume.com/v1/videos",
    new StringContent(body, Encoding.UTF8, "application/json"));
res.EnsureSuccessStatusCode();
using var job = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var pollingUrl = job.RootElement.GetProperty("polling_url").GetString()!;

while (true)
{
    await Task.Delay(TimeSpan.FromSeconds(30));
    using var st = JsonDocument.Parse(await http.GetStringAsync(pollingUrl));
    var status = st.RootElement.GetProperty("status").GetString();
    Console.WriteLine(status);
    if (status is "completed" or "failed" or "cancelled") break;
}

What to do on each outcome

After the loop, read unsigned_urls[0] on completed and error on failed (video generation docs). A failed job answers the content endpoint with 409 job_failed, which is not retryable, so do not loop on it.

seedance-2.5 on Sume, approximate prices, list x 1.25 (read 2026-10-07)
ClipResolutionBilled on Sume
4 seconds480pabout $1.07
4 seconds720pabout $2.31
10 seconds720pabout $5.78
30 seconds1080p$42.65

Production notes

Reuse one HttpClient for the life of the process. Send an Idempotency-Key header on the POST so a retried request returns the original job instead of a second charge, and set a deadline on the loop so a stuck job cannot hold a worker forever (jobs and results).

For a webhook instead of polling, pass an HTTPS callback_url in the body.

Practical notes

In ASP.NET, put the submit behind a controller action that returns the job id, and run the polling in a hosted service or a queue worker, not inside the request. Requests that wait for video time out; jobs that run in the background do not.

Log usage.cost from the completed poll response next to your own order id. It is the billed amount, so it is the number to reconcile against your invoice.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume