A mediação interna não está disponível publicamente
No momento, os lances internos com o Audience Network estão na versão beta fechada e não estão disponíveis publicamente. Compartilharemos atualizações se isso mudar.
Como alternativa, você pode acessar os lances do Audience Network usando uma das nossas plataformas parceiras de mediação.
Este guia ensina a criar um servidor de leilão interno. No guia passo a passo abaixo, usamos o exemplo de lances em um anúncio intersticial. É preciso que você já esteja familiarizado com o uso de anúncios intersticiais do Audience Network. Os lances também são compatíveis com os formatos de anúncios nativos, intersticiais, de vídeo in-stream e de vídeo com incentivo. Consulte os formatos de anúncio compatíveis abaixo para integrar um formato de anúncio diferente.
O exemplo de código que oferecemos tem apenas o intuito de demonstrar como a API de lances funciona e como você pode integrá-la ao seu servidor de leilão interno. O servidor de leilão de exemplo foi escrito em Python com Flask, com considerações mínimas de estabilidade e segurança pela simplicidade do código-fonte.
O imp.tagid
do campo ORTB detém o identificador do Audience Network para o inventário. O inventário é representado no Audience Network como o ID.
A solicitação ORTB enviada ao ponto de extremidade de lances do Audience Network precisa conter informações do dispositivo e do app. Veja aqui um exemplo de carga da solicitação ORTB:
{ // Device information from client side 'device': { 'ifa': '${DEVICE_ID}', 'dnt': '0', 'ip': '127.0.0.1' }, // Application information 'app': { 'ver': '1.0', 'bundle': 'com.facebook.audiencenetwork.AdUnitsSample', 'publisher': { // For server to server bidding integration, this is your application id on Facebook 'id': '${APP_ID}', } }, // Placement information we can store on server 'imp': [ { 'id': 'banner_test_bid_req_id', // This is the placement id for Audience Network 'tagid': '${PLACEMENT_ID}', 'banner': { 'w': -1, 'h': 50, }, }, ], // Optional regulations object from client side 'regs': { 'coppa': 0, }, // In server to server integration, you can use the Facebook app id as platform id here 'ext': { 'platformid': '${PLATFORM_ID}', // Mediation partner Platform ID or publisher FB app ID, mandatory. 'authentication_id': '${AUTHENTICATION_ID}', // Authentication token to validate the originator of the request. 'security_app_id': '${AN_SECURITY_APP_MY_SECURITY_APP_ID}', // Security app id used to generate authentication_id. 's2s_version': '${VERSION_NUMBER}', // Version of the integration. Max 16 characters. }, // buyeruid is the user bidder token generated on client side, using the `getBidderToken` method from the Audience Network SDK. // It's constant through out app session so you could cache it on the client side 'user': { 'buyeruid': 'mybuyeruid', }, // Test mode flag 'test': '1', // Time out setting we can store on server 'tmax': 1000, // Request ID we can generate on server 'id': 'banner_test_bid_req_id', // Auction setting we can store on server 'at': 1, }
Parâmetro | Tipo | Uso |
---|---|---|
|
| Se você for um parceiro de mediação, esta é a identificação do parceiro fornecida pelo seu contato do Facebook. |
Se você for um publisher e estiver integrando uma solução interna, este é o ID do app para enviar a solicitação de lance. Isso constitui a primeira parte do ID de posicionamento, antes do sublinhado. | ||
|
| Token de autenticação para validar o originador da solicitação. Consulte esta página. |
|
| O ID do app de segurança usado para gerar o authentication_id. Consulte esta página. |
|
| Versão da integração. Máximo de 16 caracteres, gerada pelo publisher. |
A carga da solicitação ORTB acima é exigida pelo Audience Network. No entanto, no seu próprio servidor de leilão, você pode definir algumas especificações do app, como ID do app, ID do posicionamento, bem como alguns outros parâmetros na configuração do servidor. Depois, o app do cliente só precisará enviar a solicitação simplificada. Seu servidor pode criar um loop com as informações de posicionamento e construir a solicitação ORTB final. Veja abaixo o exemplo de configuração do servidor de leilão:
{ "bidding_source_platforms": [ { "platform_name": "audience_network", "end_point": "https://an.facebook.com/${PLATFORM_ID}/placementbid.ortb", "timeout": 1000, "timeout_notification_url": "https://www.facebook.com/audiencenetwork/nurl/?partner=${PARTNER_FBID}&app=${APP_FBID}&auction=${AUCTION_ID}&ortb_loss_code=2" } ], "apps": [ { "app_id": "101", "app_name": "My example app", "placements": [ { "placement_id": "1", "placement_name": "My example placement", "ad_format": "interstitial", "bidding_source_placement_ids":[ { "platform_name": "audience_network", "platform_app_id": "${APP_ID}", "platform_placement_id": "${PLACEMENT_ID}" } ] } ] } ] }
Parâmetro | Uso |
---|---|
| Se você for um parceiro de mediação, esta é a identificação do parceiro fornecida pelo seu contato do Facebook. |
Se você for um publisher e estiver integrando uma solução interna, este é o ID do app para enviar a solicitação de lance. Isso constitui a primeira parte do ID de posicionamento, antes do sublinhado. |
Com as configurações de servidor de leilão descritas acima, o app do cliente só precisa enviar uma solicitação simplificada com "app_id":"101"
e "placement_id":"1"
para seu servidor; depois, seu servidor poderá procurar informações completas de posicionamento (como placement_id
, ad_format
, platform_name
, entre outros). Veja aqui um exemplo de carga da solicitação simplificada do app do cliente:
{ // App ID and placement ID are used to look up settings from // server settings 'app_id': '101', 'placement_id': '1', 'bundle': 'com.facebook.samples.s2sbiddingclient', 'bundle_version': '1.0', // Device specifics 'ifa': '${DEVICE_ID}', 'coppa': 0, 'dnt': 0, // buyer_tokens are the user tokens required for different networks 'buyer_tokens': { // Token for audience network from BidderTokenProvider.getBidderToken(context) // This can be cached for the same app session 'audience_network': 'my-buyeruid', }, }
Assim que a solicitação simplificada for enviada para seu servidor de leilão, você poderá procurar informações completas de posicionamento, dispositivo e app por app_id
e placement_id
nas configurações do seu servidor de leilão. Veja aqui um exemplo de resultado da consulta:
{ "placement_id": "1", "placement_name": "My example placement", "ad_format": "interstitial", "bidding_source_placement_ids": [{ "platform_name": "audience_network", "platform_app_id": "${APP_ID}", "platform_placement_id": "${PLACEMENT_ID}" }] }
Depois de receber a solicitação, o servidor de leilão precisará construir as solicitações de lance para cada uma das fontes de demanda. Este é o ponto de extremidade no app.py
para receber a solicitação de leilão:
@app.route('/get_bid', methods=['POST']) def get_bid(): ''' The actual endpoint that expects a ClientRequest in the parameters ''' # Initialize the server settings ServerSettings.initialize( app.config['bidding_source_platforms'], app.config['apps'] ) request_params = request.get_json(force=True) # See the code sample below for this function in `bid_manager.py` (code, response) = get_bid_response( request.remote_addr, request.user_agent.string, request_params) app.logger.debug('server response: {0} {1}'.format(code, response)) return Response( json.dumps(response), status=code, mimetype='application/json' )
Em bid_manager.py
, a solicitação de leilão no cliente contém o app_id
e o placement_id
que serão utilizados para procurar as informações completas de posicionamento nas configurações do servidor.
# Return Auction Result - Winner - back to client side def get_bid_response(ip, user_agent, request_params): """Get the winner bid response for the current request""" try: app_id = request_params['app_id'] placement_id = request_params['placement_id'] auction_id = get_auction_id() ... # Find placement in the settings placement = ServerSettings.get_placement(app_id, placement_id) # Collect bid requests for different platforms bid_requests = get_bid_requests( ip, user_agent, auction_id, placement, request_params) except Exception as e: raise ParameterError("Error in request parameters: {}".format(str(e))) ... return final_response # Get all bid requests for different platforms def get_bid_requests(ip, user_agent, auction_id, placement, request_params): """Create bid requests based on the internal placement setting""" ... (end_point, data, timeout_notification_url) = get_bid_request( ip, user_agent, auction_id, platform_name, platform_app_id, platform_placement_id, ad_format, request_params) if data is not None: results.append({ 'platform_name': platform_name, 'end_point': end_point, 'data': data, 'timeout_notification_url': timeout_notification_url, }) # current_app.logger.debug("requests: {}".format(results)) return results # Get bid request for each platform def get_bid_request( ip, user_agent, auction_id, platform_name, platform_app_id, platform_placement_id, ad_format, request_params ): """Create bid request for a specific platform""" if platform_name == 'audience_network': return audience_network.get_bid_request( ip, user_agent, auction_id, platform_app_id, platform_placement_id, ad_format, request_params) else: return (None, None, None)
Depois em audience_network. py
, a função get_bid_request
gerará uma solicitação ORTB final para o Audience Network com base em informações completas de posicionamento.
O imp.tagid
do campo ORTB detém o identificador do Audience Network para o inventário. O inventário é representado no Audience Network como o ID do posicionamento.
def get_bid_request( ip, user_agent, auction_id, platform_app_id, platform_placement_id, ad_format, request_params ): ''' Gather the required bid request parameters for networks. The parameters consist of platform settings like app id, placement ids, ad sizes etc., and client side information such as device information, user agent etc. We use the `settings.json` file to store platform specific settings, and the client request to retrieve the clietn specific information. ''' platform = ServerSettings.get_platform('audience_network') end_point = platform['end_point'] timeout = platform['timeout'] timeout_notification_url = platform['timeout_notification_url'] timeout_notification_url.replace('${PARTNER_FBID}', platform_app_id) timeout_notification_url.replace('${APP_FBID}', platform_app_id) timeout_notification_url.replace('${AUCTION_ID}', auction_id) imp = [] if ad_format == 'native': imp.append({ 'id': auction_id, 'native': { 'w': -1, 'h': -1, }, 'tagid': platform_placement_id, }) elif ad_format == 'banner': imp.append({ 'id': auction_id, 'banner': { 'w': -1, 'h': 50, }, 'tagid': platform_placement_id, }) elif ad_format == 'interstitial': imp.append({ 'id': auction_id, 'banner': { 'w': 0, 'h': 0, }, 'tagid': platform_placement_id, 'instl': 1, }) elif ad_format == 'rewarded_video': imp.append({ 'id': auction_id, 'video': { 'w': 0, 'h': 0, 'linearity': 2, }, 'tagid': platform_placement_id, }) elif ad_format == 'instream_video': imp.append({ 'id': auction_id, 'video': { 'w': 0, 'h': 0, 'linearity': 1, }, 'tagid': platform_placement_id, }) else: raise ParameterError("Incorrect ad format") typed_ip = ipaddress.ip_address(ip) device = { 'ifa': request_params['ifa'], 'ua': user_agent, 'dnt': request_params['dnt'], } if type(typed_ip) is ipaddress.IPv6Address: device['ipv6'] = ip else: device['ip'] = ip # Construct the ORTB request request = { 'id': auction_id, 'imp': imp, 'app': { 'bundle': request_params['bundle'], 'ver': request_params['bundle_version'], 'publisher': { 'id': platform_app_id, } }, 'device': device, 'regs': { 'coppa': request_params['coppa'], }, 'user': { 'buyeruid': request_params['buyer_tokens']['audience_network'], }, 'ext': { ' ': platform_app_id, }, 'at': 1, 'tmax': timeout, 'test': request_params['test'], } return (end_point, request, timeout_notification_url)
No momento, há quatro tipos de anúncios compatíveis que podem ser solicitados por meio de OpenRTB: em banner, nativo (nativo ou em banner nativo), de vídeo (com incentivo ou vídeo in-stream) e intersticial. Nota: os objetos de banner, nativo e vídeo se excluem mutuamente, mas um deles é necessário.
Veja aqui uma lista dos formatos de anúncio compatíveis na solicitação de lance:
Formato do anúncio | Parâmetros da solicitação de lance |
---|---|
Nativo |
|
Banner nativo |
|
Intersticial |
|
Vídeo com incentivo |
|
Banner - Altura: 50 |
|
Banner - Altura: 250 |
|
Vídeo In-stream |
|
Depois que o objeto de solicitação ORTB for criado acima, podemos enviar a solicitação ao ponto de extremidade do Audience Network em https://an.facebook.com /${PLATFORM_ID}/placementbid. ortb
usando a solicitação HTTP, post
e Content-Type: app/json
.
Em bid_manager. py
, depois que coletar todas as solicitações de lance para cada plataforma, ele chamará exec_bid_requests para cada plataforma:
# Return Auction Result - Winner - back to client side def get_bid_response(ip, user_agent, request_params): """Get the winner bid response for the current request""" ... # Execute bid requests by network bid_responses = [] for bid_request in bid_requests: (code, response) = exec_bid_request( bid_request['platform_name'], bid_request['end_point'], bid_request['data'], bid_request['timeout_notification_url']) bid_responses.append({ 'platform_name': bid_request['platform_name'], 'code': code, 'response': response, }) final_response = run_auction(bid_responses, placement) return final_response # Execute bid request for different platform (network) def exec_bid_request( platform_name, end_point, request_params, timeout_notification_url ): ''' Actually run the bid requests for the networks. ''' if platform_name == 'audience_network': return audience_network.exec_bid_request( end_point, request_params, timeout_notification_url, ) else: raise InternalError("Invalid platform: {}".format(platform_name))
Os cabeçalhos HTTP a seguir (em lances e não lances) contendo informações úteis para solução de problemas na resposta serão definidos e deverão ser registrados no servidor de leilão:
X-FB-AN-Request-ID
: o ID da solicitação é necessário para que o Audience Network depure uma solicitação específica. Você deverá capturá-lo sempre que solicitar suporte.X-FB-AN-Errors
: uma lista de erros encontrados, que é útil para compreender os motivos para não ter lances.X-FB-Debug
: algumas informações de depuração sobre esta solicitação que podem ser enviadas para solução de problemas junto ao seu representante de conta no Audience Network.Observação: para minimizar a latência, é obrigatório adicionar o cabeçalho X-FB-Pool-Routing-Token
à solicitação de lance.
X-FB-Pool-Routing-Token
: este token é usado para encaminhar a solicitação ao nosso data center mais próximo, e seu valor é o mesmo que user.buyeruid
. Em audience_network. py
, a solicitação ORTB será enviada ao Audience Network, e a resposta de lance correspondente será entregue ao servidor de leilão:
def exec_bid_request( end_point, request_params, timeout_notification_url ): try: platform = ServerSettings.get_platform('audience_network') headers = { 'Content-Type': 'application/json; charset=utf-8', 'X-FB-Pool-Routing-Token': request_params['user']['buyeruid'], } timeout = platform['timeout'] r = requests.post( end_point, json=request_params, headers=headers, # Timeout in settings.json in ms timeout=(timeout / 1000), ) except Exception as e: current_app.logger.error(BID_TIMEOUT) # Send time out notification r = requests.get(timeout_notification_url, timeout) return (500, BID_TIMEOUT) if r.status_code == requests.codes.ok: try: data = json.loads(r.text) current_app.logger.debug('Audience Network response: {}'.format( data )) # Parse response from Audience Network with the ORTBResponse ortb_response = ORTBResponse(data) except Exception as e: current_app.logger.error( PARSE_ERROR + "{}".format(e) ) return (500, PARSE_ERROR + "{}".format(e)) return (r.status_code, ortb_response) else: # The error message is stored in the X-FB-AN-Errors header error_header = r.headers.get('x-fb-an-errors') debug_header = r.headers.get('x-fb-debug') bid_request_id = r.headers.get('x-fb-an-request-id') if r.status_code == 400: error_message = INVALID_BID + error_header + INVALID_BID_ADVICE elif r.status_code == 204: error_message = NO_BID + error_header else: error_message = UNEXPECTED_ERROR # Log error information for debugging error = { 'bid_request_id': bid_request_id, 'debug_header': debug_header, 'error_message': error_message, } current_app.logger.error(error) # Respond error status code to client return (r.status_code, error_message)
Se você fornecer um cabeçalho Content-Encoding:gzip
na solicitação, será possível enviar um corpo de solicitação binário e compactado em gzip.
Agora, já temos respostas de lances de diferentes plataformas (rede). Em bid_manager. py
, a função get_bid_response
será responsável por comparar as respostas de lance preenchidas e decidir qual será o lance mais alto (vencedor).
def get_bid_response(ip, user_agent, request_params): """Get the winner bid response for the current request""" ... final_response = run_auction(bid_responses, placement) return final_response def run_auction(bid_responses, placement): """Run auction based on raw responses and create the response object""" other_bid = 1 response = (204, None) # default is 204 no fill for bid_response in bid_responses: if bid_response['platform_name'] == 'audience_network': if bid_response['code'] == 200: ortb_response = bid_response['response'] if ortb_response.price > other_bid: response = create_response(bid_response, placement) current_app.logger.debug( 'Audience Network bid: {} won!'.format( ortb_response.price ) ) notify_result(bid_response) else: current_app.logger.debug( 'Audience Network bid: {} lost!'.format( ortb_response.price ) ) notify_result(bid_response, 102) else: current_app.logger.debug(bid_response['response']) return response
Como o Audience Network é o único licitante no nosso exemplo, o método de execução do leilão apenas compara o lance retornado a algum valor de preço para decidir se ele vence o leilão. Como resultado, se o lance retornado do Audience Network for mais alto que US$ 1, responderemos que o Audience Network venceu o leilão. Caso contrário, trataremos o lance como tendo perdido o leilão.
Nós exigimos notificações de ganhos, perdas, passível de cobrança e tempo limite com os códigos de perda apropriados conforme definido no ORTB. O nurl, lurl e burl do ORTB são oferecidos na resposta do lance. Consulte a seção anterior para ver um exemplo de resposta de lance. Caso o lance atinja o tempo limite, ofereceremos uma rota de geração de relatórios alternativa.
O nurl de vitória será oferecido na resposta do lance. É necessário popular o preço de venda no nurl:
"https://www.facebook.com/audiencenetwork/nurl/?partner=${PARTNER_FBID}&app=${APP_FBID}&placement=${PLACEMENT_FBID}&auction=${AUCTION_ID}&impression=${IMPRESSION_ID}&request=${BID_REQUEST_ID}&bid=${BID_ID}&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}"
${AUCTION_PRICE}
: Deve ser substituído pelo preço de venda do leilão na mesma unidade do lance (ou seja, em dólares ou CPM).Nossa lurl de perda contém dois sinalizadores que você deve preencher:
"https://www.facebook.com/audiencenetwork/nurl/?partner=${PARTNER_FBID}&app=${APP_FBID}&placement=${PLACEMENT_FBID}&auction=${AUCTION_ID}&impression=${IMPRESSION_ID}&request=${BID_REQUEST_ID}&bid=${BID_ID}&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}"
${AUCTION_LOSS}
: Deve ser substituído pelo código de perda do ORTB.${AUCTION_PRICE}
: Deve ser substituído pelo preço de venda do leilão na mesma unidade do lance (ou seja, em dólares ou CPM).Veja abaixo uma lista com diferentes códigos de perda e os motivos de perda correspondentes.
Motivo de perda | Descrição | Código de perda ORTB v2.5 |
---|---|---|
Resposta de lance inválida. | O lance é inválido (mas feito a tempo, não é um não lance e é válido o suficiente para extrair o nurl) | 3 |
Tempo limite do lance * | Resposta de lance recebida, mas não a tempo de interromper o leilão | 2 |
Não lance | Não lances são indicados como HTTP 204 (ou seja, sem nurl para chamar), mas você pode interpretar nossa resposta como um não lance (provavelmente um problema de integração). Você também pode solicitar lances para várias impressões, e nós faremos lances em alguns, mas não todos. | 9 |
Não é o licitante RTB mais alto | Outro licitante nos superou, incluindo lances sintéticos (como trocas não RTB) se inseridas no mesmo leilão. | 102 |
O inventário não se materializou | Nosso lance venceu o leilão, mas a impressão não se materializou (por exemplo, a página não era longa o bastante para incluir esse espaço, ou o usuário saiu do aplicativo antes que o anúncio armazenado em cache fosse usado). Nem todos os parceiros podem oferecer isso (é um não evento); por isso, o inferiremos caso ele não seja oferecido. | 4902 |
Enviado para o servidor de anúncios | Envie isso se o último ponto de contato com o processo de decisão estiver enviando nosso lance alto ao servidor de anúncios. A impressão poderá continuar perdida por meio da perda dos itens de linha, do servidor de anúncios se sobrepondo ao leilão ou do inventário não se materializando. | 4900 |
Vencedor do RTB não escolhido pelo servidor de anúncios | Vencemos o leilão do RTB, mas o servidor de anúncios se sobrepôs ao leilão (por exemplo, diretamente). | 4903 |
Vitória | Vencemos a árvore de decisão completa, e a tag foi posicionada na página (Web) ou o objeto de anúncio foi armazenado em ache (aplicativo). Ainda assim, a impressão visualizável pode não resultar. | 0 |
Nós exigimos uma notificação de passível de cobrança caso um retorno de chamada de impressão seja acionado no SDK do Audience Network. É necessário popular o preço de venda no burl:
"https://www.facebook.com/audiencenetwork/burl/?partner=${PARTNER_FBID}&app=${APP_FBID}&placement=${PLACEMENT_FBID}&auction=${AUCTION_ID}&impression=${IMPRESSION_ID}&request=${BID_REQUEST_ID}&bid=${BID_ID}&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}"
${AUCTION_PRICE}
: Deve ser substituído pelo preço de venda do leilão na mesma unidade do lance (ou seja, em dólares ou CPM).Caso o lance atinja o tempo limite, ofereceremos uma rota de geração de relatórios alternativa. No nurl genérico que pode ser chamado sem a necessidade de esperar pela chegada do lance. O formato é o seguinte:
"https://www.facebook.com/audiencenetwork/nurl/?partner=${PARTNER_FBID}&app=${APP_FBID}&auction=${AUCTION_ID}&ortb_loss_code=2"
Nota:${PARTNER_FBID}
, ${APP_FBID}
e ${AUCTION_ID}
devem ser preenchidos com os valores adequados. A tabela abaixo oferece uma explicação sobre esses valores.
Parâmetro | Tipo | Descrição |
---|---|---|
PARTNER_FBID | Int | ID do servidor de leilão de anúncios emitido pelo Facebook. Use o ID do aplicativo aqui caso não tenha um parceiro dedicado de leilão de anúncios. |
APP_FBID | Int | ID emitido pelo Facebook do aplicativo ou da empresa que iniciou um leilão. |
AUCTION_ID | cadeia de caracteres | ID do leilão gerado pelo cliente que você usou para emitir uma solicitação de lance. |
Depois de executar o leilão, criamos o objeto de resposta e o retornamos ao app do cliente. Veja a seguir o método na amostra em bid_manager. py
que cria a resposta final do servidor de leilão:
def create_response(bid_response, placement): """Create response object based on the auction result""" ad_format = placement['ad_format'] platform_name = bid_response['platform_name'] platform_placement_id = None for bidding_source_placement_id in placement[ 'bidding_source_placement_ids' ]: if bidding_source_placement_id['platform_name'] == platform_name: platform_placement_id = bidding_source_placement_id[ 'platform_placement_id' ] if platform_placement_id is None: raise InternalError("Platform placement ID not found!") bid_payload = None if platform_name == 'audience_network': bid_payload = bid_response['response'].adm else: raise InternalError("Invalid platform") return (200, { 'placement_id': placement['placement_id'], 'ad_format': ad_format, 'platform_name': platform_name, 'platform_placement_id': platform_placement_id, 'bid_payload': bid_payload, })
Por fim, nosso servidor de exemplo pode criar esta resposta para o cliente para notificar qual plataforma deverá ser usada:
{ 'placement_id': string, // Placement identifier for the auction server 'ad_format': string, // Format of the placement 'platform_name': string, // Which platform won the auction, for example 'audience_network' 'platform_placement_id': string, // Placement ID for the platform, for example the placement ID for Audience network 'bid_payload': string, // The JSON string payload for the platform SDK to load the final ad }