DOCUMENTATION / CAMPAIGNS

Performance reporting

Filter spend, revenue, ROAS and daily delivery with the same API as the dashboard.

Markdown

One report for your agent and your dashboard

GET /api/v1/reports/campaigns powers the dashboard chart, efficiency cards, and campaign table. It is read-only: it does not change campaigns, budgets, attribution configuration, or payment balances. Use a key with campaigns:read, or an owner session. Live reports require a live key; sandbox keys default to sandbox and cannot read live reporting.

curl --get 'https://marketingrouter.com/api/v1/reports/campaigns' \
  -H "Authorization: Bearer $MR_API_KEY" \
  --data-urlencode 'from=2026-10-01' \
  --data-urlencode 'to=2026-10-01' \
  --data-urlencode 'time_zone=America/New_York' \
  --data-urlencode 'mode=live' \
  --data-urlencode 'objective=purchase' \
  --data-urlencode 'sort=roas' \
  --data-urlencode 'direction=desc'

Use your actual dates. Dates are inclusive calendar dates in time_zone. Ranges contain 1–90 days and cannot end in the future. With no dates, the last seven days including today are used. from and to should normally be sent together. The default time zone is UTC.

Filters and sorting

All filters are ANDed. Unknown query parameters and duplicate parameters are rejected rather than silently ignored.

ParameterValues / behavior
from, toInclusive YYYY-MM-DD dates, 1–90 days
time_zoneIANA zone, such as UTC or America/New_York
attributionlast_touch (default) or first_touch; selects conversion attribution only
modelive (owner/live key default), sandbox (test key default)
statusOne current campaign status from the Campaign schema
objectivepurchase, signup, traffic
qLiteral, case-insensitive search of campaign name and destination URL
campaign_ids1–50 comma-separated MarketingRouter campaign UUIDs; only owned matches are returned
min_spend_centsMinimum period media spend in USD cents
min_roas, max_roasInclusive ROAS bounds, e.g. max_roas=1
min_resultsMinimum objective-specific results
sortname, status, budget_cents, spent_cents (default), revenue_cents, results, roas, cpc_cents, ctr, impressions, clicks, cost_per_result_cents
directiondesc (default), asc
limit1–100 rows, default 25
offsetStart position, default 0; follow next_offset until null
refreshtrue requests recollection; false uses the five-minute cache. Refresh has a one-minute cache cooldown.

An example review queue: objective=purchase&min_spend_cents=10000&max_roas=1&sort=spent_cents&direction=desc. This finds owned purchase campaigns that spent at least $100 in the selected period and have known ROAS at or below 1. Unknown ROAS does not match a numeric threshold—inspect an unfiltered report too before making decisions.

Reports support at most 50 matching campaigns before numeric filters, and inspect at most 1,000 candidates before text search. If report_too_large is returned, narrow by campaign IDs, status, or objective. This is an explicit limit, not silent truncation. Rows use current names/statuses/budgets; metrics cover the requested period. Campaign selection is not a filter on creation date.

Read the response

The JSON envelope is { data, meta: { request_id, api_version } }. data includes:

  • query: normalized filters, date window, time zone, attribution, and sort.
  • summary: metrics over all matching campaigns, before pagination.
  • series: daily spend, impressions, clicks, CTR and CPC over that same selection.
  • rows: campaign identity, current lifetime budget, metrics, collection time, freshness, data status, and an error code when incomplete.
  • total_count, next_offset: pagination over sorted rows. Unknown sort values are always last; ties use campaign UUID ascending. Each request is a fresh view, so campaign changes or recollection can move rows between pages.
  • coverage: available, unavailable, and stale campaign counts. partial=true means some matching reports are incomplete.
  • warnings, definitions: machine-readable context for safe interpretation.

No campaign belonging to another owner can appear in any total, row or series. The collection uses the campaign's assigned advertising account. A changed binding produces unavailable data instead of reading a different account. Reports are private and are not publicly cached.

Correct financial interpretation

Money values end in _cents and use USD. CPC and CPM can contain fractional cents. ctr is a fraction: 0.015 means 1.5%. roas is a multiplier: 2.4 means $2.40 attributed purchase revenue per $1 of media spend.

ROAS is sum(revenue_cents) / sum(spent_cents), not the average of campaign ROAS. CPC is total spend divided by total clicks; CPM is spend × 1,000 / impressions. Zero denominators return null. Unknown input values return null, never zero. One unknown constituent makes its corresponding total unknown; known rows remain individually inspectable.

Results mean purchases, signups or clicks according to the campaign objective. Mixed objectives produce result_type=mixed with null summary results and cost per result; filter to one objective to compare these measures. Purchase revenue and spend can still be compared across objectives when known.

Media spend excludes service and payment fees. Attributed revenue does not establish profit, incrementality, or realized net proceeds. budget_cents is the current lifetime budget, and approved_ceiling_cents is the original owner-approved limit. A date filter does not prorate either budget.

Freshness and daily series

collected_at is fetch time, not proof that Meta or conversion attribution has finished updating. Daily delivery and conversion totals can be delayed or revised. Summary collection time is the oldest collection time among matching rows. Use row-level stale, data_status, error_code and coverage before decisions. available means the requested values were collected, not that attribution is final.

Daily series currently contains delivery measures only. Daily revenue, purchases and ROAS are not exposed. Missing upstream daily buckets remain null; we do not manufacture zero-spend days or derive daily results by subtracting lifetime snapshots. A reporting failure may return a stale cache only for the exact same campaign, date range, time zone, attribution and account binding. Otherwise it returns unknown values. report_refresh_pending means collection hit its bounded work window; retry after a short delay to collect the remaining campaigns.

Sandbox campaigns have zero delivery and make no advertising-provider calls. Unlaunched plans without a remote campaign have zero delivery. These states are explicitly distinguished from observed live results. Demo dashboard data is illustrative and never returned by the authenticated API.

The dashboard supports shareable filter URLs, CSV export of the complete filtered cohort, sortable columns, daily bar tooltips, optional delivery columns, and visible-tab polling every minute. Polling respects the cache; it does not promise instant upstream reporting. CSV uses cents and fractional CTR, includes the report window and attribution, and leaves unknown values blank.

Close the loop

Read this report to find campaigns worth inspecting. Then call GET /api/v1/campaigns/{id}/performance for lifetime pacing, recommendations and control history. Follow Media buying controls to pause, resume, or change a budget within its approved ceiling. Period reports never overwrite lifetime spend or authorize a new spending limit.

Performance below a threshold is a review signal, not statistical proof. Check tracking, delayed conversions, sample size and the offer before acting. There is no automatic optimization or silent budget change.