Skip to main content

Python SDK для публичного API кабинета Авито Реклама (Avito Ads). Синхронный и асинхронный режимы (httpx), Python 3.8+, интеграции с Django, Flask и FastAPI.

Project description

Avito Ads Python SDK

CI Python License: MIT

Python SDK для API Авито Реклама (Avito Ads API).

Библиотека закрывает все основные методы API: аккаунт и баланс, дочерние аккаунты и переводы средств, рекламодателей, договоры, кампании, группы объявлений, креативы, статистику и управление пользователями. Поддерживаются два режима работы — синхронный и асинхронный — с одинаковым набором методов. Транспорт построен на httpx, авторизация — OAuth2 client_credentials с автоматическим обновлением и кэшированием токена, а также повторными попытками при ошибках 429 и 5xx.

Возможности

  • Поддержка Python 3.8+.
  • Синхронный (Client) и асинхронный (AsyncClient) режимы с идентичным API — разница только в await / async for / async with.
  • Установка в любой проект; интеграции с Django, Flask, FastAPI (асинхронная).
  • OAuth2 client_credentials: автоматическое получение, кэширование и обновление токена.
  • Раздельные окружения: production и sandbox (песочница).
  • Автоматические повторы при 429/5xx с экспоненциальной задержкой и учётом заголовка Retry-After; однократное обновление токена и повтор при 401.
  • Типизированные модели ответов и типизированные исключения по кодам ошибок.
  • Постраничная выборка с удобным перебором всех страниц через генератор (for) или async-генератор (async for).
  • Чтение остатка лимита из заголовка Api-Point-Balance.
  • Полная типизация (py.typed, проверка mypy --strict).
  • Документация и docstrings на русском языке.

Требования

  • Python >= 3.8
  • httpx >= 0.24, < 1.0

Установка

pip install avito-ads

С интеграциями:

pip install "avito-ads[flask]"     # или [django], [fastapi]

Быстрый старт

from avito_ads import Client, Configuration

config = Configuration.create(
    "ВАШ_CLIENT_ID",
    "ВАШ_CLIENT_SECRET",
    123456789,            # accountID — идентификатор рекламного аккаунта
).production()            # .sandbox() для песочницы

with Client(config) as client:
    # Баланс аккаунта (в рублях)
    balance = client.account.get_balance()
    print(balance.balance, "₽, бонусы:", balance.bonus_balance)

accountID — это идентификатор аккаунта, к которому привязан токен; он задаётся один раз в конфигурации и подставляется во все запросы автоматически.

Синхронный и асинхронный режимы

Библиотека предоставляет два клиента с одинаковым набором ресурсов, методов и аргументов: синхронный Client и асинхронный AsyncClient. Оба создаются из одной и той же Configuration.

Синхронный режим:

from avito_ads import Client, Configuration

config = Configuration.create("ID", "SECRET", 123456789).sandbox()
with Client(config) as client:
    balance = client.account.get_balance()
    for campaign in client.campaigns.iterate():
        print(campaign.id, campaign.name)

Асинхронный режим — те же вызовы с await, перебор через async for, клиент как async with:

import asyncio
from avito_ads import AsyncClient, Configuration

async def main() -> None:
    config = Configuration.create("ID", "SECRET", 123456789).sandbox()
    async with AsyncClient(config) as client:
        balance = await client.account.get_balance()
        async for campaign in client.campaigns.iterate():
            print(campaign.id, campaign.name)

asyncio.run(main())

Оба клиента можно создавать и без менеджера контекста; в этом случае закрывайте их вручную: client.close() для синхронного и await client.aclose() для асинхронного.

Конфигурация

Объект Configuration неизменяемый: все методы with_* и production/sandbox возвращают новый экземпляр.

config = (
    Configuration.create(client_id, client_secret, account_id)
    .sandbox()                       # или .production()
    .with_timeout(30.0)              # таймаут запроса, сек
    .with_max_retries(4)             # макс. число повторов при 429/5xx
    .with_retry_base_delay(1000)     # базовая задержка повтора, мс
    .with_token_leeway(60)           # запас на досрочное обновление токена, сек
    .with_user_agent("my-app/1.0")   # значение заголовка User-Agent
)

При необходимости можно передать готовый HTTP-клиент httpx (например, с прокси, лимитами пула или HTTP/2):

import httpx

# для синхронного Client
config = config.with_http_client(httpx.Client(http2=True))
# для асинхронного AsyncClient
config = config.with_async_client(httpx.AsyncClient(http2=True))

Окружения

Окружение Базовый адрес API
production() https://api.avito.ru/ads/
sandbox() https://api.avito.ru/ads-sandbox/

Эндпоинт получения токена общий для обоих окружений: https://api.avito.ru/token.

Хранение и обновление токена

Токен запрашивается автоматически при первом обращении и переиспользуется до истечения срока. По умолчанию он хранится в памяти процесса. Чтобы переживать перезапуски, реализуйте свой класс хранилища и передайте его:

# синхронный клиент — протокол avito_ads.auth.storage.TokenStorage (get/set/delete)
config = config.with_token_storage(my_storage)

# асинхронный клиент — протокол avito_ads.auth.storage.AsyncTokenStorage
# (async get/set/delete, удобно для асинхронного Redis)
config = config.with_async_token_storage(my_async_storage)

Работа с методами API

Все группы методов доступны как свойства клиента.

Аккаунт

account = client.account.get()
print(account.short_name, "/ ИНН", account.inn)

balance = client.account.get_balance()

Дочерние аккаунты и переводы

for child in client.child_accounts.list():
    print(child.id, child.short_name)

# Создать дочерний аккаунт-непокупатель
created = client.child_accounts.create_nonpayer_child("ООО Дочка", True)

# Перевести средства / бонусы (сумма не менее 1)
client.child_accounts.transfer_funds(account_id_to=987654321, amount=5000)
client.child_accounts.transfer_bonus(account_id_to=987654321, amount=100)

Рекламодатели

from avito_ads.enums import LegalType, LegalRole

created = client.advertisers.create(
    inn="7712345678",
    short_name="ООО Реклама",
    long_name="Общество с ограниченной ответственностью «Реклама»",
    ogrn="1177746123456",
    legal_address="г. Москва, ул. Примерная, д. 1",
    actual_address="г. Москва, ул. Примерная, д. 1",
    legal_role=LegalRole.ADVERTISER,
    legal_type=LegalType.UL,
    kpp="771701001",
)

advertisers = client.advertisers.list()

Договоры

Для создания договора удобно использовать ContractBuilder — он проверяет обязательные поля по типу договора и бросает понятное исключение валидации ещё до запроса.

from avito_ads import ContractBuilder
from avito_ads.enums import ContractCounterpartyType

builder = (
    ContractBuilder.intermediary_contract()
    .advertiser(987654321)
    .counterparty_type(ContractCounterpartyType.DIRECT_WITH_ADVERTISER)
    .subject("mediation")
    .object("commercial")
    .reporting_required(True)
    .funds_allocation_to_principal(False)
    .date("2025-01-15")
    .number("ДА-2025/01")
    .intermediary({
        "shortName": "ООО Реклама",
        "longName": "Общество с ограниченной ответственностью «Реклама»",
        "inn": "7712345678",
        "ogrn": "1177746123456",
        "kpp": "771701001",
        "legalAddress": "г. Москва, ул. Примерная, д. 1",
        "actualAddress": "г. Москва, ул. Примерная, д. 1",
        "legalType": "ul",
    })
)

created = client.contracts.create(builder)
print("ID договора:", created.id)

Доступны быстрые конструкторы ContractBuilder.service(), ContractBuilder.intermediary_contract() и ContractBuilder.external("CID"). Дополнительное соглашение создаётся вызовом .parent_id(...) (поле intermediary при этом передавать нельзя).

В create() можно передать и готовый словарь тела запроса — тогда клиентская валидация не выполняется.

contracts = client.contracts.list()

Кампании, группы, креативы

for campaign in client.campaigns.list():
    print(campaign.id, campaign.name, f"[{campaign.status}]")

groups = client.groups.list()
creatives = client.creatives.list()

# Изменение бюджета и ставки группы (только ручное управление ставкой; значение не менее 1)
client.groups.change_budget(group_id=555, budget=100000)
client.groups.change_price(group_id=555, price=25)

Статистика

Период не может превышать 100 дней; даты — в формате YYYY-MM-DD. Эти ограничения проверяются на стороне клиента.

stats = client.statistics.campaign(campaign_id=555, date_from="2025-01-01", date_to="2025-01-31")
total = stats.campaign.total_data
print("Показы:", total.views, "клики:", total.clicks)

by_groups = client.statistics.groups(555, "2025-01-01", "2025-01-31", [101, 102])
by_creatives = client.statistics.creatives(555, "2025-01-01", "2025-01-31", [201, 202])

Пользователи

from avito_ads.enums import UserRole

users = client.users.list()
client.users.add(user_id=42, role=UserRole.ADMIN)
client.users.set_role(user_id=42, role=UserRole.VIEWER)
client.users.delete(user_id=42)

Постраничная выборка

Методы list() возвращают PaginatedResult — по нему можно итерироваться и брать len().

result = client.campaigns.list(limit=50, page=1)

print("Всего:", result.total)
print("Остаток лимита API:", result.api_point_balance)

for campaign in result:
    ...

if result.has_next_page():
    next_page = client.campaigns.list(limit=50, page=2)

Чтобы обойти все страницы автоматически, используйте iterate() — он возвращает генератор и подгружает страницы по мере необходимости:

for campaign in client.campaigns.iterate():
    print(campaign.name)

В асинхронном режиме iterate() возвращает async-генератор:

async for campaign in async_client.campaigns.iterate():
    print(campaign.name)

Фильтры

Фильтры можно задавать словарём или объектом-фильтром с проверкой имён полей.

from avito_ads import CampaignsFilter, DateRange
from avito_ads.enums import CampaignStatus

f = (
    CampaignsFilter()
    .statuses([CampaignStatus.ACTIVE.value])
    .contract_ids([10, 20])
    .created_at(DateRange("2025-01-01", "2025-01-31"))
)

campaigns = client.campaigns.list(f)

Пустой фильтр корректно сериализуется в JSON-объект {}, как того требует API.

Обработка ошибок

Все исключения наследуются от avito_ads.exceptions.AvitoAdsError.

HTTP Исключение
400 BadRequestError
401 AuthenticationError
403 AccessDeniedError
404 NotFoundError
429 RateLimitError (атрибут retry_after)
5xx ServerError
сеть/таймаут NetworkError
from avito_ads.exceptions import ApiError, RateLimitError

try:
    client.account.get_balance()
except RateLimitError as exc:
    retry_after = exc.retry_after
except ApiError as exc:
    print(exc.status_code, exc.error_code, str(exc))

Ошибки валидации на стороне клиента (неверный период статистики, сумма перевода меньше 1, неизвестная роль и т. п.) бросают avito_ads.exceptions.ValidationError.

Лимиты и повторы

API ограничивает частоту запросов (порядка 500 в минуту). Клиент автоматически повторяет запросы при 429 и 5xx с экспоненциальной задержкой и учитывает заголовок Retry-After. Остаток лимита доступен из заголовка Api-Point-Balance (например, PaginatedResult.api_point_balance).

Интеграция с Django

В settings.py:

AVITO_ADS = {
    "client_id": "...",
    "client_secret": "...",
    "account_id": 123456789,
    "environment": "production",
}

Использование:

from avito_ads.integrations.django import get_client

balance = get_client().account.get_balance()

Опционально добавьте "avito_ads.integrations.django" в INSTALLED_APPS, чтобы конфигурация проверялась при старте приложения.

Интеграция с Flask

from flask import Flask
from avito_ads.integrations.flask import AvitoAds

app = Flask(__name__)
app.config.update(
    AVITO_ADS_CLIENT_ID="...",
    AVITO_ADS_CLIENT_SECRET="...",
    AVITO_ADS_ACCOUNT_ID=123456789,
    AVITO_ADS_ENVIRONMENT="production",
)

avito = AvitoAds(app)

@app.route("/balance")
def balance():
    return {"balance": avito.client.account.get_balance().balance}

Поддерживается и фабричный паттерн через AvitoAds().init_app(app).

Интеграция с FastAPI

FastAPI — асинхронный фреймворк, поэтому интеграция отдаёт асинхронный AsyncClient:

from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI
from avito_ads import AsyncClient
from avito_ads.integrations.fastapi import AvitoAdsProvider

avito = AvitoAdsProvider({
    "client_id": "...",
    "client_secret": "...",
    "account_id": 123456789,
    "environment": "production",
})

@asynccontextmanager
async def lifespan(app: FastAPI):
    yield
    await avito.aclose()

app = FastAPI(lifespan=lifespan)

@app.get("/balance")
async def balance(client: AsyncClient = Depends(avito)):
    result = await client.account.get_balance()
    return {"balance": result.balance}

Песочница

Создание тестового аккаунта доступно только в песочнице:

config = Configuration.create(client_id, client_secret, account_id).sandbox()

with Client(config) as client:
    created = client.account.create_sandbox_account(
        inn="7712345678",
        short_name="Тестовый аккаунт",
        long_name="Полное наименование",
        ogrn="1177746123456",
        legal_address="г. Москва, ул. Примерная, д. 1",
        actual_address="г. Москва, ул. Примерная, д. 1",
        contact={"name": "Иван Иванов", "phone": "+79001234567"},
    )

Разработка и тестирование

pip install -e ".[dev]"
pytest            # модульные тесты
ruff check .      # линтер
mypy avito_ads    # проверка типов (strict)

Интеграционные тесты против реальной песочницы по умолчанию пропускаются. Чтобы запустить их:

AVITO_ADS_RUN_INTEGRATION=1 \
AVITO_ADS_CLIENT_ID=... \
AVITO_ADS_CLIENT_SECRET=... \
AVITO_ADS_ACCOUNT_ID=... \
pytest -m integration

Лицензия

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

avito_ads-0.1.0.tar.gz (47.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

avito_ads-0.1.0-py3-none-any.whl (58.5 kB view details)

Uploaded Python 3

File details

Details for the file avito_ads-0.1.0.tar.gz.

File metadata

  • Download URL: avito_ads-0.1.0.tar.gz
  • Upload date:
  • Size: 47.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for avito_ads-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eebbde538435f1946e9f68f50967151d936042023376f62ee9b5404576faca35
MD5 fafd1cc05c80909ea708393ac5124863
BLAKE2b-256 bba16586cede2f1a71f989ab06593b74acdc31ff74071018efa6a8b4df95cbec

See more details on using hashes here.

File details

Details for the file avito_ads-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: avito_ads-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 58.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for avito_ads-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 555c22fb1de222a76edea6bc4b092c7f74fb1a678bccb9af7fd34823cd892c01
MD5 b6ecc74e2cd43829b02b3e220d5088ce
BLAKE2b-256 ff9b4905971b4af19d859b45f4b8eed894a06663254cb86fe5a352f14ee0a302

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page