avito-ads 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- avito_ads-0.1.0/LICENSE +21 -0
- avito_ads-0.1.0/PKG-INFO +513 -0
- avito_ads-0.1.0/README.md +473 -0
- avito_ads-0.1.0/avito_ads/__init__.py +126 -0
- avito_ads-0.1.0/avito_ads/_async/__init__.py +7 -0
- avito_ads-0.1.0/avito_ads/_async/client.py +103 -0
- avito_ads-0.1.0/avito_ads/_async/resources.py +327 -0
- avito_ads-0.1.0/avito_ads/_async/token_provider.py +79 -0
- avito_ads-0.1.0/avito_ads/_async/transport.py +114 -0
- avito_ads-0.1.0/avito_ads/_core/__init__.py +12 -0
- avito_ads-0.1.0/avito_ads/_core/operations.py +399 -0
- avito_ads-0.1.0/avito_ads/_core/response.py +20 -0
- avito_ads-0.1.0/avito_ads/_core/transport.py +179 -0
- avito_ads-0.1.0/avito_ads/_sync/__init__.py +7 -0
- avito_ads-0.1.0/avito_ads/_sync/client.py +108 -0
- avito_ads-0.1.0/avito_ads/_sync/resources.py +304 -0
- avito_ads-0.1.0/avito_ads/_sync/token_provider.py +79 -0
- avito_ads-0.1.0/avito_ads/_sync/transport.py +111 -0
- avito_ads-0.1.0/avito_ads/auth/__init__.py +24 -0
- avito_ads-0.1.0/avito_ads/auth/credentials.py +23 -0
- avito_ads-0.1.0/avito_ads/auth/storage.py +87 -0
- avito_ads-0.1.0/avito_ads/auth/token.py +70 -0
- avito_ads-0.1.0/avito_ads/configuration.py +128 -0
- avito_ads-0.1.0/avito_ads/contract_builder.py +169 -0
- avito_ads-0.1.0/avito_ads/enums.py +161 -0
- avito_ads-0.1.0/avito_ads/exceptions.py +93 -0
- avito_ads-0.1.0/avito_ads/filters.py +193 -0
- avito_ads-0.1.0/avito_ads/integrations/__init__.py +13 -0
- avito_ads-0.1.0/avito_ads/integrations/_common.py +44 -0
- avito_ads-0.1.0/avito_ads/integrations/django.py +78 -0
- avito_ads-0.1.0/avito_ads/integrations/fastapi.py +69 -0
- avito_ads-0.1.0/avito_ads/integrations/flask.py +76 -0
- avito_ads-0.1.0/avito_ads/models/__init__.py +40 -0
- avito_ads-0.1.0/avito_ads/models/account.py +59 -0
- avito_ads-0.1.0/avito_ads/models/advertiser.py +55 -0
- avito_ads-0.1.0/avito_ads/models/base.py +65 -0
- avito_ads-0.1.0/avito_ads/models/campaign.py +75 -0
- avito_ads-0.1.0/avito_ads/models/child_account.py +33 -0
- avito_ads-0.1.0/avito_ads/models/contract.py +60 -0
- avito_ads-0.1.0/avito_ads/models/created.py +47 -0
- avito_ads-0.1.0/avito_ads/models/creative.py +71 -0
- avito_ads-0.1.0/avito_ads/models/group.py +67 -0
- avito_ads-0.1.0/avito_ads/models/statistics.py +123 -0
- avito_ads-0.1.0/avito_ads/models/user.py +23 -0
- avito_ads-0.1.0/avito_ads/pagination.py +126 -0
- avito_ads-0.1.0/avito_ads/py.typed +0 -0
- avito_ads-0.1.0/avito_ads.egg-info/PKG-INFO +513 -0
- avito_ads-0.1.0/avito_ads.egg-info/SOURCES.txt +51 -0
- avito_ads-0.1.0/avito_ads.egg-info/dependency_links.txt +1 -0
- avito_ads-0.1.0/avito_ads.egg-info/requires.txt +16 -0
- avito_ads-0.1.0/avito_ads.egg-info/top_level.txt +1 -0
- avito_ads-0.1.0/pyproject.toml +76 -0
- avito_ads-0.1.0/setup.cfg +4 -0
avito_ads-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Avito Ads Python SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
avito_ads-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: avito-ads
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK для публичного API кабинета Авито Реклама (Avito Ads). Синхронный и асинхронный режимы (httpx), Python 3.8+, интеграции с Django, Flask и FastAPI.
|
|
5
|
+
Author-email: asbezrukov <asbezrukov@avito.ru>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/avito-tech/avito-ads-sdk-python3
|
|
8
|
+
Project-URL: Repository, https://github.com/avito-tech/avito-ads-sdk-python3
|
|
9
|
+
Project-URL: Changelog, https://github.com/avito-tech/avito-ads-sdk-python3/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/avito-tech/avito-ads-sdk-python3/issues
|
|
11
|
+
Keywords: avito,avito-ads,реклама,api,client,sdk,async,httpx
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: httpx<1.0,>=0.24
|
|
28
|
+
Provides-Extra: django
|
|
29
|
+
Requires-Dist: django>=3.2; extra == "django"
|
|
30
|
+
Provides-Extra: flask
|
|
31
|
+
Requires-Dist: flask>=2.0; extra == "flask"
|
|
32
|
+
Provides-Extra: fastapi
|
|
33
|
+
Requires-Dist: fastapi>=0.95; extra == "fastapi"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# Avito Ads Python SDK
|
|
42
|
+
|
|
43
|
+
[](https://github.com/avito-tech/avito-ads-sdk-python3/actions/workflows/ci.yml)
|
|
44
|
+
[](https://www.python.org/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
Python SDK для API **Авито Реклама** (Avito Ads API).
|
|
48
|
+
|
|
49
|
+
- [Об Авито Реклама](http://avito.ru/ads)
|
|
50
|
+
- [Возможности API Авито Реклама](https://ads-help.avito.com/external/api)
|
|
51
|
+
- [Техническая документация API](https://developers.avito.ru/api-catalog/ads/documentation)
|
|
52
|
+
|
|
53
|
+
Библиотека закрывает все основные методы API: аккаунт и баланс, дочерние аккаунты и переводы средств, рекламодателей, договоры, кампании, группы объявлений, креативы, статистику и управление пользователями. Поддерживаются **два режима работы — синхронный и асинхронный** — с одинаковым набором методов. Транспорт построен на [httpx](https://www.python-httpx.org/), авторизация — OAuth2 `client_credentials` с автоматическим обновлением и кэшированием токена, а также повторными попытками при ошибках `429` и `5xx`.
|
|
54
|
+
|
|
55
|
+
## Возможности
|
|
56
|
+
|
|
57
|
+
- Поддержка **Python 3.8+**.
|
|
58
|
+
- **Синхронный (`Client`) и асинхронный (`AsyncClient`) режимы** с идентичным API — разница только в `await` / `async for` / `async with`.
|
|
59
|
+
- Установка в любой проект; интеграции с **Django**, **Flask**, **FastAPI** (асинхронная).
|
|
60
|
+
- OAuth2 `client_credentials`: автоматическое получение, кэширование и обновление токена.
|
|
61
|
+
- Раздельные окружения: **production** и **sandbox** (песочница).
|
|
62
|
+
- Автоматические повторы при `429`/`5xx` с экспоненциальной задержкой и учётом заголовка `Retry-After`; однократное обновление токена и повтор при `401`.
|
|
63
|
+
- Типизированные модели ответов и типизированные исключения по кодам ошибок.
|
|
64
|
+
- Постраничная выборка с удобным перебором всех страниц через генератор (`for`) или async-генератор (`async for`).
|
|
65
|
+
- Чтение остатка лимита из заголовка `Api-Point-Balance`.
|
|
66
|
+
- Полная типизация (`py.typed`, проверка `mypy --strict`).
|
|
67
|
+
- Документация и docstrings на русском языке.
|
|
68
|
+
|
|
69
|
+
## Требования
|
|
70
|
+
|
|
71
|
+
- Python >= 3.8
|
|
72
|
+
- `httpx >= 0.24, < 1.0`
|
|
73
|
+
|
|
74
|
+
## Установка
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install avito-ads
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
С интеграциями:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install "avito-ads[flask]" # или [django], [fastapi]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Быстрый старт
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from avito_ads import Client, Configuration
|
|
90
|
+
|
|
91
|
+
config = Configuration.create(
|
|
92
|
+
"ВАШ_CLIENT_ID",
|
|
93
|
+
"ВАШ_CLIENT_SECRET",
|
|
94
|
+
123456789, # accountID — идентификатор рекламного аккаунта
|
|
95
|
+
).production() # .sandbox() для песочницы
|
|
96
|
+
|
|
97
|
+
with Client(config) as client:
|
|
98
|
+
# Баланс аккаунта (в рублях)
|
|
99
|
+
balance = client.account.get_balance()
|
|
100
|
+
print(balance.balance, "₽, бонусы:", balance.bonus_balance)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`accountID` — это идентификатор аккаунта, к которому привязан токен; он задаётся один раз в конфигурации и подставляется во все запросы автоматически.
|
|
104
|
+
|
|
105
|
+
## Синхронный и асинхронный режимы
|
|
106
|
+
|
|
107
|
+
Библиотека предоставляет два клиента с одинаковым набором ресурсов, методов и аргументов: синхронный `Client` и асинхронный `AsyncClient`. Оба создаются из одной и той же `Configuration`.
|
|
108
|
+
|
|
109
|
+
Синхронный режим:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from avito_ads import Client, Configuration
|
|
113
|
+
|
|
114
|
+
config = Configuration.create("ID", "SECRET", 123456789).sandbox()
|
|
115
|
+
with Client(config) as client:
|
|
116
|
+
balance = client.account.get_balance()
|
|
117
|
+
for campaign in client.campaigns.iterate():
|
|
118
|
+
print(campaign.id, campaign.name)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Асинхронный режим — те же вызовы с `await`, перебор через `async for`, клиент как `async with`:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import asyncio
|
|
125
|
+
from avito_ads import AsyncClient, Configuration
|
|
126
|
+
|
|
127
|
+
async def main() -> None:
|
|
128
|
+
config = Configuration.create("ID", "SECRET", 123456789).sandbox()
|
|
129
|
+
async with AsyncClient(config) as client:
|
|
130
|
+
balance = await client.account.get_balance()
|
|
131
|
+
async for campaign in client.campaigns.iterate():
|
|
132
|
+
print(campaign.id, campaign.name)
|
|
133
|
+
|
|
134
|
+
asyncio.run(main())
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Оба клиента можно создавать и без менеджера контекста; в этом случае закрывайте их вручную: `client.close()` для синхронного и `await client.aclose()` для асинхронного.
|
|
138
|
+
|
|
139
|
+
## Конфигурация
|
|
140
|
+
|
|
141
|
+
Объект `Configuration` неизменяемый: все методы `with_*` и `production`/`sandbox` возвращают новый экземпляр.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
config = (
|
|
145
|
+
Configuration.create(client_id, client_secret, account_id)
|
|
146
|
+
.sandbox() # или .production()
|
|
147
|
+
.with_timeout(30.0) # таймаут запроса, сек
|
|
148
|
+
.with_max_retries(4) # макс. число повторов при 429/5xx
|
|
149
|
+
.with_retry_base_delay(1000) # базовая задержка повтора, мс
|
|
150
|
+
.with_token_leeway(60) # запас на досрочное обновление токена, сек
|
|
151
|
+
.with_user_agent("my-app/1.0") # значение заголовка User-Agent
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
При необходимости можно передать готовый HTTP-клиент httpx (например, с прокси, лимитами пула или HTTP/2):
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import httpx
|
|
159
|
+
|
|
160
|
+
# для синхронного Client
|
|
161
|
+
config = config.with_http_client(httpx.Client(http2=True))
|
|
162
|
+
# для асинхронного AsyncClient
|
|
163
|
+
config = config.with_async_client(httpx.AsyncClient(http2=True))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Окружения
|
|
167
|
+
|
|
168
|
+
| Окружение | Базовый адрес API |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `production()` | `https://api.avito.ru/ads/` |
|
|
171
|
+
| `sandbox()` | `https://api.avito.ru/ads-sandbox/` |
|
|
172
|
+
|
|
173
|
+
Эндпоинт получения токена общий для обоих окружений: `https://api.avito.ru/token`.
|
|
174
|
+
|
|
175
|
+
## Хранение и обновление токена
|
|
176
|
+
|
|
177
|
+
Токен запрашивается автоматически при первом обращении и переиспользуется до истечения срока. По умолчанию он хранится в памяти процесса. Чтобы переживать перезапуски, реализуйте свой класс хранилища и передайте его:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
# синхронный клиент — протокол avito_ads.auth.storage.TokenStorage (get/set/delete)
|
|
181
|
+
config = config.with_token_storage(my_storage)
|
|
182
|
+
|
|
183
|
+
# асинхронный клиент — протокол avito_ads.auth.storage.AsyncTokenStorage
|
|
184
|
+
# (async get/set/delete, удобно для асинхронного Redis)
|
|
185
|
+
config = config.with_async_token_storage(my_async_storage)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Работа с методами API
|
|
189
|
+
|
|
190
|
+
Все группы методов доступны как свойства клиента.
|
|
191
|
+
|
|
192
|
+
### Аккаунт
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
account = client.account.get()
|
|
196
|
+
print(account.short_name, "/ ИНН", account.inn)
|
|
197
|
+
|
|
198
|
+
balance = client.account.get_balance()
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Дочерние аккаунты и переводы
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
for child in client.child_accounts.list():
|
|
205
|
+
print(child.id, child.short_name)
|
|
206
|
+
|
|
207
|
+
# Создать дочерний аккаунт-непокупатель
|
|
208
|
+
created = client.child_accounts.create_nonpayer_child("ООО Дочка", True)
|
|
209
|
+
|
|
210
|
+
# Перевести средства / бонусы (сумма не менее 1)
|
|
211
|
+
client.child_accounts.transfer_funds(account_id_to=987654321, amount=5000)
|
|
212
|
+
client.child_accounts.transfer_bonus(account_id_to=987654321, amount=100)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Рекламодатели
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from avito_ads.enums import LegalType, LegalRole
|
|
219
|
+
|
|
220
|
+
created = client.advertisers.create(
|
|
221
|
+
inn="7712345678",
|
|
222
|
+
short_name="ООО Реклама",
|
|
223
|
+
long_name="Общество с ограниченной ответственностью «Реклама»",
|
|
224
|
+
ogrn="1177746123456",
|
|
225
|
+
legal_address="г. Москва, ул. Примерная, д. 1",
|
|
226
|
+
actual_address="г. Москва, ул. Примерная, д. 1",
|
|
227
|
+
legal_role=LegalRole.ADVERTISER,
|
|
228
|
+
legal_type=LegalType.UL,
|
|
229
|
+
kpp="771701001",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
advertisers = client.advertisers.list()
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Договоры
|
|
236
|
+
|
|
237
|
+
Для создания договора удобно использовать `ContractBuilder` — он проверяет обязательные поля по типу договора и бросает понятное исключение валидации ещё до запроса.
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from avito_ads import ContractBuilder
|
|
241
|
+
from avito_ads.enums import ContractCounterpartyType
|
|
242
|
+
|
|
243
|
+
builder = (
|
|
244
|
+
ContractBuilder.intermediary_contract()
|
|
245
|
+
.advertiser(987654321)
|
|
246
|
+
.counterparty_type(ContractCounterpartyType.DIRECT_WITH_ADVERTISER)
|
|
247
|
+
.subject("mediation")
|
|
248
|
+
.object("commercial")
|
|
249
|
+
.reporting_required(True)
|
|
250
|
+
.funds_allocation_to_principal(False)
|
|
251
|
+
.date("2025-01-15")
|
|
252
|
+
.number("ДА-2025/01")
|
|
253
|
+
.intermediary({
|
|
254
|
+
"shortName": "ООО Реклама",
|
|
255
|
+
"longName": "Общество с ограниченной ответственностью «Реклама»",
|
|
256
|
+
"inn": "7712345678",
|
|
257
|
+
"ogrn": "1177746123456",
|
|
258
|
+
"kpp": "771701001",
|
|
259
|
+
"legalAddress": "г. Москва, ул. Примерная, д. 1",
|
|
260
|
+
"actualAddress": "г. Москва, ул. Примерная, д. 1",
|
|
261
|
+
"legalType": "ul",
|
|
262
|
+
})
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
created = client.contracts.create(builder)
|
|
266
|
+
print("ID договора:", created.id)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Доступны быстрые конструкторы `ContractBuilder.service()`, `ContractBuilder.intermediary_contract()` и `ContractBuilder.external("CID")`. Дополнительное соглашение создаётся вызовом `.parent_id(...)` (поле `intermediary` при этом передавать нельзя).
|
|
270
|
+
|
|
271
|
+
В `create()` можно передать и готовый словарь тела запроса — тогда клиентская валидация не выполняется.
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
contracts = client.contracts.list()
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Кампании, группы, креативы
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
for campaign in client.campaigns.list():
|
|
281
|
+
print(campaign.id, campaign.name, f"[{campaign.status}]")
|
|
282
|
+
|
|
283
|
+
groups = client.groups.list()
|
|
284
|
+
creatives = client.creatives.list()
|
|
285
|
+
|
|
286
|
+
# Изменение бюджета и ставки группы (только ручное управление ставкой; значение не менее 1)
|
|
287
|
+
client.groups.change_budget(group_id=555, budget=100000)
|
|
288
|
+
client.groups.change_price(group_id=555, price=25)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Статистика
|
|
292
|
+
|
|
293
|
+
Период не может превышать 100 дней; даты — в формате `YYYY-MM-DD`. Эти ограничения проверяются на стороне клиента.
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
stats = client.statistics.campaign(campaign_id=555, date_from="2025-01-01", date_to="2025-01-31")
|
|
297
|
+
total = stats.campaign.total_data
|
|
298
|
+
print("Показы:", total.views, "клики:", total.clicks)
|
|
299
|
+
|
|
300
|
+
by_groups = client.statistics.groups(555, "2025-01-01", "2025-01-31", [101, 102])
|
|
301
|
+
by_creatives = client.statistics.creatives(555, "2025-01-01", "2025-01-31", [201, 202])
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Пользователи
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from avito_ads.enums import UserRole
|
|
308
|
+
|
|
309
|
+
users = client.users.list()
|
|
310
|
+
client.users.add(user_id=42, role=UserRole.ADMIN)
|
|
311
|
+
client.users.set_role(user_id=42, role=UserRole.VIEWER)
|
|
312
|
+
client.users.delete(user_id=42)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Постраничная выборка
|
|
316
|
+
|
|
317
|
+
Методы `list()` возвращают `PaginatedResult` — по нему можно итерироваться и брать `len()`.
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
result = client.campaigns.list(limit=50, page=1)
|
|
321
|
+
|
|
322
|
+
print("Всего:", result.total)
|
|
323
|
+
print("Остаток лимита API:", result.api_point_balance)
|
|
324
|
+
|
|
325
|
+
for campaign in result:
|
|
326
|
+
...
|
|
327
|
+
|
|
328
|
+
if result.has_next_page():
|
|
329
|
+
next_page = client.campaigns.list(limit=50, page=2)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Чтобы обойти все страницы автоматически, используйте `iterate()` — он возвращает генератор и подгружает страницы по мере необходимости:
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
for campaign in client.campaigns.iterate():
|
|
336
|
+
print(campaign.name)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
В асинхронном режиме `iterate()` возвращает async-генератор:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
async for campaign in async_client.campaigns.iterate():
|
|
343
|
+
print(campaign.name)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Фильтры
|
|
347
|
+
|
|
348
|
+
Фильтры можно задавать словарём или объектом-фильтром с проверкой имён полей.
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from avito_ads import CampaignsFilter, DateRange
|
|
352
|
+
from avito_ads.enums import CampaignStatus
|
|
353
|
+
|
|
354
|
+
f = (
|
|
355
|
+
CampaignsFilter()
|
|
356
|
+
.statuses([CampaignStatus.ACTIVE.value])
|
|
357
|
+
.contract_ids([10, 20])
|
|
358
|
+
.created_at(DateRange("2025-01-01", "2025-01-31"))
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
campaigns = client.campaigns.list(f)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Пустой фильтр корректно сериализуется в JSON-объект `{}`, как того требует API.
|
|
365
|
+
|
|
366
|
+
## Обработка ошибок
|
|
367
|
+
|
|
368
|
+
Все исключения наследуются от `avito_ads.exceptions.AvitoAdsError`.
|
|
369
|
+
|
|
370
|
+
| HTTP | Исключение |
|
|
371
|
+
|---|---|
|
|
372
|
+
| 400 | `BadRequestError` |
|
|
373
|
+
| 401 | `AuthenticationError` |
|
|
374
|
+
| 403 | `AccessDeniedError` |
|
|
375
|
+
| 404 | `NotFoundError` |
|
|
376
|
+
| 429 | `RateLimitError` (атрибут `retry_after`) |
|
|
377
|
+
| 5xx | `ServerError` |
|
|
378
|
+
| сеть/таймаут | `NetworkError` |
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
from avito_ads.exceptions import ApiError, RateLimitError
|
|
382
|
+
|
|
383
|
+
try:
|
|
384
|
+
client.account.get_balance()
|
|
385
|
+
except RateLimitError as exc:
|
|
386
|
+
retry_after = exc.retry_after
|
|
387
|
+
except ApiError as exc:
|
|
388
|
+
print(exc.status_code, exc.error_code, str(exc))
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Ошибки валидации на стороне клиента (неверный период статистики, сумма перевода меньше 1, неизвестная роль и т. п.) бросают `avito_ads.exceptions.ValidationError`.
|
|
392
|
+
|
|
393
|
+
## Лимиты и повторы
|
|
394
|
+
|
|
395
|
+
API ограничивает частоту запросов (порядка 500 в минуту). Клиент автоматически повторяет запросы при `429` и `5xx` с экспоненциальной задержкой и учитывает заголовок `Retry-After`. Остаток лимита доступен из заголовка `Api-Point-Balance` (например, `PaginatedResult.api_point_balance`).
|
|
396
|
+
|
|
397
|
+
## Интеграция с Django
|
|
398
|
+
|
|
399
|
+
В `settings.py`:
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
AVITO_ADS = {
|
|
403
|
+
"client_id": "...",
|
|
404
|
+
"client_secret": "...",
|
|
405
|
+
"account_id": 123456789,
|
|
406
|
+
"environment": "production",
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Использование:
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
from avito_ads.integrations.django import get_client
|
|
414
|
+
|
|
415
|
+
balance = get_client().account.get_balance()
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Опционально добавьте `"avito_ads.integrations.django"` в `INSTALLED_APPS`, чтобы конфигурация проверялась при старте приложения.
|
|
419
|
+
|
|
420
|
+
## Интеграция с Flask
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
from flask import Flask
|
|
424
|
+
from avito_ads.integrations.flask import AvitoAds
|
|
425
|
+
|
|
426
|
+
app = Flask(__name__)
|
|
427
|
+
app.config.update(
|
|
428
|
+
AVITO_ADS_CLIENT_ID="...",
|
|
429
|
+
AVITO_ADS_CLIENT_SECRET="...",
|
|
430
|
+
AVITO_ADS_ACCOUNT_ID=123456789,
|
|
431
|
+
AVITO_ADS_ENVIRONMENT="production",
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
avito = AvitoAds(app)
|
|
435
|
+
|
|
436
|
+
@app.route("/balance")
|
|
437
|
+
def balance():
|
|
438
|
+
return {"balance": avito.client.account.get_balance().balance}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Поддерживается и фабричный паттерн через `AvitoAds().init_app(app)`.
|
|
442
|
+
|
|
443
|
+
## Интеграция с FastAPI
|
|
444
|
+
|
|
445
|
+
FastAPI — асинхронный фреймворк, поэтому интеграция отдаёт асинхронный `AsyncClient`:
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
from contextlib import asynccontextmanager
|
|
449
|
+
from fastapi import Depends, FastAPI
|
|
450
|
+
from avito_ads import AsyncClient
|
|
451
|
+
from avito_ads.integrations.fastapi import AvitoAdsProvider
|
|
452
|
+
|
|
453
|
+
avito = AvitoAdsProvider({
|
|
454
|
+
"client_id": "...",
|
|
455
|
+
"client_secret": "...",
|
|
456
|
+
"account_id": 123456789,
|
|
457
|
+
"environment": "production",
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
@asynccontextmanager
|
|
461
|
+
async def lifespan(app: FastAPI):
|
|
462
|
+
yield
|
|
463
|
+
await avito.aclose()
|
|
464
|
+
|
|
465
|
+
app = FastAPI(lifespan=lifespan)
|
|
466
|
+
|
|
467
|
+
@app.get("/balance")
|
|
468
|
+
async def balance(client: AsyncClient = Depends(avito)):
|
|
469
|
+
result = await client.account.get_balance()
|
|
470
|
+
return {"balance": result.balance}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Песочница
|
|
474
|
+
|
|
475
|
+
Создание тестового аккаунта доступно только в песочнице:
|
|
476
|
+
|
|
477
|
+
```python
|
|
478
|
+
config = Configuration.create(client_id, client_secret, account_id).sandbox()
|
|
479
|
+
|
|
480
|
+
with Client(config) as client:
|
|
481
|
+
created = client.account.create_sandbox_account(
|
|
482
|
+
inn="7712345678",
|
|
483
|
+
short_name="Тестовый аккаунт",
|
|
484
|
+
long_name="Полное наименование",
|
|
485
|
+
ogrn="1177746123456",
|
|
486
|
+
legal_address="г. Москва, ул. Примерная, д. 1",
|
|
487
|
+
actual_address="г. Москва, ул. Примерная, д. 1",
|
|
488
|
+
contact={"name": "Иван Иванов", "phone": "+79001234567"},
|
|
489
|
+
)
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
## Разработка и тестирование
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
pip install -e ".[dev]"
|
|
496
|
+
pytest # модульные тесты
|
|
497
|
+
ruff check . # линтер
|
|
498
|
+
mypy avito_ads # проверка типов (strict)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Интеграционные тесты против реальной песочницы по умолчанию пропускаются. Чтобы запустить их:
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
AVITO_ADS_RUN_INTEGRATION=1 \
|
|
505
|
+
AVITO_ADS_CLIENT_ID=... \
|
|
506
|
+
AVITO_ADS_CLIENT_SECRET=... \
|
|
507
|
+
AVITO_ADS_ACCOUNT_ID=... \
|
|
508
|
+
pytest -m integration
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Лицензия
|
|
512
|
+
|
|
513
|
+
[MIT](LICENSE).
|