C#
Package: ValeryVerkhoturov.WbApiClient on NuGet.
- Target framework: .NET 8.0
- HTTP: RestSharp
- Namespace:
ValeryVerkhoturov.WbApiClient.<Category> - Secret wrapper: per-namespace
SecretString
Install
dotnet add package ValeryVerkhoturov.WbApiClientOr in your .csproj:
<PackageReference Include="ValeryVerkhoturov.WbApiClient" Version="1.20260926.0" />Namespace shape
The root is ValeryVerkhoturov.WbApiClient, and each WB category is a PascalCase sub-namespace. Inside one, the generator splits the code three ways:
using ValeryVerkhoturov.WbApiClient.Items.Api; // Api classes
using ValeryVerkhoturov.WbApiClient.Items.Client; // Configuration, SecretString, ApiException
using ValeryVerkhoturov.WbApiClient.Items.Model; // request and response modelsCategories are isolated by namespace, so identically named models from different sections coexist: Items.Model.Error and Finances.Model.Error are separate types.
Sub-namespaces
General, Items, OrdersFbs, OrdersDbw, Dbs, InStorePickup, OrdersFbw, Promotion, Communications, Rates, Analytics, Reports, Finances.
Authentication
using ValeryVerkhoturov.WbApiClient.General.Api;
using ValeryVerkhoturov.WbApiClient.General.Client;
var config = new Configuration();
config.SetAccessToken("<your WB JWT>");
var api = new GeneralApi(config);
var ping = api.GetPing();
Console.WriteLine(ping.Status);SetAccessToken stores the string in a SecretString internally. Configuration deliberately has no getter for the token: the generated Api classes read it through IReadableConfiguration when they build the Authorization header.
The token does not leak when printed: SecretString.ToString() always returns <REDACTED>, and string interpolation, Console.WriteLine and most logging frameworks all go through it.
var s = new SecretString("eyJhbGciOi...");
Console.WriteLine($"{s}"); // <REDACTED>
Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (on request)Async calls
Every method has an Async counterpart taking a CancellationToken:
var ping = await api.GetPingAsync(cancellationToken: token);Client settings
var config = new Configuration
{
Timeout = TimeSpan.FromSeconds(60), // 100 seconds by default
UserAgent = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/csharp",
};
config.SetAccessToken(token);
config.DefaultHeaders.Add("X-Request-Id", requestId);Every request sends ValeryVerkhoturov/wb-api-client/csharp by default.
Service address
Each WB category sits behind its own host, already baked into the client from the spec. To send requests elsewhere — a sandbox, say — set BasePath:
config.BasePath = "https://marketplace-api-sandbox.wildberries.ru";Error handling
A 4xx or 5xx response raises ApiException:
using ValeryVerkhoturov.WbApiClient.OrdersFbs.Api;
using ValeryVerkhoturov.WbApiClient.OrdersFbs.Client;
var fbsConfig = new Configuration();
fbsConfig.SetAccessToken("<your WB JWT>");
var fbs = new OrdersFbsApi(fbsConfig);
try
{
var orders = fbs.GetV3Orders(limit: 10, next: 0);
}
catch (ApiException e)
{
Console.WriteLine(e.ErrorCode); // HTTP status code
Console.WriteLine(e.Message);
Console.WriteLine(e.ErrorContent); // response body
}Configuration is per-namespace
The generator emits its own Configuration — and SecretString, and ApiException — inside every category, so OrdersFbs.Client.Configuration is not General.Client.Configuration. A single instance cannot be shared across categories: each Api class takes the Configuration from its own namespace. The token itself is the same one.
Dependency injection
Api clients are ordinary classes with a Configuration constructor, so they register as-is:
services.AddSingleton(_ =>
{
var config = new Configuration();
config.SetAccessToken(builder.Configuration["Wb:Token"]);
return config;
});
services.AddTransient<GeneralApi>();No DI-specific package is needed: the client is built on RestSharp rather than IHttpClientFactory.
See also
