Skip to main content

Base URL and authentication

Takes Authorization: Bearer <YOUR_API_KEY> (details) and the stats:read permission.
Every endpoint on this page rejects a parameter it does not recognise with a 422, rather than ignoring it. A filter we cannot apply would otherwise come back as a 200 carrying a number that answers a different question, and you would have no way to tell.
site_id is the site’s snippet key, the ttid_ value your install snippet carries as data-site. You can read it from GET /v1/sites.

Currency

Every amount is stored in USD. amount_usd, revenue_usd and ltv_usd are always USD, whatever your site’s display currency is. Where a display currency is set, the _display field beside each amount carries the same money converted at the European Central Bank reference rate for that row’s own date, so a figure for last Tuesday does not move when today’s rate does. If any date in the window has no published rate, the _display field is null. It is never the USD number relabelled. The response tells you what happened:

Refunds

Every amount on this page is net of refunds. A refund is subtracted from whichever AI source the original payment was attributed to, on all six revenue paths: Stripe, Shopify, Lemon Squeezy, Polar, Paddle, and anything you post to the Payment API. Four things follow, and they are worth knowing before you build a report on these numbers:
  • A refund is dated on the day it was issued, not the day of the original payment. A window you queried last week returns the same figures today. Refunds land in the window they happened in.
  • An amount can be negative. A day, or a source, that gave back more than it took in during your window is a negative number. Do not clamp it at zero: the negative is the answer.
  • Conversion counts stay gross. attributed_sessions on /breakdown and payments on /ltv count the purchase that happened. A refund does not erase it. So a source can show conversions and negative revenue in the same window, and both are correct.
  • A cancelled subscription is not a refund. Cancelling stops future charges; the charges already paid stay in ltv_usd and in every window that contains them.
There is no separate gross figure on this API. If you need “what did this source bill before returns”, that is your payment processor’s own reporting, not Traceten’s.

GET /v1/revenue/breakdown

Which AI sources produced revenue in the window. Top 5 by revenue, highest first.
Ranking uses the stored USD figure, so the order of the rows cannot change when the exchange rate does.

Query parameters

Sending from without to (or the reverse) is a 422. When you omit attribution_model, every model is summed together. Pass the model you actually report on if you want a single view. source_kind: "unmatched" is revenue we could not tie back to any session. It is worth reading: it is the gap between what your payment provider recorded and what Traceten could attribute.

Response fields

GET /v1/revenue/timeseries

Daily revenue per AI source, ordered by date and then by source. This is the data behind the revenue chart in the dashboard.
Takes the same parameters as /breakdown, plus interval. Only interval=day is supported today.

GET /v1/revenue/sku-breakdown

Which products the AI-referred traffic actually bought. Shopify only.
The default window is 30 days here, wider than the other revenue endpoints, because product-level data is sparser. The response is capped at 200 rows. units_sold and revenue_usd are both net of returns. A partial refund takes back only the line items that came back, so a returned SKU loses its units and its revenue while the rest of the order keeps both. Either figure can be negative in a window where returns outran sales.

Query parameters

This endpoint takes a narrower set than the other revenue routes. attribution_model and source_kind are not supported here, because the underlying product-level rollup has no column for either. Sending one is a 422, not a silent no-op. Any other parameter is rejected.

If you have not connected Shopify

This endpoint returns 403 when the account has no Shopify connection, and 404 when the site is not one your key can reach. The two are distinct on purpose, and they answer different questions: by the time you can see the 403 you have already proved you own the site, so it tells you only that the connection is missing. Connect Shopify in the dashboard, then retry.

GET /v1/revenue/ltv

Cumulative attributed recurring revenue per subscription. This is the SaaS number: Claude referred this customer, and they have now paid you this much.

This endpoint takes no date range

Lifetime value is cumulative and all-time. A window would contradict the number it reports, so from, to and days are rejected with a 422, not ignored. Use /breakdown or /timeseries when you want a period.

Query parameters

Reading the response

ai_source is the source that acquired the customer, not the source on the most recent payment. That is the whole point of the metric. ltv_usd is always USD, and it is net of refunds. This endpoint does not convert to a display currency. payments stays gross, so a subscription that was billed twice and refunded once reads two payments and one payment’s worth of value. first_payment_at and last_payment_at are the first and last payments. A refund never becomes a subscription’s last payment. total_count counts (subscription_id, ai_source) pairs, because that is the unit rows lists. A subscription credited to two sources is two rows and two counts. has_more is derived from the page coming back full, so it can be true on an exact multiple of page_size with an empty next page. total_count is exact if you would rather do the arithmetic.

Errors

Standard error shapes apply. The ones specific to these endpoints:

Next

  • Sessions API to drill from a revenue number into the sessions behind it.
  • Sources API for session counts per source.