Ads summary
Click-to-WhatsApp traffic grouped by ad, with the resolved ad, ad set and campaign names and the number of Meta Lead Ads leads each ad produced (leadCount). Newest-busiest first: ordered by conversations, then lastAt.
Permission: analytics:read, which every workspace role holds, ANALYST included — it is volume, not conversation content (compare GET /v1/referrals/summary, which needs chat:read and covers one number). Scope: a number-scoped key, or an account-wide credential with x-whatsapp-number-id, reports that number only; an account-wide credential without the header reports the whole workspace, and may narrow it with the whatsappNumberId query parameter.
conversations counts distinct conversations and messages counts messages, separately. lastHeadline and lastSourceUrl (the ad copy) are null for an ad seen only on a RELAY_ONLY number; the counts are unaffected. namesResolvedAt set with every name null means Pilot Status asked Meta and got nothing (ask the advertiser for the partner grant); null means it was not asked yet. Capped at 200 ads: truncated: true says the list was cut — narrow with startDate/endDate. An ad that produced leads but never a conversation has no row here; read it from GET /v1/leads.
curl --request GET \
--url https://pilotstatus.com.br/v1/ads/summary \
--header 'x-api-key: <api-key>'import requests
url = "https://pilotstatus.com.br/v1/ads/summary"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://pilotstatus.com.br/v1/ads/summary', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://pilotstatus.com.br/v1/ads/summary",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://pilotstatus.com.br/v1/ads/summary"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://pilotstatus.com.br/v1/ads/summary")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://pilotstatus.com.br/v1/ads/summary")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"ads": [
{
"sourceId": "120249828053880703",
"sourceType": "ad",
"conversations": 37,
"messages": 214,
"firstAt": "2026-10-02T11:04:19.000Z",
"lastAt": "2026-10-20T22:41:07.000Z",
"lastSourceUrl": "https://fb.me/2bXyZ",
"lastHeadline": "Reforma com 70% de desconto",
"adName": "Ads 01 - Vídeo dos 70%",
"adsetId": "120249828053880001",
"adsetName": "[IG] [LL1% IG + VÍDEOS] - 35 a 50",
"campaignId": "120249828053870000",
"campaignName": "[CD-01] [LEADS] - MM Design - Sale 70%",
"namesResolvedAt": "2026-10-20T23:00:02.000Z",
"leadCount": 12
}
],
"total": 1,
"limit": 200,
"truncated": false
}{
"error": "startDate must be before or equal to endDate | Intervalo de datas inválido (use ISO 8601, startDate <= endDate)",
"code": "INVALID_DATE_RANGE"
}{
"error": "Unauthorized"
}{
"error": "This credential is not granted access to that WhatsApp number | Esta credencial não tem acesso a esse número de WhatsApp",
"code": "NUMBER_NOT_IN_GRANT"
}{
"error": "Internal server error | Erro interno do servidor",
"code": "INTERNAL_ERROR"
}Authorizations
Your ps_ API key
Headers
Narrows an account-wide credential to one number (its id or instance id from GET /v1/numbers). Without it, an account-wide credential sees the whole workspace; a number-scoped key always sees only its own number.
Query Parameters
Restrict to one number inside the credential's scope (403 NUMBER_NOT_IN_GRANT otherwise).
Start of the range (ISO 8601), over the message time — and the lead's Meta creation time for leadCount. Must be ≤ endDate.
End of the range (ISO 8601), inclusive.
Response
Per-ad summary
Was this page helpful?
curl --request GET \
--url https://pilotstatus.com.br/v1/ads/summary \
--header 'x-api-key: <api-key>'import requests
url = "https://pilotstatus.com.br/v1/ads/summary"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://pilotstatus.com.br/v1/ads/summary', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://pilotstatus.com.br/v1/ads/summary",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://pilotstatus.com.br/v1/ads/summary"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://pilotstatus.com.br/v1/ads/summary")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://pilotstatus.com.br/v1/ads/summary")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"ads": [
{
"sourceId": "120249828053880703",
"sourceType": "ad",
"conversations": 37,
"messages": 214,
"firstAt": "2026-10-02T11:04:19.000Z",
"lastAt": "2026-10-20T22:41:07.000Z",
"lastSourceUrl": "https://fb.me/2bXyZ",
"lastHeadline": "Reforma com 70% de desconto",
"adName": "Ads 01 - Vídeo dos 70%",
"adsetId": "120249828053880001",
"adsetName": "[IG] [LL1% IG + VÍDEOS] - 35 a 50",
"campaignId": "120249828053870000",
"campaignName": "[CD-01] [LEADS] - MM Design - Sale 70%",
"namesResolvedAt": "2026-10-20T23:00:02.000Z",
"leadCount": 12
}
],
"total": 1,
"limit": 200,
"truncated": false
}{
"error": "startDate must be before or equal to endDate | Intervalo de datas inválido (use ISO 8601, startDate <= endDate)",
"code": "INVALID_DATE_RANGE"
}{
"error": "Unauthorized"
}{
"error": "This credential is not granted access to that WhatsApp number | Esta credencial não tem acesso a esse número de WhatsApp",
"code": "NUMBER_NOT_IN_GRANT"
}{
"error": "Internal server error | Erro interno do servidor",
"code": "INTERNAL_ERROR"
}