# wb-api-client
> Автоматически сгенерированные клиенты Wildberries Seller API для Python, TypeScript, Go, Java, PHP, OneScript, C# и Rust. Одна версия на все восемь экосистем, bearer-токен маскируется по умолчанию.
Любая страница сайта доступна в Markdown по её адресу с суффиксом `.md` — например `guides/quickstart.md`. Это относится и к {apiPages} справочника API, которые ниже не перечислены поштучно: страница модуля перечисляет все свои операции.
---
title: "SDK для Wildberries Seller API"
description: "Автоматически сгенерированные SDK для Python, TypeScript, Go, Java, PHP, OneScript (1С), C# и Rust. Одна версия для всех экосистем. Bearer-токен маскируется по умолчанию."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/
---
# wb-api-client — Wildberries API на Вашем языке.
Автоматически сгенерированные SDK для Python, TypeScript, Go, Java, PHP, OneScript (1С), C# и Rust. Одна версия для всех экосистем. Bearer-токен маскируется по умолчанию.
- **13 категорий API, один пакет** — Все категории — товары, заказы (FBS / DBW / FBW / DBS), продвижение, аналитика, финансы и остальные — поставляются как изолированные под-модули внутри одного пакета на язык. Никаких коллизий имён между категориями.
- **Маскирование секретов из коробки** — Ваш bearer-токен обёрнут в языко-специфичный SecretString, поэтому он не попадает в логи, print, дампы отладчика и Sentry, пока вы явно не запросите значение.
- **Ежедневная синхронизация с апстримом** — CI-задача каждый день скачивает спецификации WB. Если что-то изменилось, автоматически публикуется новый релиз на PyPI, npm, Go, Maven Central, Packagist, hub.oscript.io, NuGet и crates.io — с соответствующим git-тегом.
- **Одна версия, восемь экосистем** — 1.YYYYMMDD.N — валидный стабильный semver в каждом языке. pip / npm / go get / mvn / composer / opm / dotnet add / cargo add подхватывают последнюю версию без прелиз-плясок.
- **Bearer-авторизация везде** — Апстрим-YAML путает apiKey-схему с bearer JWT. Пост-обработка убирает дублирование, и в каждом сгенерированном клиенте остаётся ровно один способ авторизации.
- **Детерминированная генерация** — Зафиксированные версии openapi-generator и форматтеров, скрытые метки времени. Проверка на PR перегенерирует всё с нуля и падает, если результат отличается.
wb-api-client — клиентские библиотеки для Wildberries Seller API: 13 категорий (товары, заказы FBS / DBW / FBW / DBS, продвижение, аналитика, финансы и остальные) на восьми языках, с единой версией и ежедневной пересборкой по официальным спецификациям WB.
## Установка
**Python**
```bash
pip install valeryverkhoturov-wb-api-client
```
**TypeScript**
```bash
npm install @valeryverkhoturov/wb-api-client
```
**Go**
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@latest
```
**Java (Maven)**
```xml
io.github.valeryverkhoturov
wb-api-client
LATEST
```
**PHP**
```bash
composer require valeryverkhoturov/wb-api-client
```
**OneScript**
```bash
opm install wb-api-client
```
**C#**
```bash
dotnet add package ValeryVerkhoturov.WbApiClient
```
**Rust**
```bash
cargo add wb-api-client
```
## Первый вызов
**Python**
```python
from wb_api_client.items import Configuration, ApiClient
from wb_api_client.items.api import ItemsApi
cfg = Configuration(access_token="<ваш JWT WB>")
api = ItemsApi(ApiClient(cfg))
```
**TypeScript**
```ts
import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items";
const cfg = new Configuration({});
cfg.setAccessToken("<ваш JWT WB>");
const api = new ItemsApi(cfg);
```
**Go**
```go
import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"
cfg := wbitems.NewConfiguration()
cfg.SetAccessToken("<ваш JWT WB>")
client := wbitems.NewAPIClient(cfg)
```
**Java**
```java
import io.github.valeryverkhoturov.wbapi.items.ApiClient;
import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi;
ApiClient client = new ApiClient();
client.setAccessToken("<ваш JWT WB>");
ItemsApi api = new ItemsApi(client);
```
**PHP**
```php
use ValeryVerkhoturov\WbApiClient\Items\Configuration;
use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi;
use GuzzleHttp\Client;
$config = (new Configuration())
->setAccessToken('<ваш JWT WB>');
$api = new ItemsApi(new Client(), $config);
```
**OneScript**
```bsl
#Использовать "wb-api-client"
Настройки = Новый Конфигурация();
Настройки.УстановитьТокен("<ваш JWT WB>");
Клиент = Новый ItemsApi(Настройки);
```
**C#**
```csharp
using ValeryVerkhoturov.WbApiClient.Items.Api;
using ValeryVerkhoturov.WbApiClient.Items.Client;
var config = new Configuration();
config.SetAccessToken("<ваш JWT WB>");
var api = new ItemsApi(config);
```
**Rust**
```rust
use wb_api_client::items::apis::{configuration::Configuration, items_api};
let mut cfg = Configuration::new();
cfg.set_access_token("<ваш JWT WB>");
```
Подробно по каждому языку и всем 13 под-модулям: [Python](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python) · [TypeScript](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/typescript) · [Go](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/go) · [Java](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/java) · [PHP](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/php) · [OneScript](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/onescript) · [C#](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/csharp) · [Rust](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/rust).
Для LLM-агентов: [llms.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/llms.txt) — указатель всех страниц сайта, [llms-full.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/llms-full.txt) — вся документация одним файлом.
---
title: "Быстрый старт"
description: "Пять минут: получить токен, установить клиент для своего языка, сделать первый вызов."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/quickstart
---
# Быстрый старт
Пять минут: получить токен, установить клиент для своего языка, сделать первый вызов.
## 1. Получите API-токен WB
Клиентские библиотеки — это только транспорт; вам всё равно нужен аккаунт продавца Wildberries и персональный API-токен.
1. Войдите на [seller.wildberries.ru](https://seller.wildberries.ru) и откройте раздел [Интеграции по API](https://seller.wildberries.ru/api-integrations).
2. Нажмите **+ Создать токен** и выберите вкладку **Для интеграции вручную**.
3. Выберите тип токена — для обычной работы подойдёт **персональный** или **базовый**, для проверки в песочнице — **тестовый**.
4. Заполните название, выберите категории методов и уровень доступа (**Чтение и запись** или **Только чтение**). Для первого теста достаточно категории **Контент** — её использует под-модуль `items`.
5. Для персонального токена отметьте чекбокс **Я понимаю, что не следует передавать токен третьим лицам** и нажмите **Создать**.
6. Нажмите **Скопировать и закрыть** — токен окажется в буфере обмена. После этого посмотреть его в кабинете уже не получится.
Выбирайте только те категории, с которыми планируете работать. Если нужно вызывать несколько категорий API (`items`, `orders_fbs`, `analytics`, …), создайте токен с объединением категорий — или по токену на каждую категорию, если хотите уменьшить радиус поражения при утечке.
## 2. Установка
Один пакет на язык, внутри которого 13 категорий API как отдельные под-модули.
**Python**
```bash
pip install valeryverkhoturov-wb-api-client
```
**TypeScript**
```bash
npm install @valeryverkhoturov/wb-api-client
```
**Go**
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@latest
```
**Java (Maven)**
```xml
io.github.valeryverkhoturov
wb-api-client
LATEST
```
**PHP**
```bash
composer require valeryverkhoturov/wb-api-client
```
**OneScript**
```bash
opm install wb-api-client
```
**C#**
```bash
dotnet add package ValeryVerkhoturov.WbApiClient
```
**Rust**
```bash
cargo add wb-api-client
```
`LATEST` для Java: посмотрите текущую версию `1.YYYYMMDD.N` на [странице релизов](https://github.com/ValeryVerkhoturov/wb-api-client/releases) и подставьте её.
## 3. Первый вызов — данные о продавце
В под-модуле `general` есть эндпоинт `getV1SellerInfo` — он дёшев и подтверждает, что и токен, и сетевой путь работают.
**Python**
```python
from wb_api_client.general import Configuration, ApiClient
from wb_api_client.general.api import GeneralApi
cfg = Configuration(access_token="<ваш JWT WB>")
api = GeneralApi(ApiClient(cfg))
info = api.get_v1_seller_info()
print(info)
```
**TypeScript**
```ts
import {
Configuration,
GeneralApi,
} from "@valeryverkhoturov/wb-api-client/general";
const cfg = new Configuration({});
cfg.setAccessToken("<ваш JWT WB>");
const api = new GeneralApi(cfg);
const info = await api.getV1SellerInfo();
console.log(info.data);
```
**Go**
```go
package main
import (
"context"
"fmt"
wbgeneral "github.com/ValeryVerkhoturov/wb-api-client-go/general"
)
func main() {
cfg := wbgeneral.NewConfiguration()
cfg.SetAccessToken("<ваш JWT WB>")
client := wbgeneral.NewAPIClient(cfg)
info, _, err := client.GeneralAPI.GetV1SellerInfo(context.Background()).Execute()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", info)
}
```
**Java**
```java
import io.github.valeryverkhoturov.wbapi.general.ApiClient;
import io.github.valeryverkhoturov.wbapi.general.api.GeneralApi;
ApiClient client = new ApiClient();
client.setAccessToken("<ваш JWT WB>");
GeneralApi api = new GeneralApi(client);
System.out.println(api.getV1SellerInfo());
```
**PHP**
```php
use ValeryVerkhoturov\WbApiClient\General\Configuration;
use ValeryVerkhoturov\WbApiClient\General\Api\GeneralApi;
use GuzzleHttp\Client;
$config = (new Configuration())
->setAccessToken('<ваш JWT WB>');
$api = new GeneralApi(new Client(), $config);
print_r($api->getV1SellerInfo());
```
**OneScript**
```bsl
#Использовать "wb-api-client"
Настройки = Новый Конфигурация();
Настройки.УстановитьТокен("<ваш JWT WB>");
Клиент = Новый GeneralApi(Настройки);
Сообщить(Клиент.GetV1SellerInfo().Тело);
```
**C#**
```csharp
using ValeryVerkhoturov.WbApiClient.General.Api;
using ValeryVerkhoturov.WbApiClient.General.Client;
var config = new Configuration();
config.SetAccessToken("<ваш JWT WB>");
var api = new GeneralApi(config);
Console.WriteLine(api.GetV1SellerInfo());
```
**Rust**
```rust
use wb_api_client::general::apis::{configuration::Configuration, general_api};
#[tokio::main]
async fn main() {
let mut cfg = Configuration::new();
cfg.set_access_token("<ваш JWT WB>");
let info = general_api::get_v1_seller_info(&cfg).await;
println!("{:#?}", info);
}
```
Если вернулись данные о вашем продавце — всё подключено.
## 4. Дальше
- **[Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)** — как устроен SecretString, зачем не выводить токен и как получить сырое значение, когда оно действительно нужно.
- **[Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)** — коды ответов WB, стратегия ретраев на 429/5xx и как ошибки выглядят в каждом языке.
- **[Страницы языков](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python)** — все под-модули, сниппеты установки и форма каждого класса `Api`.
## Что делать, если первый вызов не проходит
| Симптом | Вероятная причина | Что делать |
| ------------------------------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized` | Токен неверен / просрочен / без нужных категорий | Перевыпустите в кабинете продавца; убедитесь, что категории токена подходят под модуль (например, «Контент» для `items`) |
| `403 Forbidden` | Токен валиден, но категорий недостаточно | Добавьте токену нужную категорию или создайте новый |
| `429 Too Many Requests` | Упёрлись в лимит WB | Подождите `X-Ratelimit-Retry` секунд из ответа и откладывайте повторные запросы; SDK не ретраит сам |
| Зависает соединение | Корпоративный прокси / MITM TLS | Задайте переменную `HTTPS_PROXY`; большинство клиентов её учитывает |
| `ImportError` / `Cannot find module` | Установлено не то имя пакета | Правильное имя — `valeryverkhoturov-wb-api-client` / `@valeryverkhoturov/wb-api-client`. Пакет с «голым» именем `wb-api-client` — это другой проект |
---
title: "Аутентификация"
description: "Каждый вызов Wildberries Seller API требует bearer JWT в заголовке Authorization. Клиенты сами подставляют заголовок; вы передаёте только токен."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication
---
# Аутентификация
Каждый вызов Wildberries Seller API требует **bearer JWT** в заголовке `Authorization`. Клиенты сами подставляют заголовок; вы передаёте только токен.
## Откуда берётся токен
Токены выпускаются в кабинете продавца:
1. [seller.wildberries.ru](https://seller.wildberries.ru) → раздел [Интеграции по API](https://seller.wildberries.ru/api-integrations).
2. **+ Создать токен** → вкладка **Для интеграции вручную** → выберите тип токена: **персональный**, **базовый** или **тестовый** для песочницы.
3. Заполните название, выберите категории методов (Контент, Маркетплейс, Аналитика, …) и уровень доступа — **Чтение и запись** или **Только чтение**.
4. **Создать** → **Скопировать и закрыть**. Токен показывают один раз — сохраните его сразу.
Каждой выбранной категории примерно соответствует один или несколько под-модулей в клиенте. Если пришёл `403` — токен валиден, но прав не хватает; перевыпустите с нужной категорией.
Какой тип токена выбрать:
- **Персональный** — для собственных программ и on-premise систем (включая локальные версии 1С). Даёт доступ к чувствительным данным, поэтому его нельзя передавать третьим лицам и использовать в облачных сервисах.
- **Базовый** — доступ к ограниченному набору данных. Подходит для проверки интеграции на реальных данных и для случаев, когда персональный использовать нельзя.
- **Тестовый** — работает только с [песочницей WB API](https://dev.wildberries.ru/openapi/sandbox) и сгенерированными тестовыми данными; доступа к реальным данным продавца нет.
- **Сервисный** — создаётся для конкретного облачного сервиса из каталога [«Решения для бизнеса»](https://seller.wildberries.ru/auth-services); категории и уровни доступа заполняются автоматически.
Любой токен живёт 180 дней с момента создания, после чего нужно выпустить новый. Проверить валидность токена и его категории можно, декодировав JWT — это строка по [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519). Не вставляйте токен в онлайн-декодеры: так его может перехватить кто угодно.
## Обёртки SecretString
Получив токен, не носите его по коду как обычную строку. Каждый клиент оборачивает значение в языко-специфичный «секретный» тип — он маскирует токен в логах, `print`-выводе, отладчике и дампах ошибок, пока вы явно не запросите значение.
| Язык | Обёртка | Сеттер | Маскируется в | Явное значение |
| ---------- | -------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------- | --------------------------- |
| Python | [`pydantic.SecretStr`](https://docs.pydantic.dev/latest/api/types/#pydantic.types.SecretStr) | `Configuration(access_token=…)` | `print(cfg.access_token)` → `**********` | `.get_secret_value()` |
| TypeScript | встроенный класс `SecretString` | `Configuration.setAccessToken(…)` | `console.log(new SecretString("…"))` → `` | `.exposeSecret()` |
| Go | [`secrecy.SecretString`](https://github.com/negrel/secrecy) | `Configuration.SetAccessToken(…)` | `fmt.Printf("%v", cfg.AccessToken)` → `` | `.ExposeSecret()` |
| Java | `SecretString` в каждом под-пакете | `ApiClient.setAccessToken(String)` | `System.out.println(s)` → `` | `.exposeSecret()` |
| PHP | `SecretString` в каждом под-пространстве | `Configuration::setAccessToken(string)` | `var_dump($config)` → `''` | `$config->getAccessToken()` |
| C# | `SecretString` в каждом пространстве имён | `Configuration.SetAccessToken(…)` | `Console.WriteLine(new SecretString("…"))` → `` | `.ExposeSecret()` |
| OneScript | класс `СекретнаяСтрока` | `Конфигурация.УстановитьТокен(…)` | `Сообщить(Настройки.Токен())` → `СекретнаяСтрока` | `.Раскрыть()` |
| Rust | `SecretString` в каждом модуле | `Configuration::set_access_token(…)` | `println!("{s:?}")` / `{}` → `` | `.expose_secret()` |
### Зачем это нужно
Реальные ситуации, ради которых это сделано:
- Логгер-middleware, который в трейсе ошибки дампит всю конфигурацию запроса.
- `print(config)`, оставленный в коде после отладки.
- Исключения, сериализованные в Sentry / трекер вместе с полным контекстом HTTP-клиента.
- Отладчик IDE, показывающий поля объекта на демо-встрече по шаринг-скрину.
Обёртка превращает каждый из этих сценариев из утечки токена в безобидную метку ``. Цена — одна строка кода в точке ввода.
### Как это выглядит на практике
**Python**
```python
from wb_api_client.items import Configuration
cfg = Configuration(access_token="eyJhbGciOi...")
print(cfg.access_token) # SecretStr('**********')
print(cfg.access_token.get_secret_value()) # eyJhbGciOi... (по запросу)
```
**TypeScript**
```ts
import { Configuration } from "@valeryverkhoturov/wb-api-client/items";
const cfg = new Configuration({});
cfg.setAccessToken("eyJhbGciOi...");
console.log(cfg.accessToken); // SecretString { }
console.log(cfg.accessToken.exposeSecret()); // eyJhbGciOi... (по запросу)
```
**Go**
```go
import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"
cfg := wbitems.NewConfiguration()
cfg.SetAccessToken("eyJhbGciOi...")
fmt.Printf("%v\n", cfg.AccessToken) //
fmt.Println(cfg.AccessToken.ExposeSecret()) // eyJhbGciOi... (по запросу)
```
**Java**
```java
import io.github.valeryverkhoturov.wbapi.items.SecretString;
SecretString s = new SecretString("eyJhbGciOi...");
System.out.println(s); //
System.out.println(s.exposeSecret()); // eyJhbGciOi... (по запросу)
```
**PHP**
```php
use ValeryVerkhoturov\WbApiClient\Items\SecretString;
$s = new SecretString('eyJhbGciOi...');
var_dump($s); // object(...) { ['value']=> '' }
echo $s->exposeSecret(); // eyJhbGciOi... (по запросу)
```
**OneScript**
```bsl
С = Новый СекретнаяСтрока("eyJhbGciOi...");
Сообщить(С); // СекретнаяСтрока
Сообщить(С.Раскрыть()); // eyJhbGciOi... (по запросу)
```
**C#**
```csharp
var s = new SecretString("eyJhbGciOi...");
Console.WriteLine($"{s}"); //
Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (по запросу)
```
**Rust**
```rust
use wb_api_client::items::apis::configuration::SecretString;
let s = SecretString::new("eyJhbGciOi...");
println!("{s:?}"); //
println!("{}", s.expose_secret()); // eyJhbGciOi... (по запросу)
```
## Один токен на несколько под-модулей
Если в процессе вы используете сразу несколько под-модулей, у каждого — свой `Configuration` / `ApiClient`. Так задумано: openapi-generator создаёт полностью изолированные SDK на каждую спецификацию. Переиспользуйте строку токена, но конструируйте конфиг на каждый модуль.
**Python**
```python
from wb_api_client.items import Configuration as ItemsCfg, ApiClient as ItemsClient
from wb_api_client.orders_fbs import Configuration as OrdCfg, ApiClient as OrdClient
TOKEN = "<ваш JWT WB>"
items_api = ItemsClient(ItemsCfg(access_token=TOKEN))
orders_api = OrdClient(OrdCfg(access_token=TOKEN))
```
**TypeScript**
```ts
import { Configuration as ItemsCfg } from "@valeryverkhoturov/wb-api-client/items";
import { Configuration as OrdCfg } from "@valeryverkhoturov/wb-api-client/orders_fbs";
const TOKEN = "<ваш JWT WB>";
const itemsCfg = new ItemsCfg({}); itemsCfg.setAccessToken(TOKEN);
const ordCfg = new OrdCfg({}); ordCfg.setAccessToken(TOKEN);
```
## Ротация токенов
Токены WB не ротируются сами и живут максимум 180 дней — их нужно заменять вручную. Хорошая практика:
1. Держите токен в секрет-менеджере (SSM, Vault, GitHub Actions secret) — не в исходниках.
2. Читайте в переменную окружения при старте процесса и конструируйте конфиг один раз.
3. На ротации: убейте процесс (или пул соединений) и запустите заново с новым токеном. Клиент кэширует токен на уровне экземпляра Configuration.
## Типичные ошибки авторизации
| Ответ | Значение |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| `401 Unauthorized` — `token expired` | Срок токена истёк; выпустите новый |
| `401 Unauthorized` — `invalid signature` | Неверный формат заголовка Authorization или токен от другой среды (sandbox / prod) |
| `403 Forbidden` — `no access to resource` | Токен валиден, прав не хватает — перевыпустите с правильной галочкой |
| `429 Too Many Requests` | Rate-limit; сам токен в порядке |
Ретраи и работа с ошибками — в [руководстве по обработке ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling).
---
title: "Обработка ошибок"
description: "WB возвращает стандартные HTTP-коды и JSON в теле. Клиенты превращают их в языко-нативные исключения — сырой Response вы получите только если явно об этом…"
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling
---
# Обработка ошибок
WB возвращает стандартные HTTP-коды и JSON в теле. Клиенты превращают их в языко-нативные исключения — сырой `Response` вы получите только если явно об этом попросите.
## Коды, за которыми стоит следить
| Код | Значение | Правильная реакция |
| ----------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`–`204` | Успех | Разобрать тело (или ничего не делать при 204) |
| `400` | Некорректный запрос | Починить запрос; ретрай бесполезен |
| `401` | Нет токена / плохой / просрочен | Обновить токен; НЕ ретраить с тем же |
| `402` | У сервиса недостаточно средств на балансе | Возвращается только сервисам из каталога «Решения для бизнеса» — пополнить баланс |
| `403` | Токену не хватает прав на эндпоинт | Проверить: категория токена подходит методу, метод разрешён типу токена, токен не создан удалённым пользователем. Перевыпустить при необходимости |
| `404` | Ресурса нет | Проверить URL; бизнес-логика — не всегда ошибка. Полезное — в поле `detail` тела |
| `409` | Конфликт (напр. поставка уже доставлена) | Бизнес-логика; у WB своя схема на каждый эндпоинт |
| `413` | Тело запроса больше лимита | Уменьшить число объектов в запросе |
| `422` | Ошибка обработки параметров | Проверить данные запроса — они не должны противоречить друг другу |
| `429` | Rate-limit | Подождать `X-Ratelimit-Retry` секунд из ответа, повторить |
| `498` | Anti-bot WBAAS | С этими клиентами не должно возникать (корректный UA); если увидели — что-то проксирует/переписывает запрос |
| `5xx` | Проблема на стороне WB | Ретрай с экспоненциальной задержкой, лимит по попыткам |
## Паттерн ретраев
Клиенты сами не ретраят — это осознанное решение (политика ретраев — дело приложения). Минимальный хендлер для ретраибельных ошибок:
**Python**
```python
import time
from wb_api_client.items.exceptions import ApiException
def with_retry(fn, *, max_attempts=5, base=1.0):
for attempt in range(1, max_attempts + 1):
try:
return fn()
except ApiException as e:
if e.status not in (429, 500, 502, 503, 504) or attempt == max_attempts:
raise
retry_after = float(
e.headers.get("X-Ratelimit-Retry") or e.headers.get("Retry-After") or 0
) or base * 2 ** attempt
time.sleep(retry_after)
```
**TypeScript**
```ts
import { AxiosError } from "axios";
async function withRetry(
fn: () => Promise,
{ maxAttempts = 5, base = 1000 } = {},
): Promise {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
const e = err as AxiosError;
const status = e.response?.status ?? 0;
if (![429, 500, 502, 503, 504].includes(status) || attempt === maxAttempts) throw err;
const retryAfterHeader =
e.response?.headers?.["x-ratelimit-retry"] ??
e.response?.headers?.["retry-after"];
const delay = retryAfterHeader
? Number(retryAfterHeader) * 1000
: base * 2 ** attempt;
await new Promise((r) => setTimeout(r, delay));
}
}
throw new Error("unreachable");
}
```
**Go**
```go
import (
"context"
"errors"
"net/http"
"time"
)
func withRetry[T any](ctx context.Context, fn func() (T, *http.Response, error)) (T, error) {
var zero T
for attempt := 1; attempt <= 5; attempt++ {
v, resp, err := fn()
if err == nil {
return v, nil
}
status := 0
if resp != nil { status = resp.StatusCode }
if status != 429 && (status < 500 || status > 504) || attempt == 5 {
return zero, err
}
delay := time.Duration(1<
---
title: "Свои заголовки"
description: "Иногда к каждому запросу нужно добавить заголовок, которого нет в спецификации WB: креденшелы шлюза или корпоративного прокси, стоящего перед API,…"
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/custom-headers
---
# Свои заголовки
Иногда к каждому запросу нужно добавить заголовок, которого нет в спецификации WB: креденшелы шлюза или корпоративного прокси, стоящего перед API, идентификатор трассировки, метка окружения. Каждый клиент умеет добавлять такие заголовки — механизм свой в каждом языке, и ниже разобраны все восемь.
## Что сюда не относится
Две вещи стоит отделить сразу, иначе легко добавить заголовок, который клиент и так проставляет.
**Токен WB.** Заголовок `Authorization` клиент формирует сам из токена — см. [Аутентификацию](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication). Не собирайте его руками через механизмы этой страницы: в части языков (Go) это приведёт к двум заголовкам `Authorization` в одном запросе.
**Заголовки, описанные в спецификации.** Если заголовок объявлен в спецификации операции, генератор уже превратил его в обычный аргумент метода. В WB такие есть: `X-Nm-Id` и `X-Photo-Number` у `postV3MediaFile` в под-модуле `items`. Передавайте их аргументами, а не как «свои».
Остаётся всё остальное — заголовки, о которых спецификация не знает. Их и добавляем.
## Заголовки на весь клиент
Самый частый случай: заголовок нужен всем запросам. Задаётся один раз при сборке клиента.
**Python**
```python
from wb_api_client.items import Configuration, ApiClient
from wb_api_client.items.api import ItemsApi
client = ApiClient(Configuration(access_token="<ваш JWT WB>"))
client.set_default_header("X-Gateway-Token", "<креденшелы шлюза>")
client.set_default_header("X-Request-Source", "erp-sync")
api = ItemsApi(client)
```
**TypeScript**
```ts
import {
Configuration,
ItemsApi,
} from "@valeryverkhoturov/wb-api-client/items";
const cfg = new Configuration({
baseOptions: {
headers: {
"X-Gateway-Token": "<креденшелы шлюза>",
"X-Request-Source": "erp-sync",
},
},
});
cfg.setAccessToken("<ваш JWT WB>");
const api = new ItemsApi(cfg);
```
**Go**
```go
import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"
cfg := wbitems.NewConfiguration()
cfg.SetAccessToken("<ваш JWT WB>")
cfg.AddDefaultHeader("X-Gateway-Token", "<креденшелы шлюза>")
cfg.AddDefaultHeader("X-Request-Source", "erp-sync")
client := wbitems.NewAPIClient(cfg)
```
**Java**
```java
import io.github.valeryverkhoturov.wbapi.items.ApiClient;
import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi;
ApiClient client = new ApiClient();
client.setAccessToken("<ваш JWT WB>");
client.addDefaultHeader("X-Gateway-Token", "<креденшелы шлюза>");
client.addDefaultHeader("X-Request-Source", "erp-sync");
ItemsApi api = new ItemsApi(client);
```
**PHP**
```php
use ValeryVerkhoturov\WbApiClient\Items\Configuration;
use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi;
use GuzzleHttp\Client;
$config = (new Configuration())
->setAccessToken('<ваш JWT WB>');
// У Configuration в PHP нет реестра заголовков — их держит Guzzle-клиент.
$http = new Client([
'headers' => [
'X-Gateway-Token' => '<креденшелы шлюза>',
'X-Request-Source' => 'erp-sync',
],
]);
$api = new ItemsApi($http, $config);
```
**OneScript**
```bsl
#Использовать "wb-api-client"
Настройки = Новый Конфигурация();
Настройки.УстановитьТокен("<ваш JWT WB>");
Настройки.ДополнительныеЗаголовки.Вставить("X-Gateway-Token", "<креденшелы шлюза>");
Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Source", "erp-sync");
Клиент = Новый ItemsApi(Настройки);
```
**C#**
```csharp
using ValeryVerkhoturov.WbApiClient.Items.Api;
using ValeryVerkhoturov.WbApiClient.Items.Client;
var config = new Configuration();
config.SetAccessToken("<ваш JWT WB>");
config.DefaultHeaders["X-Gateway-Token"] = "<креденшелы шлюза>";
config.DefaultHeaders["X-Request-Source"] = "erp-sync";
var api = new ItemsApi(config);
```
**Rust**
```rust
use wb_api_client::items::apis::configuration::Configuration;
// У Configuration в Rust нет реестра заголовков — их держит reqwest-клиент.
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("X-Gateway-Token", "<креденшелы шлюза>".parse().unwrap());
headers.insert("X-Request-Source", "erp-sync".parse().unwrap());
let mut cfg = Configuration::new();
cfg.set_access_token("<ваш JWT WB>");
cfg.client = reqwest::Client::builder()
.default_headers(headers)
.build()
.unwrap();
```
Заголовки живут на экземпляре `Configuration` / `ApiClient` — а он свой у каждого под-модуля. Используете `items` и `orders_fbs` — задайте заголовки обоим; подробнее про это в разделе [«Один токен на несколько под-модулей»](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication#один-токен-на-несколько-под-модулеи).
## Заголовки на один запрос
Отдельный заголовок для одного вызова умеют не все клиенты: это зависит от того, что сгенерировал openapi-generator для конкретного языка.
| Язык | Поддержка на уровне вызова |
| ---------- | ------------------------------------------------------ |
| Python | да — именованный параметр `_headers=` у каждого метода |
| TypeScript | да — последний аргумент `RawAxiosRequestConfig` |
| Go | нет — методы не принимают заголовки |
| Java | нет |
| PHP | нет |
| C# | нет |
| OneScript | нет — `Заголовки` собираются внутри метода |
| Rust | нет — методы принимают только `&Configuration` |
**Python**
```python
# Внимание: заголовки по умолчанию перекрывают _headers при совпадении имени.
tags = api.get_v2_tags(_headers={"X-Correlation-Id": "9f1c-…"})
```
**TypeScript**
```ts
// Здесь наоборот: заголовок вызова перекрывает и baseOptions, и Authorization.
const tags = await api.getV2Tags({
headers: { "X-Correlation-Id": "9f1c-…" },
});
```
В остальных шести языках, если заголовок нужен только части запросов, соберите второй экземпляр клиента со своим набором заголовков и ходите через него.
## Что чем перекрывается
Порядок слияния у каждого генератора свой, и он не везде интуитивен. Проверено по сгенерированному коду:
| Язык | Порядок (побеждает правый) | Особенность |
| ---------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Python | `_headers` → заголовки по умолчанию | **Умолчания побеждают**: `default_headers` применяются поверх `_headers` |
| TypeScript | авторизация → `baseOptions.headers` → заголовки вызова | заголовком конфигурации можно перебить и `Authorization` |
| Go | — | используется `Header.Add`, то есть **добавление, а не замена**: дубль имени даст два заголовка |
| Java | `defaultHeaderMap` поверх заголовков операции | замена по имени |
| PHP | заголовки запроса → дефолты Guzzle | клиентские дефолты Guzzle применяются, только если заголовка ещё нет |
| C# | `DefaultHeaders` → заголовки операции | замена по имени |
| OneScript | `Accept`/`User-Agent`/`Authorization` → `ДополнительныеЗаголовки` → заголовки операции | `Соответствие.Вставить` заменяет значение |
| Rust | `default_headers` клиента → заголовки операции | заголовок, который ставит операция, перекрывает дефолт reqwest-клиента |
Практический вывод один: не используйте механизмы этой страницы, чтобы переопределить заголовок, который клиент уже ставит сам. Для `User-Agent` есть штатная настройка в каждом языке, для `Authorization` — сеттер токена.
## Секреты в заголовках
`SecretString` оборачивает **только** токен WB. Креденшелы шлюза или партнёрского сервиса — обычные строки, и клиент их не маскирует: в логе HTTP-клиента или в дампе конфигурации они будут видны целиком.
Поэтому к ним применимы те же правила, что и к токену WB:
1. Держите значение в секрет-менеджере (Vault, SSM, GitHub Actions secret), а не в исходниках.
2. Читайте в переменную окружения на старте процесса и собирайте клиент один раз.
3. Проверьте, что ваш логгер запросов не пишет заголовки целиком — либо внесите свои имена заголовков в список маскируемых.
Если вы логируете запросы через middleware, добавьте имена этих заголовков в тот же фильтр, где у вас уже лежит `Authorization`.
---
title: "Python"
description: "Python-клиент Wildberries Seller API: пакет valeryverkhoturov-wb-api-client с PyPI, 13 категорий API, маскирование токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python
---
# Python
Пакет: [`valeryverkhoturov-wb-api-client`](https://pypi.org/project/valeryverkhoturov-wb-api-client/) на PyPI.
- **Python:** 3.9+
- **HTTP:** `urllib3`
- **Модели:** `pydantic` v2
- **Обёртка секретов:** `pydantic.SecretStr`
## Установка
```bash
pip install valeryverkhoturov-wb-api-client
```
Для проектов на Poetry / uv / PDM добавляется как обычная зависимость:
**pyproject.toml**
```toml
[project]
dependencies = [
"valeryverkhoturov-wb-api-client",
]
```
**uv**
```bash
uv add valeryverkhoturov-wb-api-client
```
**poetry**
```bash
poetry add valeryverkhoturov-wb-api-client
```
## Форма импортов
Дистрибутив ставит один пакет `wb_api_client` с 13 под-модулями — по одному на категорию WB API. Каждый под-модуль — самостоятельный SDK со своими `Configuration`, `ApiClient` и ровно одним API-классом, названным по модулю (`GeneralApi`, `ItemsApi`, `OrdersFbsApi` и т.д.).
```python
from wb_api_client. import Configuration, ApiClient
from wb_api_client..api import Api
```
## Под-модули
| Слаг | Категория |
| ----------------- | -------------------------------------------------------- |
| `general` | Общее — ping, инфо о продавце, управление пользователями |
| `items` | Работа с товарами (Контент) |
| `orders_fbs` | Заказы FBS |
| `orders_dbw` | Заказы DBW |
| `dbs` | DBS |
| `in_store_pickup` | Самовывоз |
| `orders_fbw` | Поставки FBW |
| `promotion` | Маркетинг и продвижение |
| `communications` | Общение с покупателями |
| `rates` | Тарифы |
| `analytics` | Аналитика и данные |
| `reports` | Отчёты |
| `finances` | Документы и бухгалтерия |
В каждом под-модуле ровно один класс API, названный по модулю: `general` → `GeneralApi`, `items` → `ItemsApi`, `orders_fbs` → `OrdersFbsApi` и так далее. Полный список — в README, который перегенерируется каждый релиз: [clients/python/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md).
## Авторизация
```python
from wb_api_client.items import Configuration, ApiClient
from wb_api_client.items.api import ItemsApi
cfg = Configuration(access_token="<ваш JWT WB>") # оборачивается в SecretStr
client = ApiClient(cfg)
api = ItemsApi(client)
```
Обычная строка тоже подойдёт — она автоматически оборачивается. Чтобы прочитать сырое значение (например, для диагностики):
```python
cfg.access_token # SecretStr(**********)
cfg.access_token.get_secret_value() # "<ваш JWT WB>"
```
## Async / sync
Сгенерированный клиент — **синхронный** (urllib3). Если нужна асинхронность — запускайте вызовы в тред-пуле:
```python
import asyncio
result = await asyncio.to_thread(api.get_v1_seller_info)
```
Нативно-асинхронный вариант в планах нет — asynс-python-шаблон openapi-generator отстаёт от синхронного.
## Типизация
Каждая модель — pydantic v2. IDE и статические анализаторы видят полные типы:
```python
from wb_api_client.items.models import Product # pydantic-модель, dataclass-like
p: Product = Product.model_validate(some_dict)
```
## Тестирование против клиента
У `Configuration` есть параметр `host` — можно направить на мок-сервер:
```python
cfg = Configuration(
access_token="test-token",
host="http://localhost:8080",
)
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md)
---
title: "TypeScript"
description: "TypeScript-клиент Wildberries Seller API: пакет @valeryverkhoturov/wb-api-client с npm, типы .d.ts, axios, 13 категорий API."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/typescript
---
# TypeScript
Пакет: [`@valeryverkhoturov/wb-api-client`](https://www.npmjs.com/package/@valeryverkhoturov/wb-api-client) на npm.
- **Node:** 18+
- **HTTP:** `axios`
- **Типы:** first-class TypeScript, поставляются `.d.ts`
- **Обёртка секретов:** встроенный класс `SecretString`
## Установка
**npm**
```bash
npm install @valeryverkhoturov/wb-api-client
```
**pnpm**
```bash
pnpm add @valeryverkhoturov/wb-api-client
```
**yarn**
```bash
yarn add @valeryverkhoturov/wb-api-client
```
**bun**
```bash
bun add @valeryverkhoturov/wb-api-client
```
## Форма импортов
Каждый под-модуль — отдельный subpath-экспорт. Импортируйте из `@valeryverkhoturov/wb-api-client/`:
```ts
import {
Configuration,
ItemsApi,
} from "@valeryverkhoturov/wb-api-client/items";
```
Импорт с корня `@valeryverkhoturov/wb-api-client` не поддерживается — barrel-экспорта нет специально, чтобы не тянуть в бандл все 13 SDK, когда нужен один.
## Под-модули
По под-модулю на категорию: `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`.
Полный список классов `Api` на каждый модуль — в per-release README: [clients/typescript/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md).
## Авторизация
```ts
import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items";
const cfg = new Configuration({});
cfg.setAccessToken("<ваш JWT WB>");
const api = new ItemsApi(cfg);
const res = await api.someEndpoint();
console.log(res.data);
```
`setAccessToken` внутри оборачивает значение в `SecretString`. Прямое присваивание (`cfg.accessToken = "..."`) не поддерживается — всегда используйте сеттер, чтобы гарантировать маскирование.
Получить сырое значение обратно (например, для диагностического лога):
```ts
cfg.accessToken.exposeSecret();
```
## User-Agent
Клиент отправляет `ValeryVerkhoturov/wb-api-client/typescript` в каждом запросе — WB использует это как грубый сигнал маршрутизации. Чтобы добавить свой идентификатор приложения, не убирая базовый:
```ts
const cfg = new Configuration({});
cfg.setAccessToken(TOKEN);
cfg.baseOptions = {
...cfg.baseOptions,
headers: {
...cfg.baseOptions.headers,
"User-Agent":
"MyApp/1.2.3 (+https://myapp.example) ValeryVerkhoturov/wb-api-client/typescript",
},
};
```
## ESM / CJS
Пакет публикует dual entry points — `import` из ESM-кода, `require` из CommonJS. В `tsconfig.json` `"module": "NodeNext"` или `"module": "bundler"` выбирают правильный автоматически.
## Поддержка бандлеров
Subpath-экспорты описаны в `package.json` через `exports`. Современные бандлеры (Vite, Rollup, esbuild, Webpack 5+, Turbopack) их уважают из коробки. Webpack 4 — нет; обновляйте или пропишите алиас руками.
## Тестирование
Направьте на мок-сервер через `basePath`:
```ts
const cfg = new Configuration({ basePath: "http://localhost:8080" });
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md)
---
title: "Go"
description: "Go-клиент Wildberries Seller API: модуль github.com/ValeryVerkhoturov/wb-api-client-go, 13 категорий API, маскирование bearer-токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/go
---
# Go
Модуль: `github.com/ValeryVerkhoturov/wb-api-client-go` — резолвится из git-тегов [отдельного репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-go).
- **Go:** 1.22+
- **HTTP:** `net/http` (стандартная библиотека)
- **Обёртка секретов:** [`github.com/negrel/secrecy`](https://github.com/negrel/secrecy)
## Установка
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@latest
```
Зафиксировать конкретную версию по тегу:
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0
```
`proxy.golang.org` берёт код прямо из пушнутого тега — отдельный реестр не нужен.
## Почему модуль живёт в отдельном репозитории
В Go принято «один модуль на репозиторий», поэтому клиент лежит в [wb-api-client-go](https://github.com/ValeryVerkhoturov/wb-api-client-go) с `go.mod` в корне. Релизные теги (`vX.Y.Z`) пушатся в этот репозиторий тем же пайплайном, что и в основной — Go подхватывает нужный тег.
Поскольку `MAJOR = 1` зафиксирован (см. [версионирование](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/versioning)), суффикса `/v2` не будет ни сейчас, ни в будущем.
## Форма импортов
По одному Go-пакету на категорию WB API, у каждого свои `Configuration`, `APIClient` и типы `*API`.
```go
import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"
```
Давайте каждому под-модулю алиас (`wbitems`, `wbfbs`, `wbanalytics`, …) — иначе имена конфликтуют: во всех модулях экспортируются одни и те же идентификаторы.
## Под-модули
`general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`.
Структуры `*API` и методы эндпоинтов на модуль перечислены в per-release README: [wb-api-client-go/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme).
## Авторизация
```go
package main
import (
"context"
"fmt"
wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"
)
func main() {
cfg := wbitems.NewConfiguration()
cfg.SetAccessToken("<ваш JWT WB>") // оборачивается в *secrecy.SecretString
client := wbitems.NewAPIClient(cfg)
v, _, err := client.DefaultAPI.SomeEndpoint(context.Background()).Execute()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", v)
}
```
`SetAccessToken` — единственный путь: стандартный для openapi-generator канал `ContextAccessToken` через `context.Value` вырезан, случайно обойти обёртку не получится.
Получить сырое значение обратно:
```go
cfg.AccessToken.ExposeSecret()
```
## Контексты
Каждый вызов принимает `context.Context`. Прокидывайте дедлайн и отмену из своего приложения — клиент их уважает:
```go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
v, _, err := client.DefaultAPI.SomeEndpoint(ctx).Execute()
```
## Свой HTTP-клиент
Подставьте `*http.Client` для прокси, кастомных таймаутов или транспорт-обёртки:
```go
cfg := wbitems.NewConfiguration()
cfg.HTTPClient = &http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
```
## Тестирование
Переопределите хост при создании Configuration:
```go
cfg := wbitems.NewConfiguration()
cfg.Host = "localhost:8080"
cfg.Scheme = "http"
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme)
---
title: "Java"
description: "Java-клиент Wildberries Seller API: координаты io.github.valeryverkhoturov:wb-api-client с Maven Central, 13 категорий API, маскирование токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/java
---
# Java
Координаты: `io.github.valeryverkhoturov:wb-api-client` на [Maven Central](https://central.sonatype.com/artifact/io.github.valeryverkhoturov/wb-api-client).
- **Java:** 8+ (компилируется с `-source 1.8 -target 1.8`)
- **HTTP:** OkHttp 4
- **JSON:** Gson + gson-fire
- **Обёртка секретов:** `SecretString` в каждом под-пакете
## Установка
**Maven**
```xml
io.github.valeryverkhoturov
wb-api-client
LATEST
```
**Gradle (Kotlin)**
```kts
dependencies {
implementation("io.github.valeryverkhoturov:wb-api-client:LATEST")
}
```
**Gradle (Groovy)**
```groovy
dependencies {
implementation 'io.github.valeryverkhoturov:wb-api-client:LATEST'
}
```
**sbt**
```scala
libraryDependencies += "io.github.valeryverkhoturov" % "wb-api-client" % "LATEST"
```
Замените `LATEST` на актуальную версию `1.YYYYMMDD.N` со [страницы релизов](https://github.com/ValeryVerkhoturov/wb-api-client/releases).
## Раскладка пакетов
Базовый пакет: `io.github.valeryverkhoturov.wbapi`. Каждая категория WB — свой под-пакет со своим `ApiClient`, моделями и классами `api`.
```java
import io.github.valeryverkhoturov.wbapi.items.ApiClient;
import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi;
```
## Под-пакеты
`general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`.
Полный список API-классов на модуль: [clients/java/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md).
## Авторизация
```java
import io.github.valeryverkhoturov.wbapi.items.ApiClient;
import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi;
ApiClient client = new ApiClient();
client.setAccessToken("<ваш JWT WB>");
ItemsApi api = new ItemsApi(client);
```
`setAccessToken` принимает обычную `String` и оборачивает её в `SecretString` внутри клиента — конструировать обёртку руками не нужно, а в логах и дампах токен остаётся замаскированным.
## Один `SecretString` на под-пакет — почему
Обёртка генерируется в каждый под-пакет, а не расшарена на всю библиотеку, потому что openapi-generator создаёт полностью изолированные SDK на каждую спецификацию — общего «core»-модуля нет. Объединение потребовало бы дополнительного прохода генерации с межмодульными зависимостями. Цена — один класс на под-пакет; плюс в том, что клиент остаётся чистым выхлопом openapi-generator с минимальными точечными правками.
На практике это незаметно: `setAccessToken` оборачивает строку в `SecretString` своего под-пакета сам, поэтому импортировать и конструировать обёртку не требуется.
## Свой OkHttp-клиент
Подставьте свой экземпляр для прокси, интерсепторов или кастомных таймаутов:
```java
import okhttp3.OkHttpClient;
import java.time.Duration;
OkHttpClient http = new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(120))
.build();
ApiClient client = new ApiClient(http);
client.setAccessToken(TOKEN);
```
## Java 8 vs новее
Генератор выдаёт Java-8-совместимый код с `threetenbp` для дат и времени. На Java 11+ это по-прежнему работает — «современную» зависимость дополнительно ставить не нужно. Если хочется работать с `java.time` напрямую, модели предоставляют доступ к исходным строковым значениям через методы `set*ToString` / `get*ToString`, где это применимо.
## Тестирование
Переопределите base path:
```java
ApiClient client = new ApiClient();
client.setBasePath("http://localhost:8080");
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md)
---
title: "PHP"
description: "PHP-клиент Wildberries Seller API: пакет valeryverkhoturov/wb-api-client с Packagist, Guzzle, 13 категорий API, маскирование токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/php
---
# PHP
Пакет: [`valeryverkhoturov/wb-api-client`](https://packagist.org/packages/valeryverkhoturov/wb-api-client) на Packagist.
- **PHP:** 8.1+
- **HTTP:** [Guzzle 7](https://docs.guzzlephp.org/en/stable/)
- **Автозагрузка:** PSR-4, корневое пространство `ValeryVerkhoturov\WbApiClient\`
- **Обёртка секретов:** `SecretString` в каждом под-пространстве
## Установка
```bash
composer require valeryverkhoturov/wb-api-client
```
## Где лежит исходник
PHP здесь — отдельный случай: Packagist требует, чтобы `composer.json` был в **корне** обходимого репозитория, поэтому PHP-клиент живёт в отдельном репозитории — [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) — а не под `clients/php` в моно-репозитории.
Соседний репозиторий подключён как git-submodule по пути `clients/php` в основном репозитории, так что ежедневный CI-прогон генерирует все восемь языков в одном месте, а затем коммитит и тегает оба репозитория синхронно. Для конечного потребителя через Composer это прозрачно — просто `composer require` и работайте.
## Форма пространств имён
Корневое пространство: `ValeryVerkhoturov\WbApiClient\`. Каждая категория WB — PascalCase-подпространство:
```php
use ValeryVerkhoturov\WbApiClient\Items\Configuration;
use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi;
```
## Под-пространства
`General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`.
Полный список API на под-пространство: [README проекта wb-api-client-php](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme).
## Авторизация
```php
setAccessToken('<ваш JWT WB>');
$api = new ItemsApi(new Client(), $config);
$result = $api->someEndpoint();
```
`setAccessToken` принимает обычную строку и хранит её внутри как `SecretString`: `var_dump($config)` покажет токен замаскированным, а не сырым.
Получить сырое значение:
```php
$raw = $config->getAccessToken(); // string|null
```
## User-Agent
Каждый запрос отправляет `ValeryVerkhoturov/wb-api-client/php` (не дефолт openapi-generator). Чтобы добавить свой идентификатор приложения:
```php
$config = (new Configuration())
->setAccessToken(TOKEN)
->setUserAgent('MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/php');
```
## Свой Guzzle-клиент
Собственный клиент для прокси, middleware, кастомных таймаутов:
```php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
$stack = HandlerStack::create();
// $stack->push(...); // ваш middleware
$http = new Client([
'timeout' => 60,
'handler' => $stack,
'proxy' => getenv('HTTPS_PROXY') ?: null,
]);
$api = new ItemsApi($http, $config);
```
## Тестирование
Переопределите host на Configuration:
```php
$config = (new Configuration())
->setHost('http://localhost:8080')
->setAccessToken('test-token');
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [README соседнего репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme)
---
title: "OneScript (1С)"
description: "OneScript-клиент (1С) Wildberries Seller API: пакет wb-api-client с hub.oscript.io, 13 категорий API, маскирование bearer-токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/onescript
---
# OneScript (1С)
Пакет: [`wb-api-client`](https://hub.oscript.io/pools/default/packages/wb-api-client) на hub.oscript.io.
- **OneScript:** 2.0.0+
- **HTTP:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3
- **Зависимости:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3 — транспорт, [jason](https://hub.oscript.io/pools/default/packages/jason) 0.6.0 — сериализация моделей
- **Обёртка секретов:** класс `СекретнаяСтрока`
## Установка
```bash
opm install wb-api-client
```
Либо подключите каталог пакета напрямую — например, склонировав соседний репозиторий на нужном теге:
```bash
git clone --branch v1.20260926.0 https://github.com/ValeryVerkhoturov/wb-api-client-1c.git
```
```bsl
#Использовать "путь/к/wb-api-client-1c"
```
## Где лежит исходник
Как и PHP, OneScript живёт в отдельном репозитории — [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c), подключённом к моно-репозиторию как git-submodule по пути `clients/onescript`. Так пользователи 1С могут склонировать только клиент, не утаскивая весь пайплайн, и поставить его из тега.
Ежедневный CI-прогон генерирует все восемь языков в одном месте, затем коммитит и тегает оба соседних репозитория одним и тем же тегом `v`.
## Как разделены категории
В OneScript нет пространств имён: имя класса глобально. Поэтому категории разделены **именами**, а не путями импорта:
- **Классы API** названы по категории — одна категория, один класс: `GeneralApi`, `ItemsApi`, `OrdersFbsApi`, `FinancesApi`. Имена уникальны во всех 13 спецификациях, поэтому префикс им не нужен.
- **Классы моделей** несут категорию префиксом: `ItemsResponse4XX`, `OrdersFbsResponse4XX`. Без этого они бы столкнулись — одно только имя `Response4XX` встречается в 12 спецификациях из 13.
Файлы разложены по каталогам `src/Классы/<Категория>/` и `src/Модели/<Категория>/`, но каталоги — только для чтения человеком: классы резолвит `lib.config`.
Один `#Использовать` подключает сразу все категории.
## Авторизация
```bsl
#Использовать "wb-api-client"
Настройки = Новый Конфигурация();
Настройки.УстановитьТокен("<ваш JWT WB>");
Клиент = Новый GeneralApi(Настройки);
Ответ = Клиент.GetPing();
Сообщить(Ответ.КодСостояния);
Сообщить(Ответ.Данные["Status"]);
```
`УстановитьТокен` принимает и строку, и готовую `СекретнаяСтрока`.
Токен не раскрывается при выводе: у объекта нет экспортируемого свойства со значением, а `Строка(Объект)` в OneScript возвращает только имя класса.
```bsl
Сообщить(Настройки.Токен()); // СекретнаяСтрока
Сообщить(Настройки.Токен().Раскрыть()); // eyJhbGciOi... (по запросу)
```
## Форма методов
Обязательные параметры передаются позиционно, необязательные — одной коллекцией последним аргументом:
```bsl
Клиент = Новый ItemsApi(Настройки);
// Только тело запроса
Ответ = Клиент.PostV2CardsDeleteTrash(Тело);
// Тело + необязательные параметры строки запроса
Ответ = Клиент.PostV2CardsErrorList(Тело, Новый Структура("locale", "ru"));
```
Ключи в `ДопПараметры` совпадают с именами из спецификации. Если имя не является допустимым идентификатором `Структуры` (например, содержит дефис — как заголовок `X-Trace`), передайте `Соответствие`:
```bsl
ДопПараметры = Новый Соответствие;
ДопПараметры.Вставить("X-Trace", "trace-1");
```
## Ответ
Каждый метод возвращает `ОтветAPI`:
| Свойство / метод | Тип | Описание |
| ---------------- | ------------ | ---------------------------------- |
| `КодСостояния` | Число | код состояния HTTP |
| `Данные` | Произвольный | тело, разобранное из JSON |
| `Тело` | Строка | тело ответа как есть |
| `Заголовки` | Соответствие | заголовки ответа |
| `Успешно()` | Булево | код 2xx |
| `ЭтоОшибка()` | Булево | код 4xx или 5xx |
| `Заголовок(Имя)` | Строка | поиск заголовка без учёта регистра |
Если тело не JSON (`text/plain`, `application/zip`), `Данные` будет `Неопределено`, а содержимое останется в `Тело`.
## Обработка ошибок
По умолчанию ответы 4xx и 5xx приводят к исключению с текстом вида `HTTP 404 — GET /api/v3/orders: <тело>`.
`ВызватьИсключение` в OneScript не сохраняет объект для перехватчика — `ОписаниеОшибки()` вернул бы только имя класса, — поэтому отдельного класса исключения здесь нет: вся информация в тексте. Чтобы разобрать ошибку по полям, отключите выброс:
```bsl
Настройки.ВыбрасыватьИсключениеПриОшибке = Ложь;
Ответ = Клиент.GetPing();
Если Ответ.ЭтоОшибка() Тогда
Сообщить(Ответ.КодСостояния);
Сообщить(Ответ.Данные["detail"]);
КонецЕсли;
```
## Адрес сервиса
У каждой категории WB свой хост, и он зашит в операцию. `БазовыйURL` по умолчанию пустой — это значит «брать адрес из спецификации». Заполните свойство, чтобы направить **все** запросы на другой хост, например на песочницу или тестовый стенд:
```bsl
Настройки.БазовыйURL = "https://marketplace-api-sandbox.wildberries.ru";
```
## User-Agent и заголовки
Каждый запрос отправляет `ValeryVerkhoturov/wb-api-client/onescript`. Заменить или дополнить:
```bsl
Настройки.ПользовательскийАгент = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/onescript";
Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Id", "…");
```
> **Заголовки только ASCII**
>
> Значения заголовков должны состоять из символов ASCII — это ограничение HTTP-стека .NET, на котором работает OneScript. Кириллица в параметрах строки запроса, сегментах пути и теле запроса работает: они кодируются отдельно.
## Таймаут и прокси
```bsl
Настройки.Таймаут = 120; // секунды
Настройки.Прокси = Новый ИнтернетПрокси(Истина);
```
## Модели
Модели — классы со свойствами и парой методов преобразования. Незаполненные свойства в тело запроса не попадают, поэтому там не окажется `null` вместо отсутствующего поля.
```bsl
Товар = Новый ItemsItem();
Товар.nmID = 123;
Товар.price = 1000;
Данные = Товар.Данные(); // Соответствие с именами из спецификации
// Обратно из ответа
Восстановленный = Новый ItemsItem(Ответ.Данные);
```
Тело запроса можно передавать и как модель, и как обычное `Соответствие` / `Структуру` — транспорт разложит и то, и другое.
## Чем сгенерировано
В openapi-generator нет генератора OneScript, поэтому он написан отдельно: [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator) — плагин, наследующий `DefaultCodegen` и подключаемый рядом с `openapi-generator-cli.jar` через `META-INF/services`. В основном репозитории он закреплён по коммиту, как и версия образа openapi-generator для остальных языков.
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [README соседнего репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-1c#readme)
---
title: "C#"
description: "C#-клиент Wildberries Seller API: пакет ValeryVerkhoturov.WbApiClient с NuGet, 13 категорий API, маскирование токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/csharp
---
# C#
Пакет: [`ValeryVerkhoturov.WbApiClient`](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient) на NuGet.
- **Целевая платформа:** .NET 8.0
- **HTTP:** [RestSharp](https://restsharp.dev/)
- **Пространство имён:** `ValeryVerkhoturov.WbApiClient.<Категория>`
- **Обёртка секретов:** `SecretString` в каждом пространстве имён
## Установка
```bash
dotnet add package ValeryVerkhoturov.WbApiClient
```
Или в `.csproj`:
```xml
```
## Форма пространств имён
Корень — `ValeryVerkhoturov.WbApiClient`, каждая категория WB это PascalCase-подпространство. Внутри него генератор раскладывает код по трём частям:
```csharp
using ValeryVerkhoturov.WbApiClient.Items.Api; // классы Api
using ValeryVerkhoturov.WbApiClient.Items.Client; // Configuration, SecretString, ApiException
using ValeryVerkhoturov.WbApiClient.Items.Model; // модели запросов и ответов
```
Категории изолированы пространствами имён, поэтому одноимённые модели из разных разделов не конфликтуют: `Items.Model.Error` и `Finances.Model.Error` сосуществуют.
## Под-пространства
`General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`.
## Авторизация
```csharp
using ValeryVerkhoturov.WbApiClient.General.Api;
using ValeryVerkhoturov.WbApiClient.General.Client;
var config = new Configuration();
config.SetAccessToken("<ваш JWT WB>");
var api = new GeneralApi(config);
var ping = api.GetPing();
Console.WriteLine(ping.Status);
```
`SetAccessToken` принимает обычную строку и хранит её внутри как `SecretString`. Геттера для токена у `Configuration` нет специально: сырое значение читают только сгенерированные классы Api — через `IReadableConfiguration`.
Токен не раскрывается при выводе: `ToString()` у `SecretString` всегда возвращает ``, а через него проходят и интерполяция строк, и `Console.WriteLine`, и большинство логгеров.
```csharp
var s = new SecretString("eyJhbGciOi...");
Console.WriteLine($"{s}"); //
Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (по запросу)
```
## Асинхронные вызовы
У каждого метода есть асинхронный вариант с суффиксом `Async`, принимающий `CancellationToken`:
```csharp
var ping = await api.GetPingAsync(cancellationToken: token);
```
## Настройки клиента
```csharp
var config = new Configuration
{
Timeout = TimeSpan.FromSeconds(60), // по умолчанию 100 секунд
UserAgent = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/csharp",
};
config.SetAccessToken(token);
config.DefaultHeaders.Add("X-Request-Id", requestId);
```
Каждый запрос по умолчанию отправляет `ValeryVerkhoturov/wb-api-client/csharp`.
### Адрес сервиса
У каждой категории WB свой хост, и он подставлен в клиент из спецификации. Чтобы направить запросы на другой хост — например на песочницу — задайте `BasePath`:
```csharp
config.BasePath = "https://marketplace-api-sandbox.wildberries.ru";
```
## Обработка ошибок
Ответы 4xx и 5xx приводят к `ApiException`:
```csharp
using ValeryVerkhoturov.WbApiClient.OrdersFbs.Api;
using ValeryVerkhoturov.WbApiClient.OrdersFbs.Client;
var fbsConfig = new Configuration();
fbsConfig.SetAccessToken("<ваш JWT WB>");
var fbs = new OrdersFbsApi(fbsConfig);
try
{
var orders = fbs.GetV3Orders(limit: 10, next: 0);
}
catch (ApiException e)
{
Console.WriteLine(e.ErrorCode); // код состояния HTTP
Console.WriteLine(e.Message);
Console.WriteLine(e.ErrorContent); // тело ответа
}
```
> **Configuration своя в каждом пространстве имён**
>
> Генератор выпускает отдельный `Configuration` (и `SecretString`, и
> `ApiException`) в каждой категории, поэтому `OrdersFbs.Client.Configuration` —
> это не `General.Client.Configuration`. Один экземпляр на все категории
> переиспользовать не получится: каждый класс Api принимает `Configuration`
> своего пространства имён. Токен при этом один и тот же.
## Внедрение зависимостей
Клиенты Api — обычные классы с конструктором от `Configuration`, поэтому регистрируются как есть:
```csharp
services.AddSingleton(_ =>
{
var config = new Configuration();
config.SetAccessToken(builder.Configuration["Wb:Token"]);
return config;
});
services.AddTransient();
```
Отдельного пакета для DI не требуется: генератор использует библиотеку RestSharp, а не `IHttpClientFactory`.
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Пакет на NuGet](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient)
---
title: "Rust"
description: "Rust-клиент Wildberries Seller API: крейт wb-api-client на crates.io, 13 категорий API, маскирование bearer-токена."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/rust
---
# Rust
Крейт: [`wb-api-client`](https://crates.io/crates/wb-api-client) на crates.io — исходники живут в [отдельном репозитории](https://github.com/ValeryVerkhoturov/wb-api-client-rust), подключённом к основному как submodule `clients/rust`.
- **Rust:** edition 2021 (пайплайн публикует с toolchain 1.98)
- **HTTP:** `reqwest` с `rustls-tls` — системный OpenSSL не нужен
- **Обёртка секретов:** домашний `SecretString` в каждом модуле
## Установка
```bash
cargo add wb-api-client
```
Зафиксировать конкретную версию по тегу git-зависимостью:
```toml
wb-api-client = { git = "https://github.com/ValeryVerkhoturov/wb-api-client-rust", tag = "v1.20261022.0" }
```
## Почему крейт живёт в отдельном репозитории
crates.io связывает крейт с выделенным репозиторием, а git-зависимости резолвятся от корня репозитория — поэтому клиент лежит в [wb-api-client-rust](https://github.com/ValeryVerkhoturov/wb-api-client-rust) с `Cargo.toml` в корне. Релизные теги (`vX.Y.Z`) пушатся в этот репозиторий тем же пайплайном, что и в основной.
## Форма импортов
Один крейт, 13 модулей-категорий под `wb_api_client::`, у каждого — под-модули `apis` и `models`:
```rust
use wb_api_client::items::apis::{configuration::Configuration, items_api};
```
Методы эндпоинтов — свободные `async fn` внутри `<категория>_api` и принимают `&Configuration`, поэтому нужен async-рантайм (в примерах — `tokio`).
## Под-модули
`general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`.
Функции и модели на модуль перечислены в per-release README: [wb-api-client-rust/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme).
## Авторизация
```rust
use wb_api_client::items::apis::{configuration::Configuration, items_api};
#[tokio::main]
async fn main() {
let mut cfg = Configuration::new();
cfg.set_access_token("<ваш JWT WB>"); // оборачивается в SecretString
let result = items_api::some_endpoint(&cfg).await;
println!("{:#?}", result);
}
```
`set_access_token` — единственный путь: токен хранится в домашнем `SecretString`, у которого и `Debug`, и `Display` печатают `` — случайно слить его в лог через `println!("{cfg:?}")` не получится. Получить сырое значение обратно — только явно:
```rust
use wb_api_client::items::apis::configuration::SecretString;
let s = SecretString::new("<ваш JWT WB>");
println!("{}", s.expose_secret());
```
## Свой HTTP-клиент
Подставьте свой `reqwest::Client` — для прокси, кастомных таймаутов или заголовков по умолчанию:
```rust
let mut cfg = Configuration::new();
cfg.client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(60))
.build()
.unwrap();
```
## Тестирование
Переопределите базовый URL при создании Configuration:
```rust
let mut cfg = Configuration::new();
cfg.base_path = "http://localhost:8080".to_owned();
```
## См. также
- [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)
- [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)
- [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme)
---
title: "Версионирование"
description: "Все восемь языковых пакетов делят одну версионную строку на релиз: 1.YYYYMMDD.N."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/versioning
---
# Версионирование
Все восемь языковых пакетов делят одну версионную строку на релиз: **`1.YYYYMMDD.N`**.
## Формат
| Часть | Значение | Почему |
| ------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MAJOR` | `1` — зафиксирован | Позволяет пути Go-модуля обходиться без суффикса `/vN`, который требовался бы при `MAJOR ≥ 2`. Любое действительно ломающее изменение пайплайна отражается мажором _репозитория кода_, а не новым путём модуля. |
| `MINOR` | `YYYYMMDD` (UTC-дата релиза) | Монотонно растёт по дням — `20260919 < 20260920 < 20261001`. Сортируется как целое по правилам semver. |
| `PATCH` | `N` — счётчик за день | `0` для первого релиза за день, далее `1`, `2`, … (редко). |
Пример: `1.20260921.0` — первый релиз 21 сентября 2026 (UTC).
## Как считается номер
Прямо в workflow daily-check:
```bash
today="$(date -u +%Y%m%d)"
n="$(git tag --list "v1.${today}.*" | wc -l | tr -d ' ')"
version="1.${today}.${n}"
```
Полученная строка вставляется в каждый языковой манифест — `pyproject.toml`, `package.json`, тег `go.mod`, `pom.xml`, `composer.json`, `Cargo.toml` — как есть. Никакой язык не видит переведённой формы.
## Что резолвится как «последняя»
Все релизы — **стабильные** (не prerelease), поэтому `@latest` / `LATEST` / `^1.0.0` берут максимальную опубликованную:
**Python**
```bash
pip install valeryverkhoturov-wb-api-client
```
**TypeScript**
```bash
npm install @valeryverkhoturov/wb-api-client
```
**Go**
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@latest
```
**Java**
```xml
[1.0.0,)
```
**PHP**
```bash
composer require valeryverkhoturov/wb-api-client
```
**OneScript**
```bash
opm install wb-api-client
```
**C#**
```bash
dotnet add package ValeryVerkhoturov.WbApiClient
```
**Rust**
```bash
cargo add wb-api-client
```
## Фиксация версии
Фиксируйте точный `1.YYYYMMDD.N`, когда важна воспроизводимость (CI, production):
**Python**
```txt
valeryverkhoturov-wb-api-client==1.20260926.0
```
**TypeScript**
```json
"@valeryverkhoturov/wb-api-client": "1.20260926.0"
```
**Go**
```bash
go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0
```
**Java**
```xml
1.20260926.0
```
**PHP**
```json
"valeryverkhoturov/wb-api-client": "1.20260926.0"
```
**OneScript**
```bsl
.ЗависитОт("wb-api-client", "1.20260926.0")
```
**C#**
```xml
```
**Rust**
```toml
wb-api-client = "=1.20260926.0"
```
## Каденция релизов
Новые версии выпускаются только когда апстрим-спецификации реально меняются. Технически: `daily-check.yml` запускается в 06:15 UTC, сверяет `swaggers/checksums.txt` с предыдущим прогоном и перегенерирует + тегает только если что-то изменилось. Спокойная неделя = ноль релизов; выкатка портала WB обычно даёт один релиз следующим утром.
Ручной форс-релиз (например, чтобы забрать client-side фикс): `Actions → Daily upstream check → Run workflow` с `force: true`.
## Депрекации
WB убирает поля по собственному графику. Когда это случается:
1. Из спецификации пропадает поле.
2. На следующем прогоне daily-check перегенерирует без поля.
3. Публикуется новая версия без поля.
4. Клиенты на предыдущей версии продолжают работать — в их локальных моделях поле есть, просто приходят null'ы.
Формального окна депрекации нет — источник истины — контракт апстрима. Если нужно время на миграцию, фиксируйте старую версию.
## Расхождение версий между языками — невозможно по построению
Одна и та же строка `1.YYYYMMDD.N` вставляется в манифест каждого языка в одном и том же вызове generate-clients.py, и все языки перегенерируются в каждом релизном прогоне — поэтому получить «Python 1.20260921.0, а Go 1.20260919.0» из одного апстрим-состояния невозможно.
Единственный способ уплыть: сломается публикация одного языка (например, Maven Central отклонит подпись). Тег и исходник уже в GitHub; ручной перезапуск конкретного publish-job для этого языка чинит ситуацию.
---
title: "Архитектура"
description: "Один экран о том, что реально лежит в основном репозитории, чтобы про сгенерированные клиенты ничего не казалось магией."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture
---
# Архитектура
Один экран о том, что реально лежит в [основном репозитории](https://github.com/ValeryVerkhoturov/wb-api-client), чтобы про сгенерированные клиенты ничего не казалось магией.
## Пайплайн
```
download-specs.py → swaggers/*.yaml (сырой апстрим, checksummed)
process-specs.py → swaggers/processed/ (13 проходов)
generate-clients.py → clients//… (openapi-generator-cli, 8 языков)
├── inject-secrets.py (SecretString-обёртка на каждый язык)
├── {black|prettier|gofmt|spotless|php-cs-fixer|rustfmt} (канонизация форматирования)
└── generate-readmes.py (README.md на каждый язык)
↓ коммитится тегом v, один тег покрывает все 8 языков
publish.yml → PyPI / npm / Go tag / Maven Central / Packagist / hub.oscript.io / NuGet / crates.io
generate-docs.py → wb-api-client-docs (справка по API: страницы операций,
сниппеты и структуры данных на 8 языках, RU/EN)
```
Каждый шаг — обычный Python-файл, ничего не скрыто за непрозрачным тулингом.
## Зачем генерировать
WB выпускает OpenAPI-спецификации. Написать 13 клиентов × 8 языков вручную — уплывут за неделю. Плюс генерации из спецификации: каждое поле, каждый enum, каждая обёртка ответа — ровно то, что задокументировал WB. И повторная синхронизация стоит нуля усилий после сборки пайплайна.
Минус — выхлоп генератора остаётся выхлопом генератора: местами уродливым, местами со следами кривостей спецификации. Именно поэтому есть слой пост-обработки.
## process-specs.py — 13 проходов
1. **`inject_bearer_auth`** — удаляет схему `HeaderApiKey` (дублирует реальный bearer JWT) и ставит одну `BearerAuth`. Каждый сгенерированный клиент получает ровно один путь авторизации.
2. **`fix_untyped_arrays`** — добавляет `items: {type: string}` массивам без указания внутреннего типа (иначе Go / TS сгенерируют `[]any`).
3. **`widen_integer_formats`** — ставит `format: int64` целым полям без формата и с форматами `uint32`/`uint64`/Unix-timestamp: WB использует их для ID и миллисекундных меток времени, которые переполняют знаковый 32-битный int. Явные `int32` и коды валют не трогаются.
4. **`inline_top_level_arrays`** — инлайнит все `$ref` на компоненты с `type: array` и схлопывает обёртки `allOf: [array, description-only]` (openapi-generator плохо работает с массивом на корне). Гоняется до неподвижной точки, потому что заинлайненные поддеревья могут содержать другие ссылки.
5. **`rename_digit_prefixed_schemas`** — `409SupplyDeliverError` → `Http409SupplyDeliverError` (иначе невалидный Go-идентификатор).
6. **`name_inline_response_schemas`** — поднимает анонимные inline-тела 4xx/5xx в схемы `Response` (снова ради валидных Go-идентификаторов).
7. **`drop_response_required`** — WB помечает `required` почти каждое поле ответа, но живой API опускает поля когда хочет, и тогда каждый язык падает на десериализации. Проход удаляет `required` у всех схем, достижимых из ответов; у тел запросов `required` сохраняется, так что валидация ввода не страдает.
8. **`sanitize_non_ascii_enums`** — добавляет `x-enum-varnames` для русских значений вроде `"Склад WB"` (иначе — пустые идентификаторы в JS/Go/Java).
9. **`rename_tags_to_module`** — теги WB русские (`Финансовые отчёты`), openapi-generator зачищает их до пустого имени класса, и все языки получают `Api`/`DefaultApi`. Проход переписывает теги каждой операции на имя модуля (`Finances`, `Reports`, …), поэтому у модуля один нормально названный класс (`FinancesApi`, …), а исходные теги сохраняются в `x-original-tags` для генератора документации.
10. **`drop_date_formats`** — WB возвращает `""` для неустановленных дат (`fixTariffDateFrom`, `utdUcdDate`), что не парсится ни одним строгим date-парсером. Проход снимает `format: date` / `format: date-time` со строковых схем, и клиенты типизируют их как обычные строки.
11. **`htmlize_descriptions_to_markdown`** — каждое поле `description` прогоняется через `markdownify`, чтобы HTML в докстрингах рендерился как Markdown.
12. **`absolutize_description_links`** — к относительным ссылкам `[…](https://valeryverkhoturov.github.io/wb-api-client-docs/openapi/…)` добавляется `https://dev.wildberries.ru`, чтобы ссылки в сгенерированных докстрингах открывались.
13. **`add_docs_links`** — добавляет строку `Library doc: ` в `description` каждой операции со ссылкой на страницу операции на этом сайте. Генераторы копируют `description` в докстринги/Javadoc, поэтому каждый метод клиента ссылается на свою страницу документации. `generate-docs.py` вырезает эту строку обратно, так что сам сайт не затрагивается.
## generate-clients.py
- Гоняет `openapi-generator-cli` (закреплённый Docker-образ) для каждой спецификации и каждого языка во временные каталоги.
- Склеивает пер-спецификационные SDK в единое дерево на язык — Python-пакеты под `wb_api_client.`, TypeScript subpath-экспорты, Go под-пакеты, Java под-пакеты, PHP под-пространства, OneScript — по префиксу имени класса (пространств имён в языке нет), C# — пространства имён, Rust — модули одного крейта (`wb_api_client::`).
- Запускает `inject-secrets.py`, чтобы патчить каждый сгенерированный `Configuration` / `ApiClient` соответствующей языковой обёрткой секрета.
- Запускает закреплённый форматтер: `black`, `prettier`, `gofmt`, spotless (google-java-format), `php-cs-fixer`, `dotnet format`, `cargo fmt` (rustfmt). Всё внутри Docker — без языковых рантаймов на хосте.
- Подставляет `__VERSION__` в топ-уровневые манифесты (`pyproject.toml`, `package.json`, `go.mod`, `pom.xml`, `composer.json`, `Cargo.toml`).
- Запускает `generate-readmes.py`, чтобы выпустить README на каждый язык.
Итог: `clients/{python,typescript,go,java,php,onescript,csharp,rust}/` — восемь пакетов, готовых к публикации.
## Детерминизм
Ежедневная задача постоянно бы «дрожала», не будь генерация детерминированной. Что для этого сделано:
- Docker-тег `openapi-generator-cli` закреплён.
- В конфиге openapi-generator для каждого языка выставлено `hideGenerationTimestamp: true`.
- Все форматтеры закреплены на конкретных версиях.
- PR-check-workflow перегенерирует всё с нуля и падает, если `git diff swaggers/ clients/` непустой. Это ловит любое изменение, из-за которого следующие прогоны разъедутся.
## Публикация
`daily-check.yml` считает версию, регенерирует, коммитит, тегает. Затем шлёт 8 событий `workflow_dispatch` в `publish.yml` — по одному на язык — с тегом в качестве входа `ref`. Каждая языковая ветка `publish.yml`:
- Python — OIDC → PyPI trusted publishing (`pypa/gh-action-pypi-publish`).
- TypeScript — OIDC → npm trusted publishing (`npm publish --provenance`).
- Go — тег пушится в [`wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) (см. ниже), сама джоба — no-op: `proxy.golang.org` тянет прямо из тега.
- Java — подписанный GPG-деплой в Maven Central через `central-publishing-maven-plugin`. Maven закреплён на 3.9.x (`stCarolas/setup-maven`): плагин собирает bundle через Maven-2 stager, и Resolver из Maven 3.10 роняет туда служебный `maven-metadata-local.xml`, из-за чего Central отклоняет bundle («Bundle has content that does NOT have a .pom file»).
- PHP — пинг API `packagist.org/api/update-package`.
- OneScript — `opm build` и `opm push` в hub.oscript.io.
- C# — `dotnet pack` и `dotnet nuget push` в NuGet.
- Rust — `cargo publish` в crates.io (GitHub-окружение `crates-io`, toolchain закреплён на 1.98).
Отдельно daily-check перегенерирует справку по API (`generate-docs.py` — страницы операций, сниппеты вызова и структуры входящих/исходящих данных на каждом языке, ссылки на спецификацию WB) и пушит её в `wb-api-client-docs`, откуда сайт деплоится на GitHub Pages.
Reusable-воркфлоу (`workflow_call`) был бы аккуратнее, но PyPI и npm trusted publishing их не поддерживают: и OIDC-claim `job_workflow_ref` (callee), и Sigstore-attestation `workflow_ref` (caller) должны указывать на один и тот же файл, что с `workflow_call` невозможно. Поэтому — `workflow_dispatch`.
## Соседние репозитории
Четыре языка живут в своих репозиториях, подключённых сюда как git-submodule:
- [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) → `clients/php`. Причина: Packagist требует `composer.json` в КОРНЕ обходимого репозитория.
- [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c) → `clients/onescript`. Так пользователи 1С могут склонировать только клиент и поставить его из тега.
- [`ValeryVerkhoturov/wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) → `clients/go`. Причина: Go версионирует модули тегами репозитория, поэтому модуль обязан лежать в корне репо (`go get github.com/ValeryVerkhoturov/wb-api-client-go`) — модуль в подкаталоге потребовал бы тегов вида `clients/go/v…` и тянул бы весь монорепозиторий.
- [`ValeryVerkhoturov/wb-api-client-rust`](https://github.com/ValeryVerkhoturov/wb-api-client-rust) → `clients/rust`. Причина: crates.io связывает крейт с выделенным репозиторием, а git-зависимости (`wb-api-client = { git = "…", tag = "vX.Y.Z" }`) резолвятся от корня репозитория.
Ежедневный прогон коммитит и тегает все четыре соседних репозитория ДО основного — иначе записанные указатели сабмодулей ссылались бы на коммиты, которых на их удалённых ещё нет, и свежий `git clone --recurse-submodules` падал бы.
Пятый сосед — [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs), этот сайт. Он не сабмодуль: daily-check клонирует его, запускает `generate-docs.py` и пушит результат обратно.
## Генератор OneScript
В openapi-generator нет генератора OneScript, поэтому он вынесен в отдельный репозиторий — [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator). Это обычный плагин: класс наследует `DefaultCodegen`, регистрируется через `META-INF/services` и подключается рядом с `openapi-generator-cli.jar`, так что `-g onescript` работает как встроенный генератор. В основном репозитории он закреплён по коммиту — это аналог закреплённого тега Docker-образа для остальных языков.
## Что НЕ генерируется
Ручные части основного репозитория:
- `scripts/` — сам пайплайн, разложенный по доменным контекстам в `scripts/pipeline/`: `core/` (общее ядро: пути репозитория, именование спецификаций/модулей/языков, YAML IO, запуск внешних инструментов (subprocess, docker, git, python, macOS codesign)), `specs/` (download, postprocess), `generation/` (generate, secrets), `onescript/` (toolchain, manifests, verify), `docs/` (reference, readmes). Плоские `scripts/*.py` — тонкие CLI-обёртки над этими модулями, названные по производимому результату: `download-specs`, `process-specs`, `generate-clients`, `generate-readmes`, `generate-docs`, `generate-onescript-manifests`, `inject-secrets`, `verify-onescript`. Домашнее правило: ни `#`-комментариев, ни докстрингов под `scripts/` — проверяется в `make scripts-check` и в PR-check.
- `generator-configs/*.yaml` — по одному конфигу openapi-generator на язык.
- `templates/*` — топ-уровневые манифесты с плейсхолдерами `__VERSION__`.
- `.github/workflows/*` — daily-check, per-language publish, PR drift check.
- `Makefile`, `README.md`, `CLAUDE.md`.
Всё под `clients/` — сгенерированный выхлоп, руками не редактируется. Перегенерируйте.
## Полное описание пайплайна
Файл [`CLAUDE.md`](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/CLAUDE.md) в основном репозитории — рабочая, машиночитаемая версия того же содержимого, поддерживается в актуальном состоянии.
---
title: "Участие в разработке"
description: "Баги, странности спецификации, кривости генератора — заводите issue в основном репозитории кода: ValeryVerkhoturov/wb-api-client/issues."
lang: ru-RU
source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/contributing
---
# Участие в разработке
## Сообщения об ошибках
Баги, странности спецификации, кривости генератора — заводите issue в основном репозитории кода: [`ValeryVerkhoturov/wb-api-client/issues`](https://github.com/ValeryVerkhoturov/wb-api-client/issues).
Что приложить:
- Какой языковой клиент (Python / TypeScript / Go / Java / PHP / OneScript / C# / Rust)
- Какой под-модуль (`items`, `orders_fbs`, `analytics`, …)
- Версию из манифеста (`1.YYYYMMDD.N`)
- Минимальный воспроизводимый пример — один вызов API, одно исключение, ожидание против факта
Проблемы сайта документации (опечатки, недостающее, битые примеры) — в репозиторий документации: [`ValeryVerkhoturov/wb-api-client-docs/issues`](https://github.com/ValeryVerkhoturov/wb-api-client-docs/issues).
## Сгенерированный код руками НЕ редактируется
Каждый файл под `clients/` в основном репозитории — сгенерирован. Правка там переписывается на следующем прогоне daily-check — PR-check тоже отметит правку до мерджа.
Если нашли ошибку в сгенерированном выходе, чинить нужно в одном из:
- **`scripts/pipeline/specs/postprocess.py`** — новый проход, если правка применяется к спецификации до генерации.
- **`scripts/pipeline/generation/secrets.py`** — если правка внутри сгенерированного `Configuration` / `ApiClient`.
- **`generator-configs/*.yaml`** — если это флаг openapi-generator.
- **`templates/{lang}/*`** — если это топ-уровневый манифест.
Карта пайплайна — в [справочнике по архитектуре](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture).
## Локальная разработка
```bash
git clone --recurse-submodules https://github.com/ValeryVerkhoturov/wb-api-client
cd wb-api-client
pip install -r scripts/requirements.txt # ruamel.yaml, markdownify, requests, black, flask
./scripts/download-specs.py # тянет апстрим (локально может отдать 498)
python scripts/process-specs.py # -> swaggers/processed/
./scripts/generate-clients.py 1.20260920.0 # -> clients/{python,typescript,go,java,php,onescript,csharp,rust}
make verify # сборка + проверка форматирования на каждом языке
```
`generate-clients.py` всегда регенерирует все восемь языков. Для одноязычной итерации при отладке комментируйте остальные вызовы `queue_generation`, а не добавляйте флаг. Переменная окружения `OPENAPI_GENERATOR_THREADS` управляет параллелизмом генерации (по умолчанию 2).
Форматирование гоняется в Docker, поэтому языковые рантаймы на хосте не нужны. `make verify-` и `make {black,prettier,gofmt,spotless,php-cs-fixer,dotnet-format}` — отдельные цели. У OneScript форматтера нет, поэтому `make verify-onescript` вместо него компилирует каждый модуль через `oscript -check` и загружает пакет целиком. `make help` перечисляет всё.
Сам пайплайн лежит в `scripts/pipeline/` по доменным контекстам (см. [архитектуру](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture)); плоские `scripts/*.py` — лишь CLI-обёртки. Правки в `scripts/` дополнительно проверяются `make scripts-check` (smoke-проверки общего ядра, запрет `#`-комментариев и докстрингов), а black гоняется и по `scripts/`, не только по `clients/python`.
## Flow с PR
1. Форкните основной репозиторий.
2. Внесите изменение в `scripts/`, `templates/`, `generator-configs/` или воркфлоу.
3. Перегенерируйте локально, чтобы `clients/` совпало.
4. Закоммитьте `swaggers/`, `clients/` и свои правки исходников одним коммитом.
5. Откройте PR. PR-check-workflow перегенерирует всё с нуля и упадёт, если `git diff` непустой относительно вашего коммита — это ловит недетерминированность.
## Участие в документации
Этот сайт живёт в [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs).
```bash
git clone https://github.com/ValeryVerkhoturov/wb-api-client-docs
cd wb-api-client-docs
npm install
npm run docs:dev
# → http://localhost:5173/wb-api-client-docs/
```
При пуше в `main` workflow деплоя сам собирает и публикует на GitHub Pages.
## Лицензия
Оба репозитория — Apache 2.0. Ваши правки считаются переданными под той же лицензией.