Разработчикам

iOS 16+ Swift 6

Интеграция iOS SDK

Подключите Melvad к iOS-приложению, настройте рекламные места и управляйте загрузкой и показом из Swift. Ниже — общий цикл интеграции и примеры для каждого поддерживаемого формата.

Инструкция для Melvad iOS SDK 1.0.2. Подключите бинарный пакет через Swift Package Manager или скачайте готовую поставку по ссылке ниже.

Другие интеграции: Синхронизация кук Форматы OpenRTB

1. Подготовьте приложение

  1. Зарегистрируйтесь в Melvad и создайте инвентарь типа «Приложение» для iOS. В карточке приложения укажите его Bundle ID и ссылку на настоящую страницу в App Store.
  2. Создайте рекламные места для нужных форматов. Получите inventoryID и placementID из настроек или тега размещения. Для разных форматов используйте соответствующие им рекламные места.
  3. Получите у команды Melvad числовой clientID. Production-адрес https://pub.supplier.melvad.com уже задан в SDK по умолчанию. SDK подключается из GitHub на следующем шаге; для тестовой среды адрес можно переопределить.

Требуются iOS 16+ и Xcode с поддержкой Swift 6. Примеры используют UIKit. В SwiftUI размещайте рекламный UIView через UIViewRepresentable и удерживайте владельца сессии в coordinator или модели экрана.

Bundle ID и App Store URL в конфигурации SDK должны соответствовать приложению, указанному в инвентаре. Данные приложения для подбора рекламы задаются в настройках инвентаря.

2. Подключите SDK

  1. В Xcode откройте File → Add Package Dependencies.
  2. Введите адрес пакета: https://github.com/Melvad/ios-ads-sdk.
  3. Выберите Exact Version: 1.0.2 и добавьте в target приложения оба продукта: MelvadAdsCore и MelvadAdsRuntime.
  4. Swift Package Manager загрузит XCFramework для устройства и симулятора вместе с ресурсами. Prebid уже включён в поставку; отдельно вторую копию подключать не нужно.

Для локального подключения клонируйте репозиторий SDK и выберите его корневой каталог с Package.swift через Add Local…. Готовые XCFramework находятся в папке Frameworks. Сохраняйте структуру каталогов, ресурсы и лицензии.

Для подключения без загрузки пакета из Git скачайте MelvadAdsSDK 1.0.2, распакуйте архив и добавьте каталог с Package.swift через Add Local…. Сохраняйте все бинарные файлы и resource bundles вместе. Контрольные суммы SHA-256 приложены к релизу.

MelvadAdsTracking — опциональный продукт. Добавляйте его, если приложению нужны рекламные идентификаторы при разрешённом ATT; настройка описана в разделе privacy.

Условия использования: лицензия SDK.

3. Настройте запросы

Создайте конфигурацию один раз для каждого маршрута: .display для баннеров и native, .video для видео и rewarded. Передайте в функцию выданные Melvad значения и App Store URL вашего приложения.

По умолчанию используется production-адрес https://pub.supplier.melvad.com; аргумент supplierURL можно не передавать. Для тестовой среды задайте его явно. При прямом создании SupplierConfiguration такое же значение по умолчанию применяется к baseURL.

import UIKit
import WebKit
import MelvadAdsCore
import MelvadAdsRuntime

enum IntegrationError: Error {
    case missingBundleID, missingUserAgent
}

@MainActor
func makeConfiguration(
    supplierURL: URL = SupplierConfiguration.productionBaseURL,
    clientID: Int64,
    appStoreURL: URL,
    route: SupplierConfiguration.Route,
    identityProvider: (any AdvertisingIdentityProvider)? = nil
) async throws -> SupplierConfiguration {
    guard let bundleID = Bundle.main.bundleIdentifier else {
        throw IntegrationError.missingBundleID
    }
    let webView = WKWebView(frame: .zero)
    guard let userAgent = try await webView.evaluateJavaScript(
        "navigator.userAgent"
    ) as? String, !userAgent.isEmpty else {
        throw IntegrationError.missingUserAgent
    }
    return try SupplierConfiguration(
        baseURL: supplierURL,
        clientID: clientID,
        bundleID: bundleID,
        storeURL: appStoreURL,
        userAgent: userAgent,
        language: Locale.preferredLanguages.first,
        route: route,
        identityProvider: identityProvider
    )
}

supplierURL — базовый адрес без /get, /video, query-параметров и идентификатора клиента. SDK сам добавляет /get/{clientID} или /video/{clientID}. Используйте HTTPS; отключать ATS для этой интеграции не требуется.

Далее displayConfiguration и videoConfiguration — результаты этой функции с соответствующим route. Вызывайте её через try await из @MainActor и обрабатывайте ошибку настройки.

4. Передайте согласия пользователя

Приложение управляет CMP и запросом ATT; SDK не показывает системный диалог. Создайте PrivacyContext по фактическим настройкам пользователя, сохраняя неизвестные значения неизвестными.

По умолчанию MelvadAdsCore и MelvadAdsRuntime не читают IDFA/IDFV. Для запросов SDK приложению не требуется подключать AdSupport или AppTrackingTransparency.

import MelvadAdsCore

@MainActor
func makePrivacy(
    gdprApplies: Bool?,
    consentString: String?,
    personalizationAllowed: Bool,
    globalPrivacyControl: Bool
) -> PrivacyContext {
    PrivacyContext(
        personalizationAllowed: personalizationAllowed,
        gdprApplies: gdprApplies,
        consentString: consentString,
        globalPrivacyControl: globalPrivacyControl
    )
}

Если нужен сбор рекламных идентификаторов, добавьте продукт MelvadAdsTracking и передайте SystemAdvertisingIdentityProvider() при создании конфигурации:

import MelvadAdsTracking

// Inside a @MainActor method, after your app has handled consent:
let configuration = try await makeConfiguration(
    clientID: clientID,
    appStoreURL: appStoreURL,
    route: .display,
    identityProvider: SystemAdvertisingIdentityProvider()
)

Provider перечитывает ATT перед каждым запросом. Идентификаторы доступны только при ATT authorized, personalizationAllowed == true, явно заданном gdprApplies == false и globalPrivacyControl == false. При denied, restricted и notDetermined не отправляется ни IDFA, ни IDFV. IDFV не используется для обхода отказа в ATT.

Если приложение запрашивает ATT, задайте NSUserTrackingUsageDescription и используйте API Apple. Сохраняйте privacy manifests SDK в сборке и проверяйте Privacy Report приложения с учётом подключённых партнёров спроса.

Отсутствие обоих идентификаторов не блокирует запрос. После изменения согласий вызовите updatePrivacy: текущая загрузка отменится, подготовленный креатив будет удалён. Новую загрузку запускает приложение.

5. Создайте владельца рекламного места

Общий порядок: load → ready → show. Для fullscreen вместо show() можно использовать present(from:). Загрузка сама по себе не означает показ. Все вызовы и callbacks выполняются на MainActor.

Ниже — небольшой класс приложения поверх реального API SDK. Он удерживает сессию, обрабатывает клики и вызывает onReady только после загрузки: обычный hide() не запускает показ повторно.

Полный код AdSlotController.swift
import UIKit
import MelvadAdsCore
import MelvadAdsRuntime

@MainActor
final class AdSlotController: NSObject, MelvadAdSessionDelegate {
    let session: MelvadAdSession
    var onReady: ((MelvadAdSession) -> Void)?
    var onUnavailable: (() -> Void)?
    var onError: ((NSError) -> Void)?
    var onEvent: ((String) -> Void)?
    private var awaitingLoad = false

    init(configuration: SupplierConfiguration, privacy: PrivacyContext) {
        session = MelvadAdSession(configuration: configuration)
        super.init()
        session.updatePrivacy(privacy)
        session.delegate = self
    }

    func load(in slot: UIView, inventoryID: String,
              placementID: String, mode: String? = nil) {
        slot.isHidden = false
        slot.superview?.layoutIfNeeded()
        guard slot.window != nil,
              slot.bounds.width >= 1, slot.bounds.height >= 1 else {
            onError?(NSError(domain: "MelvadIntegration", code: 1,
                userInfo: [NSLocalizedDescriptionKey:
                    "Attach the ad slot to a window and set its size before loading."]))
            return
        }
        awaitingLoad = true
        session.load(in: slot, inventoryID: inventoryID,
                     placementID: placementID, mode: mode,
                     width: Int(slot.bounds.width),
                     height: Int(slot.bounds.height))
    }

    func adSession(_ session: MelvadAdSession,
                   didChange state: MelvadAdSessionState) {
        switch state {
        case .ready where awaitingLoad:
            awaitingLoad = false
            onReady?(session)
        case .noFill, .destroyed:
            awaitingLoad = false
            onUnavailable?()
        case .idle, .failed:
            awaitingLoad = false
        default:
            break
        }
    }

    func adSession(_ session: MelvadAdSession, didFail error: NSError) {
        onError?(error)
    }

    func adSession(_ session: MelvadAdSession, didReceiveEvent event: String) {
        onEvent?(event)
    }

    func adSession(_ session: MelvadAdSession, didClick url: URL) {
        UIApplication.shared.open(url)
    }
}

Сохраните этот класс в проекте. В своём UIViewController удерживайте владельцев рекламных мест как свойства: SDK хранит delegate слабо. Один объект обслуживает одно размещение.

// Properties of your UIViewController, not local variables:
private var displayAd: AdSlotController?
private var fullscreenAd: AdSlotController?
private var videoAd: AdSlotController?

В следующих примерах privacy — ваш PrivacyContext, идентификаторы получены из Melvad, а slot — UIView с заданными размерами, уже добавленный в окно. Запускайте загрузку после layout, например однократно в viewDidAppear. Не вызывайте её на каждом viewDidLayoutSubviews.

Поддерживаемые форматы

ФорматМаршрутModeПоказ
Баннер / MREC.displaynilshow()
Полноэкранный баннер.display"fullscreen"present(from:)
Закреплённый / раскрывающийся баннер.displaynilshow()
Native image / native video.displaynilshow()
Inread / infeed / inslide.video"inread" / "infeed" / "inslide"show()
Полноэкранное видео.video"fullscreen"show() / present(from:)
Rewarded.video"rewarded"present(from:)
Pre- / mid- / post-roll.videonilMelvadAdBreakController

Шаблон и креатив выбирает сервер по настройкам рекламного места. mode передаёт режим показа и не превращает обычное баннерное размещение в native или video. Размеры в примерах — в UIKit points, а не физических пикселях.

6. Баннер и MREC

Используйте displayConfiguration и баннерное рекламное место. Задайте слоту размер, согласованный в настройках размещения: например, 320 × 50 для баннера или 300 × 250 для MREC. Контейнер не должен обрезать креатив.

// Inside your UIViewController, create the banner slot once:
let slot = UIView()
slot.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(slot)
NSLayoutConstraint.activate([
    slot.widthAnchor.constraint(equalToConstant: 320),
    slot.heightAnchor.constraint(equalToConstant: 50),
    slot.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    slot.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
// Retain this view and load after the screen appears and layout completes.

После появления экрана и расчёта размеров загрузите объявление в созданный слот:

// Inside a method of your UIViewController:
displayAd?.session.destroy()
let ad = AdSlotController(configuration: displayConfiguration, privacy: privacy)
displayAd = ad
ad.onReady = { $0.show() }
ad.onUnavailable = { [weak slot] in slot?.isHidden = true }
ad.onError = { [weak slot] error in
    slot?.isHidden = true
    print(error.domain, error.code, error.localizedDescription)
}
ad.load(in: slot, inventoryID: inventoryID, placementID: placementID)

При noFill уберите пустой слот и продолжите работу приложения. Для UIStackView удобно использовать arrangedSubview; для table/collection view обновляйте высоту ячейки самостоятельно.

7. Полноэкранный баннер

Создайте display-размещение с полноэкранным шаблоном. Подготовьте объявление в UIView текущего экрана; его ширина и высота должны быть положительными. После .ready показывайте рекламу в подходящей точке перехода интерфейса.

// Prepare a fullscreen banner using displayConfiguration:
fullscreenAd?.session.destroy()
let ad = AdSlotController(configuration: displayConfiguration, privacy: privacy)
fullscreenAd = ad
ad.onError = { error in
    print(error.domain, error.code, error.localizedDescription)
}
ad.load(in: slot, inventoryID: inventoryID,
        placementID: placementID, mode: "fullscreen")
// A method of your UIViewController; call at an appropriate UI transition:
func showPreparedFullscreen() {
    guard let session = fullscreenAd?.session,
          session.state == .ready,
          view.window != nil,
          presentedViewController == nil else { return }
    do {
        try session.present(from: self)
    } catch {
        print(error.localizedDescription)
    }
}

Показывайте из той же scene, в которой находится исходный контейнер. Одновременно в scene доступен один fullscreen-показ. Закрытие пользователем уничтожает сессию; для следующего объявления создайте новую.

8. Закреплённый баннер и раскрытие

Для adhesion и adhesion fullscreen используйте .display и предназначенное для них рекламное место. Отдельное значение mode: "adhesion" не требуется. SDK размещает баннер у нижней safe area; приложение оставляет под ним отступ для контента.

// Use a placement configured for adhesion or expandable adhesion.
displayAd?.session.destroy()
let ad = AdSlotController(configuration: displayConfiguration, privacy: privacy)
displayAd = ad
ad.onReady = { $0.show() }
ad.onUnavailable = { [weak slot] in slot?.isHidden = true }
ad.onError = { [weak slot] error in
    slot?.isHidden = true
    print(error.localizedDescription)
}
ad.load(in: slot, inventoryID: inventoryID, placementID: adhesionPlacementID)

Раскрытие на весь экран доступно, если его разрешают настройки supplier и MRAID-креатив. Оно запускается действием пользователя. Закрытие раскрытого объявления возвращает тот же баннер; закрытие самого баннера завершает сессию.

Поддерживаются команды MRAID для открытия ссылок, видимости, геометрии, expand, close и playVideo. В текущем профиле resize() означает раскрытие на весь экран; two-part expansion и принудительная ориентация не поддерживаются.

9. Native image и native video

Оба формата используют displayConfiguration. Выберите рекламное место для native image или native video. SDK сам строит карточку из ответа; вручную запрашивать и собирать assets не нужно.

// Use the placement ID for native image OR native video.
displayAd?.session.destroy()
let ad = AdSlotController(configuration: displayConfiguration, privacy: privacy)
displayAd = ad
ad.onReady = { $0.show() }
ad.onUnavailable = { [weak nativeSlot] in nativeSlot?.isHidden = true }
ad.onError = { [weak nativeSlot] error in
    nativeSlot?.isHidden = true
    print(error.localizedDescription)
}
ad.load(in: nativeSlot, inventoryID: inventoryID, placementID: nativePlacementID)

Размещайте nativeSlot внутри ленты или статьи, с обычным контентом до и после него. Для карточки задайте достаточную высоту; в примере SDK используется 460 pt. Native показывается через show(); present(from:) для него не поддерживается.

Native video начинает воспроизведение без звука после разрешения показа и при видимости не менее 50%. За пределами экрана и в фоне оно приостанавливается. После завершения уберите слот. Текущий native-video путь поддерживает один VAST InLine с линейным MP4-креативом; цепочки VAST wrappers для него не заявлены.

10. Inread, infeed, inslide и fullscreen video

Используйте videoConfiguration и видеорекламное место. Метод ниже загружает объявление и разрешает показ после готовности. Выберите один из четырёх режимов.

ModeПоведение
.inreadВ статье; воспроизведение при видимости от 50%.
.infeedВ ленте; старт после 1 секунды непрерывной видимости от 50%, пауза за экраном.
.inslideВыезжающий блок: снизу на iPhone, справа на iPad.
.fullscreenПолноэкранный видеопоказ с элементами управления SDK.
// A method of your UIViewController:
func loadVideo(in slot: UIView, configuration: SupplierConfiguration,
               privacy: PrivacyContext, inventoryID: String,
               placementID: String, mode: MelvadVideoMode) {
    videoAd?.session.destroy()
    let ad = AdSlotController(configuration: configuration, privacy: privacy)
    videoAd = ad
    ad.onReady = { $0.show() }
    ad.onUnavailable = { [weak slot] in slot?.isHidden = true }
    ad.onError = { [weak slot] error in
        slot?.isHidden = true
        print(error.localizedDescription)
    }
    ad.load(in: slot, inventoryID: inventoryID, placementID: placementID,
            mode: mode.supplierValue)
}

// Call from your screen after layout; choose one mode for this placement:
// loadVideo(in: slot, configuration: videoConfiguration, privacy: privacy,
//           inventoryID: inventoryID, placementID: placementID, mode: .inread)
// Other modes: .infeed, .inslide, .fullscreen

Для inread/infeed подготовьте слот внутри статьи или ленты, обычно с пропорциями 16:9. Для inslide и fullscreen исходный UIView также должен находиться в окне. При fullscreen на экране не должно быть другой открытой модали. Если нужно разделить подготовку и момент показа, не вызывайте show() в onReady: вызовите его позже из состояния .ready.

11. Видео за вознаграждение

Используйте videoConfiguration, отдельное rewarded-размещение и строку mode: "rewarded". Значение rewarded не входит в enum MelvadVideoMode. Пользователь должен выбрать просмотр до запуска показа.

// Call after the user opts in to watch a rewarded ad:
fullscreenAd?.session.destroy()
let ad = AdSlotController(configuration: videoConfiguration, privacy: privacy)
fullscreenAd = ad
ad.onReady = { [weak self] _ in self?.showPreparedFullscreen() }
ad.onUnavailable = { print("Rewarded ad unavailable or closed") }
ad.onError = { error in print(error.localizedDescription) }
ad.onEvent = { event in
    if event == "AdVideoComplete" {
        print("Video completed; this is not a verified reward")
    }
}
ad.load(in: slot, inventoryID: inventoryID,
        placementID: rewardedPlacementID, mode: "rewarded")

Подтверждение награды. AdVideoComplete сообщает только о завершении видео. В текущей поставке нет готового контракта SSV для серверной проверки наград. Не начисляйте подтверждённую награду по этому callback; до запуска монетизации с наградами согласуйте серверный контракт с Melvad.

12. Pre-roll, mid-roll и post-roll

Внешний плеер воспроизводит основной контент, а MelvadAdBreakController ставит его на паузу и показывает рекламу. Для AVPlayer добавьте адаптер; для другого плеера реализуйте те же два метода.

import AVFoundation
import MelvadAdsRuntime

@MainActor
final class ContentAdapter: MelvadContentPlayback {
    let player: AVPlayer
    init(player: AVPlayer) { self.player = player }
    func playContent() { player.play() }
    func pauseContent() { player.pause() }
}

Добавьте рекламный UIView поверх области контентного плеера. После layout создайте контроллер с videoConfiguration и instream-размещением. Пример показывает pre-roll, один mid-roll на 30-й секунде и post-roll.

// Properties of your UIViewController:
private var contentAdapter: ContentAdapter?
private var adBreaks: MelvadAdBreakController?

// Inside a method, after laying out the ad overlay inside the content player:
adBreaks?.destroy()
let adapter = ContentAdapter(player: contentPlayer)
contentAdapter = adapter
let breaks = MelvadAdBreakController(
    content: adapter,
    container: adOverlay,
    configuration: videoConfiguration,
    inventoryID: inventoryID,
    placementID: instreamPlacementID,
    midrollTime: 30,
    privacy: privacy
)
adBreaks = breaks
breaks.onEvent = { print($0) }
breaks.start()

Подключите периодический callback времени вашего плеера и событие окончания текущего контентного элемента. У AVPlayer это addPeriodicTimeObserver и AVPlayerItem.didPlayToEndTimeNotification. Передавайте события на MainActor и снимайте observers при освобождении экрана.

// Forward player callbacks on MainActor:
adBreaks?.contentTimeDidChange(contentPlayer.currentTime().seconds)
// Call the line above on periodic playback updates, not just once.

// When the current content item finishes:
adBreaks?.contentDidFinish()

// When the user's privacy settings change:
adBreaks?.updatePrivacy(updatedPrivacy)

// Before leaving the screen; also remove the player's time/end observers:
adBreaks?.destroy()
adBreaks = nil
contentAdapter = nil

Pre-/mid-/post-roll — точки вставки, а не значения mode. Для каждой паузы контроллер выполняет отдельный video-запрос с указанным placement. No-fill или ошибка возвращают управление контенту; после post-roll контент не запускается заново. Для нового видео или повторного воспроизведения создайте новый контроллер.

13. Жизненный цикл и проверка интеграции

// Pause and resume the SAME inline creative:
displayAd?.session.hide()
// Later, when the slot should be visible again:
displayAd?.session.show()

// After a privacy change, discard the prepared creative:
displayAd?.session.updatePrivacy(updatedPrivacy)
// Start a new load explicitly with the new privacy context.

// Before removing a slot, reusing a cell, or leaving the screen:
displayAd?.session.destroy()
displayAd = nil
videoAd?.session.destroy()
videoAd = nil
fullscreenAd?.session.destroy()
fullscreenAd = nil
  • destroy() — окончательная операция. После неё создайте новую сессию. Новый load в живой сессии заменяет предыдущую загрузку и объявление.
  • Готовый креатив хранится не более 120 секунд; Prebid может задать меньший срок. Если он истёк, загрузите новое объявление.
  • Повторный show() после inline hide() использует тот же креатив. Закрытие модального видео уничтожает его сессию.
  • SDK управляет impression, billing и VAST trackers. Не отправляйте их повторно из приложения.
  • На физическом устройстве проверьте получение каждого формата, клики, закрытие, возврат из фона, прокрутку, повторное использование ячеек и изменение согласий.
РезультатЧто проверить
noFillШтатное отсутствие объявления. Проверьте активность и формат placement, настройки спроса; уберите пустое место.
failedСохраните domain/code из NSError. Проверьте HTTPS-адрес, clientID, inventory/placement, сеть и конфигурацию приложения.
Есть ready, но нет показаВызван ли show/present? Контейнер в окне, имеет размер и виден? Приложение в foreground?
Fullscreen не открываетсяТа же scene, нет другой модали или полноэкранной рекламы, креатив не истёк. Для native нужен inline-показ.

Автоматического повторного аукциона нет. Если нужен повтор, планируйте его с ограничением частоты; не запускайте бесконечный load() из callback ошибки. Проверка примеров с тестовыми ответами не гарантирует наличие рекламного спроса в конкретном размещении.

Для помощи напишите на support@melvad.com: укажите версию SDK, iOS, формат, inventory/placement и шаги воспроизведения. Перед отправкой логов удалите токены, идентификаторы устройства и данные согласий.

14. Нативный Open Measurement

Версия 1.0.2 включает нативный OMSDK_Melvad 1.6.12. Runtime управляет сессией измерения и событиями объявления/видео для display и VAST, включая pre-roll, mid-roll и post-roll. Дополнительная инициализация OMID в приложении не нужна.

Не подключайте второй web OMID Service/Session Client и не отправляйте события измерения того же креатива вручную. Ресурсы проверки VAST берутся из AdVerifications; без подходящих ресурсов видео продолжает воспроизведение без OMID-сессии. Наличие интеграции не означает сертификацию IAB.

15. Логирование SDK и ошибки

Логирование по умолчанию выключено. Выберите минимальный уровень debug, info, warning или error и задайте обработчик:

import MelvadAdsCore

MelvadLogger.configure(minimumLevel: .debug) { entry in
    print("[\(entry.subsystem)] \(entry.message)")
}

// Disable SDK logging:
MelvadLogger.configure()

Swift-обработчик выполняется в потоке, который создал запись; работу с UI переносите на MainActor. Для Objective-C доступен MelvadDiagnostics.configure(minimumLevel:handler:) с callback на MainActor. SDK записывает состояния и статусы, без creative markup, значений consent и идентификаторов устройства.

В adSession(_:didFail:) проверяйте NSError.localizedDescription, domain, code и NSUnderlyingErrorKey. Исходная сетевая ошибка сохраняется. noFill остаётся штатным результатом, а не ошибкой.

Вернуться к началу руководства

Android 10+ · API 29+ · SDK 1.0.2

Интеграция Android SDK

Подключите Melvad к Android-приложению, настройте рекламные места и управляйте загрузкой, показом и жизненным циклом из Kotlin.

Инструкция использует публичную бинарную поставку Melvad Android Ads SDK 1.0.2. Рендереры входят в SDK, а необходимые нативные зависимости устанавливает Gradle.

Нативный Open Measurement (OMID) входит в SDK и не требует дополнительной настройки в приложении.

Другие интеграции: iOS SDK · Синхронизация кук · Форматы OpenRTB

1. Подготовьте приложение

Зарегистрируйте Android-приложение в Melvad и укажите имя пакета и страницу в Google Play. Создайте места для нужных форматов. Получите ключ вида inventory_id#placement_id#client_id; последняя часть — числовой ID клиента. SDK автоматически получает имя пакета приложения и формирует его Google Play URL.

Нужны Android 10/API 29+, compile SDK 37+, компилятор, совместимый с Kotlin 2.2.10, и Android System WebView с поддержкой WebMessageListener. Используйте JDK, поддерживаемый вашим Android Gradle Plugin. Создавайте view и вызывайте методы SDK в главном потоке Android.

2. Установите бинарный SDK

Добавьте репозиторий в dependencyResolutionManagement.repositories файла settings.gradle.kts, сохранив существующие репозитории:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/Melvad/android-ads-sdk/1.0.2/maven")
            content { includeGroup("com.melvad") }
        }
    }
}

Добавьте зависимость в build.gradle.kts модуля приложения и установите minSdk = 29 или выше:

dependencies {
    implementation("com.melvad:android-sdk:1.0.2")
}

Gradle установит AAR, нативный Prebid Mobile и остальные зависимости. Учётная запись GitLab и сборка исходников не нужны. Подключение только локального AAR не устанавливает транзитивные зависимости. Доступны бинарный релиз и контрольные суммы и лицензия SDK.

3. Передайте решение CMP

Получите решение CMP издателя до вызова loadAd. Передайте фактический признак применимости GDPR и TC string: SDK не определяет разрешение на рекламный запрос. Вызовите функцию с уже определёнными значениями:

fun privacySnapshot(
    gdprApplies: Boolean,
    tcString: String,
    advertisingIdAllowed: Boolean
): MelvadPrivacy = MelvadPrivacy(
    gdprApplies = gdprApplies,
    tcString = tcString,
    limitAdTracking = !advertisingIdAllowed
)

limitAdTracking управляет использованием рекламного ID и не заменяет решение приложения о допустимости рекламного запроса. По умолчанию использование рекламного ID ограничено. SDK использует ID только при разрешающих настройках приложения и платформы; отсутствие ID не блокирует загрузку. После изменения согласия создайте новый снимок privacy и выполните новую загрузку.

4. Создайте и сохраните рекламный view

Создайте один клиент MelvadSdk(activity) для приложения или экрана. Функция ниже добавляет view, ожидает layout и выполняет один запрос. Передайте клиент, Activity, контейнер места, ключ и полученный снимок privacy. Сохраняйте возвращённый view до его освобождения.

import android.app.Activity
import android.util.Log
import android.view.ViewGroup
import com.melvad.sdk.*

fun attachAd(
    sdk: MelvadSdk,
    activity: Activity,
    host: ViewGroup,
    placementKey: String,
    privacy: MelvadPrivacy,
    format: MelvadAdFormat = MelvadAdFormat.BANNER,
    videoOptions: MelvadVideoOptions = MelvadVideoOptions(),
    instream: MelvadInstreamOptions? = null
): MelvadAdView {
    val view = sdk.createAdView(activity)
    val fullSize = format in setOf(
        MelvadAdFormat.INTERSTITIAL, MelvadAdFormat.FULLSCREEN,
        MelvadAdFormat.REWARDED, MelvadAdFormat.INSTREAM
    )
    host.addView(view, ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        if (fullSize) ViewGroup.LayoutParams.MATCH_PARENT
        else ViewGroup.LayoutParams.WRAP_CONTENT
    ))
    view.listener = object : MelvadAdListener {
        override fun onLoaded(view: MelvadAdView) {
            Log.d("Melvad", "Creative loaded")
        }
        override fun onImpression(view: MelvadAdView) {
            Log.d("Melvad", "Visible impression")
        }
        override fun onError(view: MelvadAdView, error: MelvadAdError) {
            Log.e("Melvad", "Ad result: " + error.code)
        }
    }
    view.post {
        if (view.parent === host && view.state != MelvadAdState.DESTROYED) {
            view.loadAd(MelvadAdRequest(
                placement = MelvadPlacement.parse(placementKey),
                format = format,
                privacy = privacy,
                videoOptions = videoOptions,
                instream = instream
            ))
        }
    }
    return view
}

onLoaded означает готовность креатива. onImpression — отдельное событие видимого показа. SDK отправляет трекеры самостоятельно: не дублируйте запросы impression, billing или VAST из приложения.

Поддерживаемые форматы

ФорматМесто и контейнерПоведение
BANNER22 / 23Фиксированный / inline-баннер
EXPANDABLE22, intsl=1Inline-баннер с раскрытием на весь экран
INTERSTITIAL20, intsl=1Полноэкранный HTML-баннер
ADHESION21Нижний контейнер вне прокрутки
NATIVE10 / 11Inline-карточка с изображением или видео
VIDEOВидео-местоInline-контейнер для видео
INREADВидео-местоРаскрывающийся inline-контейнер, WRAP_CONTENT
INSLIDEВидео-местоНижний контейнер вне прокрутки
INFEEDВидео-местоПостоянная inline-карточка
INSTREAMВидео-местоОверлей поверх контентного плеера
FULLSCREENВидео-местоВидео-контейнер размером с окно; без награды
REWARDEDВидео-местоВидео-контейнер размером с окно; награда за полный просмотр

5. Баннеры фиксированного размера и раскрывающиеся баннеры

Для BANNER вызовите attachAd с форматом по умолчанию. Для раскрывающегося inline-баннера передайте MelvadAdFormat.EXPANDABLE и соответствующее место. Размер фиксированного баннера задаётся в настройках места. Остальные баннерные форматы используют поведение шаблона в рендерере.

Раскрывающемуся view нужен Activity context. Используйте onExpanded/onCollapsed, isExpanded и при необходимости collapse(). Шаблон 22 возвращается в исходное место без второй загрузки. Закрытие раскрытого adhesion закрывает всё размещение.

6. Полноэкранные баннеры, видео и rewarded

Используйте контейнер размером с окно с учётом системных панелей и вырезов экрана. INTERSTITIAL — полноэкранный HTML-баннер, FULLSCREEN — видео. Запускайте REWARDED после выбора пользователя посмотреть рекламу за награду. Пример создаёт и освобождает полноэкранный диалог:

fun showFullscreenAd(
    sdk: MelvadSdk,
    activity: Activity,
    placementKey: String,
    privacy: MelvadPrivacy,
    format: MelvadAdFormat,
    onReward: () -> Unit = {}
): android.app.Dialog {
    require(format in setOf(
        MelvadAdFormat.INTERSTITIAL,
        MelvadAdFormat.FULLSCREEN,
        MelvadAdFormat.REWARDED
    ))
    val dialog = android.app.Dialog(
        activity, android.R.style.Theme_Material_Light_NoActionBar
    )
    val host = android.widget.FrameLayout(activity)
    host.fitsSystemWindows = true
    dialog.setContentView(host)
    val view = attachAd(sdk, activity, host, placementKey, privacy, format)
    view.listener = object : MelvadAdListener {
        override fun onStateChanged(view: MelvadAdView, state: MelvadAdState) {
            if (state == MelvadAdState.CLOSED) dialog.dismiss()
        }
        override fun onError(view: MelvadAdView, error: MelvadAdError) {
            dialog.dismiss()
        }
        override fun onRewardEarned(view: MelvadAdView) { onReward() }
    }
    dialog.setOnDismissListener { view.destroy() }
    dialog.show()
    dialog.window?.setLayout(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    )
    return dialog
}

Выдавайте награду только в onRewardEarned. SDK вызывает его один раз после видимого и полностью просмотренного rewarded-видео. Раннее закрытие, no-fill и ошибки не дают награду. Сохраняйте диалог и закрывайте его при уничтожении Activity.

7. Adhesion и inslide

Разместите нижний контейнер вне прокручиваемого контента. Корневой layout приложения должен учитывать системные панели, клавиатуру и другие оверлеи. Функция по умолчанию использует ADHESION; для выезжающего видео передайте INSLIDE и видео-место. Рендерер добавляет кнопку закрытия и сообщает нужную высоту нативного контейнера.

fun loadDockedAd(
    sdk: MelvadSdk,
    activity: Activity,
    root: android.widget.FrameLayout,
    placementKey: String,
    privacy: MelvadPrivacy,
    format: MelvadAdFormat = MelvadAdFormat.ADHESION
): MelvadAdView {
    require(format in setOf(MelvadAdFormat.ADHESION, MelvadAdFormat.INSLIDE))
    val dock = android.widget.FrameLayout(activity)
    root.addView(dock, android.widget.FrameLayout.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.WRAP_CONTENT,
        android.view.Gravity.BOTTOM
    ))
    return attachAd(sdk, activity, dock, placementKey, privacy, format)
}

8. Нативные карточки с изображением и видео

Используйте NATIVE с местом для карточки с изображением или видео. Место определяет шаблон 10 или 11 и тему. Это HTML-карточки внутри SDK, а не набор assets для сборки Android Views. Задайте корректную ширину контейнера и разрешите SDK обновлять его высоту.

fun loadNativeCard(
    sdk: MelvadSdk, activity: Activity, host: ViewGroup,
    placementKey: String, privacy: MelvadPrivacy
): MelvadAdView = attachAd(
    sdk, activity, host, placementKey, privacy, MelvadAdFormat.NATIVE
)

9. Inread, infeed и inline video

Для INREAD, INFEED и VIDEO используйте inline-контейнер с высотой WRAP_CONTENT. Inread раскрывается при видимости, infeed сохраняет карточку/постер и поддерживает повторную попытку. Для inslide используйте пример нижнего контейнера выше. Нативная видимость и жизненный цикл приложения управляют воспроизведением.

fun loadInlineVideo(
    sdk: MelvadSdk, activity: Activity, host: ViewGroup,
    placementKey: String, privacy: MelvadPrivacy,
    format: MelvadAdFormat, posterUrl: String? = null
): MelvadAdView {
    require(format in setOf(
        MelvadAdFormat.VIDEO, MelvadAdFormat.INREAD, MelvadAdFormat.INFEED
    ))
    return attachAd(sdk, activity, host, placementKey, privacy, format,
        videoOptions = MelvadVideoOptions(
            posterUrl = posterUrl,
            refreshSeconds = 0,
            closeButton = format == MelvadAdFormat.INFEED
        )
    )
}

Для восстанавливаемой ошибки infeed используйте onVideoError/videoState и вызывайте retryVideo() по явному действию повторной попытки. Для включения звука после действия пользователя есть setVideoMuted(false). HTTPS URL постера необязателен. Нулевой refreshSeconds означает одно объявление для inread/inslide.

10. Instream: pre-roll, mid-roll и post-roll

Разместите INSTREAM view поверх контентного плеера на всю его область. Приложение управляет воспроизведением контента. Подключите оба callback паузы/возобновления и передавайте таймлайн. Пример задаёт pre-roll, mid-roll на 30-й секунде и post-roll:

fun loadInstreamAd(
    sdk: MelvadSdk, activity: Activity, overlay: ViewGroup,
    placementKey: String, privacy: MelvadPrivacy,
    pauseContent: () -> Unit, resumeContent: () -> Unit
): MelvadAdView {
    val options = MelvadInstreamOptions(breaks = listOf(
        MelvadAdBreak("pre", MelvadAdBreakPosition.PREROLL),
        MelvadAdBreak("mid-30", MelvadAdBreakPosition.MIDROLL, offsetMillis = 30_000),
        MelvadAdBreak("post", MelvadAdBreakPosition.POSTROLL)
    ))
    return attachAd(sdk, activity, overlay, placementKey, privacy,
        MelvadAdFormat.INSTREAM, instream = options
    ).apply {
        listener = object : MelvadAdListener {
            override fun onContentPauseRequested(view: MelvadAdView) { pauseContent() }
            override fun onContentResumeRequested(view: MelvadAdView) { resumeContent() }
            override fun onVideoError(view: MelvadAdView, error: MelvadAdError) {
                Log.e("Melvad", "Video opportunity: " + error.code)
            }
        }
    }
}

Получив adView из функции, передавайте реальные события плеера в главном потоке. Позиция задаётся в миллисекундах, а не секундах:

// When prepared content is started by the user:
adView.startContent()
// On every content-player time/seek/playback update, using milliseconds:
adView.updateContent(positionMillis, durationMillis, isPlaying)
// When the current content item ends:
adView.updateContent(durationMillis, durationMillis, false, ended = true)

Для расписания VMAP используйте MelvadInstreamOptions(vmapUrl = yourHttpsVmapUrl) вместо breaks. Не передавайте оба варианта. Пауза может содержать собственный MelvadPlacement; иначе используется место основного запроса. No-fill или восстанавливаемая ошибка возвращает управление контенту и сохраняет следующие паузы.

11. Жизненный цикл и диагностика

View с ViewTreeLifecycleOwner следует его жизненному циклу. В обычной Activity или другом host передавайте pause() и resume(). Вызывайте destroy() перед окончательным удалением места, повторным использованием ячейки или уходом с экрана. После destroy нужен новый view. Полный пример Activity владеет одним баннером и не отправляет автоматические запросы:

import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.widget.FrameLayout
import com.melvad.sdk.*

class AdsActivity : Activity() {
    private lateinit var sdk: MelvadSdk
    private lateinit var host: FrameLayout
    private var ad: MelvadAdView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sdk = MelvadSdk(this)
        host = FrameLayout(this)
        setContentView(host)
    }

    // Call from your CMP callback after the app permits an ad request.
    fun showBanner(placementKey: String, privacy: MelvadPrivacy) {
        ad?.destroy()
        host.removeAllViews()
        val view = sdk.createAdView(this)
        ad = view
        host.addView(view, FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ))
        view.listener = object : MelvadAdListener {
            override fun onLoaded(view: MelvadAdView) {
                Log.d("Melvad", "Creative loaded")
            }
            override fun onImpression(view: MelvadAdView) {
                Log.d("Melvad", "Visible impression")
            }
            override fun onError(view: MelvadAdView, error: MelvadAdError) {
                Log.e("Melvad", "Ad result: " + error.code)
            }
        }
        view.post {
            if (ad === view) view.loadAd(MelvadAdRequest(
                placement = MelvadPlacement.parse(placementKey),
                format = MelvadAdFormat.BANNER,
                privacy = privacy
            ))
        }
    }

    override fun onPause() { ad?.pause(); super.onPause() }
    override fun onResume() { super.onResume(); ad?.resume() }
    override fun onDestroy() { ad?.destroy(); ad = null; super.onDestroy() }
}

Вызывайте showBanner(placementKey, privacy) после получения решения CMP. В Fragment освобождайте сохранённый view в onDestroyView(). В Compose создавайте его в AndroidView.factory и освобождайте в onRelease; не загружайте рекламу на каждой рекомпозиции.

РезультатЧто проверить
NO_FILLНет подходящего объявления. Проверьте статус места, формат и спрос; сверните пустое место или запланируйте контролируемую повторную попытку.
TIMEOUTПроверьте сеть и таймаут загрузки. Не запускайте немедленный бесконечный цикл повторов.
RENDERER_UNAVAILABLEОбновите Android System WebView и проверьте поддержку WebMessageListener.
RENDERER_ERROR / WEBVIEW_TERMINATEDПроверьте формат и контейнер. Освободите view с ошибкой и создайте новый для последующей загрузки.
Загружено, но не видноПроверьте, что контейнер добавлен, имеет размер, виден и приложение активно. Полноэкранным и нижним форматам нужен соответствующий нативный host.

Перед выпуском приложения проверьте реальные места на физических устройствах: прокрутку, фон/возврат, клики, закрытие, поворот, изменение согласия и полный rewarded-просмотр. Для помощи отправьте версию SDK, Android/WebView, формат и шаги воспроизведения на support@melvad.com. Удалите приватные идентификаторы и данные согласия из передаваемых логов.

К началу инструкции Android

Входящие запросы на синхронизацию кук

Пример запроса:

https://sync.dmp.melvad.com/match/0?ssp_id=0&user_id={user_id}&rt={REDIRECT_TO_ENCODE_URL}
&gdpr=${GDPR}&gdpr_consent=${GDPR_CONSENT_XXXXX}&sub_field1=&sub_field2=&sub_field3=&sub_field4=&sub_field5=

, где

  • {user_id} - идентификатор пользователя SSP
  • {REDIRECT_TO_ENCODE_URL} - URL, на какой необходимо перенаправить запрос (делается 302 редирект), обязательно передается в encode
  • ${GDPR} - принимаются значение 0 или 1, согласно документации CMP API v2
  • ${GDPR_CONSENT_XXXX} - согласие пользователя на обработку его данных, согласно документации CMP API v2
  • sub_field1, sub_field2, sub_field3, sub_field4, sub_field5 - необязательные параметры, в которые можно передать свое значение, если необходимо передать его в URL перенаправления
  • %7B%7Bsub_field1%7D%7D, %7B%7Bsub_field2%7D%7D, %7B%7Bsub_field3%7D%7D, %7B%7Bsub_field4%7D%7D, %7B%7Bsub_field5%7D%7D - необязательные encode макросы, которые размещается в URL перенаправления, если необходимо пробросить значение из соответствующих параметров "sub_field1..5"

Поддерживаемые форматы рекламы в формате OpenRTB стандарта (версия OpenRTB 2.5)

Баннерная реклама

Полноэкранное объявление

Пример запроса

{
   "id": "o37du34ajfj34h",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "pdsu2389fdfj34",
         "secure": 1,
         "banner": {
            "w": 375,
            "h": 667,
            "format": [
               {
                  "w": 375,
                  "h": 667
               },
               {
                  "w": 320,
                  "h": 480
               }
            ],
            "api": [
               5
            ],
            "mimes": [
               "image/png",
               "image/jpeg",
               "image/gif",
               "text/html",
			   "text/plain"
            ],
            "btype": [
               1,
               3,
               4
            ],
            "battr": [
               3,
               4,
               5
            ]
         },
         "instl": 1
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr"
      },
      "cat": [
         "IAB9"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "api" передается опционально, но только для отображения рекламы в мобильном приложении

Пример ответа

Раскрывающееся объявление

Пример запроса

{
   "id": "o37du34ajfj34h",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "pdsu2389fdfj34",
         "secure": 1,
         "banner": {
            "w": 320,
            "h": 480,
            "format": [
               {
                  "w": 320,
                  "h": 480
               }
            ],
            "api": [
               5
            ],
            "expdir": [
               5
            ],
            "mimes": [
               "image/png",
               "image/jpeg",
               "image/gif",
               "text/html",
			   "text/plain"
            ],
            "btype": [
               1,
               3,
               4
            ]
         },
         "instl": 1
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr"
      },
      "cat": [
         "IAB9"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание:

  • в атрибут "expdir" передается направление раскрытия, если в нем присутствует значение 5, то так же должен передавать и атрибут "instl" со значением 1
  • атрибут "api" передается опционально, но только для отображения рекламы в мобильном приложении

Пример ответа

Приклеенное объявление

Пример запроса

{
   "id": "o37du34ajfj34h",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "pdsu2389fdfj34",
         "secure": 1,
         "banner": {
            "w": 414,
            "h": 50,
            "format": [
               {
                  "w": 414,
                  "h": 50
               }
            ],
            "api": [
               5
            ],
            "mimes": [
               "image/png",
               "image/jpeg",
               "image/gif",
               "text/html",
			   "text/plain"
            ],
            "btype": [
               1,
               3,
               4
            ],
            "battr": [
               3,
               4,
               5
            ]
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr"
      },
      "cat": [
         "IAB9"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

Приклеенное полноэкранное объявление

Пример запроса

{
   "id": "o37du34ajfj34h",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "pdsu2389fdfj34",
         "secure": 1,
         "banner": {
            "w": 414,
            "h": 50,
            "format": [
               {
                  "w": 414,
                  "h": 50
               }
            ],
            "api": [
               5
            ],
            "mimes": [
               "image/png",
               "image/jpeg",
               "image/gif",
               "text/html",
			   "text/plain"
            ],
            "btype": [
               1,
               3,
               4
            ]
         },
         "instl": 1
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr"
      },
      "cat": [
         "IAB9"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

Объявление с фиксированным размером

Пример запроса

{
   "id": "o37du34ajfj34h",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "pdsu2389fdfj34",
         "secure": 1,
         "banner": {
            "w": 728,
            "h": 90,
            "format": [
               {
                  "w": 728,
                  "h": 90
               },
               {
                  "w": 468,
                  "h": 60
               }
            ],
            "api": [
               5
            ],
            "mimes": [
               "image/png",
               "image/jpeg",
               "image/gif",
               "text/html",
			   "text/plain",
               "application/javascript",
               "text/javascript"
            ],
            "btype": [
               1,
               4
            ],
            "battr": [
               3,
               4,
               5
            ]
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr"
      },
      "cat": [
         "IAB9"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "api" передается опционально, но только для отображения рекламы в мобильном приложении

Пример ответа

Видео реклама

Линейное объявление с сопутствующим объявлением и возможностью пропуска

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "protocols": [
               3
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "startdelay": 0,
            "placement": 1,
            "companionad": [
               {
                  "id": "1",
                  "format": [
                     {
                        "w": 300,
                        "h": 250
                     }
                  ],
                  "w": 300,
                  "h": 250,
                  "btype": [
                     1,
                     3,
                     4
                  ]
               }
            ],
            "companiontype": [
               1
            ]
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344"
      },
      "cat": [
         "IAB12",
         "IAB17-12"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "startdelay" может иметь значение -2, -1, 0 или любое число больше 1

Пример ответа

Линейное объявление с цепочкой последовательных объявлений и возможностью пропуска

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "sequence": 3,
            "protocols": [
               3
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "startdelay": 0,
            "placement": 1
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344"
      },
      "cat": [
         "IAB12",
         "IAB17-12"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание:

  • атрибут "sequence" может иметь значение 2 или 3
  • атрибут "startdelay" может иметь значение -2, -1, 0 или любое число больше 1

Пример ответа

Линейное объявление с возможностью пропуска

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "protocols": [
               3,
               6
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "startdelay": 0,
            "placement": 1
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344"
      },
      "cat": [
         "IAB12",
         "IAB17-12"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "startdelay" может иметь значение -2, -1, 0 или любое число больше 1

Пример ответа

Линейное вне потоковое объявление за вознаграждение

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "protocols": [
               3
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "placement": 4,
            "ext": {
               "rewarded": 1
            }
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr" 
      },
      "cat": [
         "IAB9" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "placement" передается опционально

Пример ответа

Полноэкранное линейное вне потоковое объявление

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "protocols": [
               3
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "placement": 5
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr" 
      },
      "cat": [
         "IAB9" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

Линейное вне потоковое объявление

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 640,
            "h": 360,
            "minduration": 5,
            "maxduration": 60,
            "minbitrate": 500,
            "maxbitrate": 6000,
            "protocols": [
               3
            ],
            "api": [
               2
            ],
            "mimes": [
               "video/mp4",
               "video/webm",
               "video/mpeg",
               "application/javascript"
            ],
            "linearity": 1,
            "delivery": [
               2
            ],
            "placement": 4
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344"
      },
      "cat": [
         "IAB12",
         "IAB17-12"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Примечание: атрибут "placement" может иметь значение 3 или 4

Пример ответа

Нелинейное объявление

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB"
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "video": {
            "w": 320,
            "h": 50,
            "protocols": [
               3
            ],
            "mimes": [
               "image/gif",
               "image/jpeg",
               "image/png"
            ],
            "linearity": 2
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles"
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344"
      },
      "cat": [
         "IAB12",
         "IAB17-12"
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

Нативная реклама

Мобильное приложение в ленте

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"required\":1,\"img\":{\"type\":1,\"wmin\":100,\"hmin\":100,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":3,\"required\":1,\"img\":{\"type\":3,\"wmin\":1200,\"hmin\":627,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"required\":1,\"data\":{\"type\":2,\"len\":90}},{\"id\":5,\"data\":{\"type\":3,\"len\":5}},{\"id\":6,\"data\":{\"type\":4,\"len\":11}},{\"id\":7,\"data\":{\"type\":12,\"len\":15}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr" 
      },
      "cat": [
         "IAB9" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

Мобильное приложение в ленте с видео

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"required\":1,\"img\":{\"type\":1,\"wmin\":100,\"hmin\":100,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":3,\"required\":1,\"img\":{\"type\":3,\"wmin\":1200,\"hmin\":627,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"required\":1,\"video\":{\"mimes\":[\"video\/mp4\",\"video\/webm\",\"video\/mpeg\"],\"protocols\":[3]}},{\"id\":5,\"required\":1,\"data\":{\"type\":2,\"len\":90}},{\"id\":6,\"data\":{\"type\":3,\"len\":5}},{\"id\":7,\"data\":{\"type\":4,\"len\":11}},{\"id\":8,\"data\":{\"type\":12,\"len\":15}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "app": {
      "id": "9fu48u347ru34o",
      "name": "Angry Birds 2",
      "bundle": "com.rovio.baba",
      "storeurl": "https://apps.apple.com/app/angry-birds-2/id880047117",
      "publisher": {
         "id": "p-8e23ywepo3rr" 
      },
      "cat": [
         "IAB9" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c"
   }
}

Пример ответа

В контенте

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"img\":{\"type\":1,\"wmin\":100,\"hmin\":100,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":3,\"required\":1,\"img\":{\"type\":3,\"wmin\":1200,\"hmin\":627,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"required\":1,\"data\":{\"type\":2,\"len\":90}},{\"id\":5,\"required\":1,\"data\":{\"type\":1,\"len\":25}},{\"id\":6,\"data\":{\"type\":12,\"len\":15}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344" 
      },
      "cat": [
         "IAB12",
         "IAB17-12" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c" 
   }
}

Пример ответа

В контенте с видео

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"required\":1,\"img\":{\"type\":1,\"wmin\":100,\"hmin\":100,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":3,\"required\":1,\"img\":{\"type\":3,\"wmin\":1200,\"hmin\":627,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"required\":1,\"video\":{\"mimes\":[\"video\/mp4\",\"video\/webm\",\"video\/mpeg\"],\"protocols\":[3]}},{\"id\":5,\"data\":{\"type\":2,\"len\":90}},{\"id\":6,\"required\":1,\"data\":{\"type\":1,\"len\":25}},{\"id\":7,\"data\":{\"type\":12,\"len\":15}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344" 
      },
      "cat": [
         "IAB12",
         "IAB17-12" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c" 
   }
}

Пример ответа

Текст и изображение

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"data\":{\"type\":12,\"len\":15}},{\"id\":3,\"required\":1,\"img\":{\"type\":3,\"wmin\":1200,\"hmin\":627,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"data\":{\"type\":2,\"len\":90}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344" 
      },
      "cat": [
         "IAB12",
         "IAB17-12" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c" 
   }
}

Пример ответа

Текст и видео

Пример запроса

{
   "id": "o37934lssd34fgd",
   "cur": [
      "USD",
      "RUB" 
   ],
   "at": 2,
   "imp": [
      {
         "id": "1",
         "bidfloor": 0.25,
         "bidfloorcur": "USD",
         "tagid": "ierui234fdfdf",
         "secure": 1,
         "native": {
            "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":25}},{\"id\":2,\"data\":{\"type\":12,\"len\":15}},{\"id\":3,\"required\":1,\"img\":{\"type\":1,\"wmin\":100,\"hmin\":100,\"mimes\":[\"image\/png\",\"image\/jpeg\",\"image\/gif\",\"text\/html\"]}},{\"id\":4,\"data\":{\"type\":3,\"len\":5}},{\"id\":5,\"required\":1,\"video\":{\"mimes\":[\"video\/mp4\",\"video\/webm\",\"video\/mpeg\"],\"protocols\":[3]}},{\"id\":6,\"data\":{\"type\":2,\"len\":90}}],\"eventtrackers\":[{\"event\":1,\"methods\":[1]}]}",
            "ver": "1.2" 
         }
      }
   ],
   "device": {
      "w": 375,
      "h": 667,
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.46 Mobile/15E148 Safari/604.1",
      "ip": "69.75.122.146",
      "ifa": "460384F3-D3B0-47CA-8090-F120F270A8B7",
      "language": "en",
      "geo": {
         "country": "USA",
         "region": "CA",
         "city": "Los Angeles" 
      }
   },
   "site": {
      "id": "9fu48u347ru34o",
      "domain": "tass.ru",
      "page": "https://tass.ru/sport/4237913",
      "publisher": {
         "id": "p-93dj34di3344" 
      },
      "cat": [
         "IAB12",
         "IAB17-12" 
      ]
   },
   "user": {
      "id": "d92a55e6-3eda-44c4-a448-14830a4c6838",
      "buyeruid": "213ba1a2425be71ec5080b1d64b8450c" 
   }
}

Пример ответа

Для получения технической поддержки, вы можете написать на адрес support@melvad.com и описать характер проблемы, которую необходимо решить, а также предоставить сопутствующую информацию для улучшения понимания этой проблемы. Мы приложим все усилия для своевременного ответа на такие запросы, но не можем гарантировать, что проблема будет решена.