Reporting (spend / ROAS)
Why
Firing events is half the loop. An agent that can also read spend and ROAS can decide whether a campaign is working. One platform per call. Dates are YYYY-MM-DD.
import { getReport } from 'adscapi';
const rows = await getReport('meta', secrets, {
since: '2026-04-01',
until: '2026-04-08',
});
for (const row of rows) {
console.log(row.campaignName, row.spend, row.roas);
}level defaults to 'campaign'. Pass 'account' for an account rollup (Meta honors it; TikTok always returns auction-campaign rows). Inject fetchImpl in tests.
ReportRow
Google cost_micros is divided by 1e6. Meta spend is already major units. When Google gives conversion value but no native ROAS, adscapi sets roas = conversionValue / spend.
type ReportRow = {
platform: 'meta' | 'google' | 'tiktok';
campaignId?: string;
campaignName?: string;
spend: number; // major units, platform currency
impressions: number;
conversions?: number;
conversionValue?: number;
roas?: number;
currency?: string;
};Secrets
| Platform | Required |
|---|---|
| Meta | META_AD_ACCOUNT_ID, META_CAPI_TOKEN |
| Google Ads | GOOGLE_ADS_CUSTOMER_ID, GOOGLE_ADS_ACCESS_TOKEN, GOOGLE_ADS_DEVELOPER_TOKEN |
| TikTok | TIKTOK_ADVERTISER_ID, TIKTOK_ACCESS_TOKEN |
Google also reads optional GOOGLE_ADS_LOGIN_CUSTOMER_ID (MCC login customer). A missing required secret throws missing required secret <NAME>. A non-2xx from the platform throws PlatformHttpError.
CLI and MCP
adscapi report --platform meta --since 2026-04-01 --until 2026-04-08 adscapi report --platform google --since 2026-04-01 --until 2026-04-08 --level account
The CLI reads secrets from the environment. MCP tool adscapi_report takes the same args: { platform, since, until, level? }.
Notes
- TikTok ROAS uses
complete_payment_roas. That metric name is unverified against TikTok’s supported-metrics list. Spend, impressions, andconversionare confirmed. If the field is absent,roasis omitted — the call still succeeds. - One platform per call. Fan out yourself if you want Meta + Google + TikTok.
- Numbers come back as strings from the platforms; adscapi coerces them with
Number().
Related
- conversions.track() — the write half of the loop
- AdCP interop — map report totals onto AdCP measurements
- Errors