Query Attribution from the CLI

The Adapty CLI reads Adapty Attribution analytics from the terminal, under the adapty attribution topic. It returns the same numbers as the Attribution dashboard: spend, installs, revenue, ROAS, cohort values, and predictions, grouped by channel, campaign, ad set, ad, keyword, country, store, or date.

Use it to hand an AI agent live access to your acquisition numbers, to pull a report into a script, or to answer one question without building a view in the dashboard.

Tip

To have an AI coding tool answer questions like “Which channel paid back best last month?” for you, install the Attribution skill. It knows which commands to run and how to read their results.

Every attribution command is read-only. Nothing you run changes a campaign, a tracking link, or an integration.

Before you start

The attribution topic uses the same installation and login as the rest of the CLI. If you have not set that up, follow steps 1 and 2 of the quickstart guide. The topic needs CLI version 0.8.8 or later. To see your version, run adapty --version.

Two more conditions apply:

  • Attribution access: Without it, report and values fail with 402 attribution_access_required. Logging in again doesn’t change that. metrics and dimensions work without access, so a working metrics call proves your login, not your access.
  • An app set up in Attribution: report and values take the app’s UUID from adapty apps list. An app that isn’t set up in Attribution, or that your Adapty user can’t read, fails with 404 attribution_app_not_found.

Build a report

Build every report in the same order: pick names from the catalogs, look up filter values, then run the report.

  1. List the metrics. Each metric comes with its unit and, for ratios, the metrics it divides by:

    adapty attribution metrics

    Some names are templates, such as d{N}_roas. Fill in the day yourself: d7_roas, d30_roas. A leading zero, as in d07_roas, is refused.

  2. List the dimensions you can group or filter by:

    adapty attribution dimensions

    --group-by takes date, campaign, adset, ad, keyword, channel, country, and store. Campaigns, ad sets, and ads filter by ID, never by name.

  3. Look up filter values. Skip this step when you don’t filter. For campaigns, it returns each campaign’s ID, name, and channel over the period:

    adapty attribution values --app <app-id> --date-from 2026-08-01 --date-to 2026-08-31 --dimension campaign
  4. Run the report:

    adapty attribution report --app <app-id> --date-from 2026-08-01 --date-to 2026-08-31 \
      --metrics spend,installs,cpi,roas,d7_roas --group-by campaign --sort roas:desc

A report returns one row for every combination of the --group-by dimensions, plus totals. Read totals from totals, not by adding rows up: ratios and unique counts don’t sum across rows.

Examples

A weekly trend for two countries in one campaign:

adapty attribution report --app <app-id> --date-from 2026-07-01 --date-to 2026-08-31 \
  --metrics spend,installs,cost_per_trial,d7_roas --group-by date,channel --granularity week \
  --filter campaign=<campaign-id> --filter country=US,GB

Trial quality by campaign, on revenue after the store’s commission:

adapty attribution report --app <app-id> --date-from 2026-07-01 --date-to 2026-08-15 \
  --metrics spend,count_trial_started,d14_count_trial_converted,cost_per_trial,d30_roas \
  --group-by campaign --revenue-basis proceeds

Predicted payback per day of installs:

adapty attribution report --app <app-id> --date-from 2026-09-01 --date-to 2026-09-29 \
  --metrics spend,d7_revenue,d90_predict_roas,d365_predict_roas --group-by date,campaign --granularity day

To get the raw response for a script or an agent, add --json to any command.

Read the results

  • Currency: Money is in USD. Revenue follows --revenue-basis: gross (the default, and the dashboard’s default), proceeds (after the store’s commission), or net (after commission and taxes). The response’s meta.query names the basis in effect.
  • Percentages: ROAS and rates are on a 0–100 scale. A roas of 150 is 150%, or 1.5x.
  • Empty values: An empty value means it can’t be computed, never zero. A ratio is empty when there is nothing to divide by, a prediction is empty when the model has no value for that day, and every spend-based metric is empty on channels Attribution has no spend for. In --json output these values are null; the table view prints —.
  • Cohort metrics: A cohort metric counts only what the cohort has done so far. d30_roas for installs from last week covers one week of revenue and will keep rising. Compare campaigns only at a day that every cohort in the period has reached.
  • Dates: Dates are days in your app’s reporting timezone. There is no timezone flag. The timezone appears as meta.query.timezone in the --json output of report and values.

Apple Search Ads spend

Attribution collects ad spend from Meta, TikTok, and Google Ads. Apple Search Ads rows carry installs and revenue, but spend, CPI, ROAS, and every other spend-based metric are empty on them. Connecting Apple Ads doesn’t add that spend to Attribution: Apple Search Ads spend and ROAS are in Ads Manager, under adapty asa metrics.

Don’t add Ads Manager numbers to Attribution rows or totals. Ads Manager reports in your campaign group’s currency and attributes installs differently.

Limits

The finest date grouping sets the widest period a report can cover:

GroupingLongest period
--granularity day31 days
--granularity week180 days
--granularity month, quarter, or year366 days
No date grouping92 days

For a longer period, use a coarser --granularity. A year of data is one report at --granularity month.

A report also has these limits:

  • At most 10,000 rows. For more, coarsen the date grouping, drop the keyword or ad grouping, or filter.
  • At most 25 metrics, and at most 100 values in one filter.
  • Prediction metrics (d{N}_predict_…) need --group-by date --granularity day and at most 2 other groupings, with at most 4 distinct horizons of up to 365 days.

adapty attribution metrics --json returns these limits in data.limits.

Run reports one after another, not in parallel: the service runs only a few queries per company at a time.

Errors

A request the service rejects exits with code 4. Input the CLI rejects before sending, such as a malformed date or --granularity without --group-by date, exits with code 2. With --json, the error includes the service’s error_code.

CodeHTTPWhat to do
auth_required401Log in again with adapty auth login.
attribution_access_required402Your company has no Attribution access. Logging in again doesn’t help.
attribution_app_not_found404Look up the app ID in adapty apps list, which lists only the apps you can read.
attribution_unknown_metric422Fix the metric names from adapty attribution metrics. The message names each invalid one, and nothing else in the report runs.
attribution_validation_error422Fix the dimension, filter, sort field, or dates named in the message. Put all values for one dimension in a single --filter.
attribution_query_too_large422Coarsen the report as the message says. See Limits.
attribution_busy429Too many queries are running for your company. Wait retry_after_seconds, then run the report once more.
attribution_upstream_unavailable, attribution_query_unavailable503Wait retry_after_seconds, then run it again. If attribution_query_unavailable repeats, make the report smaller.

The CLI never retries report or values for you, so a failed report stays failed until you run it again.

Command reference

attribution metrics

Lists every metric a report accepts, with its unit, description, and the metrics a ratio divides by, plus the report limits. Takes no flags besides --json.

attribution dimensions

Lists what --group-by and --filter accept, whether each dimension filters by ID or by value, and the granularities of date. Takes no flags besides --json.

attribution values

Lists the values one dimension takes for an app over a period. These are exactly the values report --filter accepts.

FlagRequiredDescription
--appYesApp UUID, from adapty apps list.
--date-fromYesFirst day of the period, inclusive, as YYYY-MM-DD in the app’s timezone.
--date-toYesLast day of the period, inclusive. Can’t be earlier than --date-from.
--dimensionYesA filterable dimension from adapty attribution dimensions.
--revenue-basisNogross, proceeds, or net.

For campaign, adset, and ad, each value comes with an ID, a name, and a channel. A value with an empty ID is organic or store-referrer traffic, which no filter can select.

attribution report

Runs a report: metrics over a period, grouped by dimensions.

FlagRequiredDescription
--appYesApp UUID, from adapty apps list.
--date-fromYesFirst day of the period, inclusive, as YYYY-MM-DD in the app’s timezone.
--date-toYesLast day of the period, inclusive.
--metricsYesMetric names from adapty attribution metrics, comma-separated or repeated. At most 25.
--group-byYesdate, campaign, adset, ad, keyword, channel, country, or store, comma-separated or repeated.
--granularityWith --group-by dateday, week, month, quarter, or year. Required when you group by date, and refused otherwise.
--filterNodimension=value[,value]. Several values match any of them; filters on different dimensions all apply. One --filter per dimension. Write \, for a comma inside a value.
--revenue-basisNogross (default), proceeds, or net.
--sortNofield:asc or field:desc, where the field is a requested metric or --group-by dimension. Ascending when you leave out the direction. Empty values sort last.

Rows grouped by campaign, adset, or ad carry that entity’s ID, name, and channel. A campaign’s name is the latest name it had within the period, so match campaigns across periods by ID. A date value is the first day of its bucket.

What’s next