Webclat logoWebclat . | Amplitude Solutions

QA - revenue tracking

Why do my Amplitude revenue properties ($price/$quantity/$revenue) get set to zero after sending?

The fix, why it happens, and how to confirm it actually worked in your own instance.

The fix, in short

Amplitude's revenue reporting reads from events sent through its dedicated Revenue interface (a revenue() call built from a Revenue object with setProductId/setPrice/setQuantity), not from arbitrary event properties named price, quantity, or revenue - sending them as plain properties on a regular track() call is the single most common reason revenue reports show zero even though the event itself arrived. The fix is to switch to the Revenue interface, or if you must send raw properties, to confirm they're numeric types, not strings.

Why this happens

Amplitude's revenue charts (Revenue analysis, LTV, ARPU) are computed from a specific, recognized revenue event shape - the SDK's Revenue interface builds this for you and sends it as a properly typed event (historically named "revenue_amount" internally, tagged so Amplitude's revenue reports can find it), not from any event with a property that happens to be named `price` or `revenue`. A regular custom event with properties called `$price` or `revenue` looks, to Amplitude's revenue reporting, like any other custom event property - visible in that event's raw property list, invisible to revenue charts.

Even when a team does use the Revenue interface correctly, a second cause of the same symptom is type mismatch: price and quantity need to be numbers. A price value that arrives as the string `"19.99"` instead of the number `19.99` - common when the value comes straight from a form input or a URL parameter without an explicit cast - can compute to zero or fail the multiplication Amplitude performs internally (`price x quantity = revenue`) depending on SDK version and how strictly it coerces the input.

A third, subtler cause: currency-unit mismatch between systems. If your payment processor reports amounts in the smallest currency unit (cents, as Stripe does by default) and that raw integer gets sent as "price" without converting to a decimal major-unit amount, the number itself isn't zero, but it's off by a factor of 100 - which can look like "zero-ish, rounds-to-nothing" revenue on a chart scaled for real dollar amounts, especially at low quantities.

Fix it

Migrating from plain event properties to the Revenue interface

  1. Find every place in your codebase currently sending price/quantity/revenue as plain properties on a `track()` call - search for property keys like `price`, `revenue`, `$price`, `$revenue` in your event-tracking code.
  2. Replace each one with the Revenue object: construct it with `new amplitude.Revenue().setProductId(sku).setPrice(numericPrice).setQuantity(numericQty)`, adding `.setRevenueType("purchase")` (or another label appropriate to the transaction type) if you distinguish purchase types.
  3. Send it via `amplitude.revenue(revenueObj)` rather than `amplitude.track()` - this is a distinct method specifically so Amplitude's ingestion can tag the event correctly for revenue reporting.
  4. Keep the underlying business event too if you want it (e.g. a `"Purchase Completed"` track() event for funnel analysis) - the Revenue interface and a regular tracked event aren't mutually exclusive, and most teams send both: one for revenue reporting, one for behavioral funnels.

Already using the Revenue interface, still reading zero

  1. Log the exact value and JavaScript `typeof` of what you're passing into `setPrice()` and `setQuantity()` immediately before the call - if either logs as `"string"` instead of `"number"`, cast explicitly with `Number(value)` or `parseFloat(value)` before constructing the Revenue object.
  2. Check your payment/checkout source for the currency-unit convention it uses (cents vs. major units) and convert once, at the boundary where the value enters your tracking code, so every downstream consumer of that value is in agreement.
  3. Confirm quantity defaults to at least 1, not 0 or undefined, for a single-item purchase - a missing quantity defaulting to zero silently zeroes out the whole revenue calculation (price x 0 = 0) even with a perfectly correct price.

How to verify it worked

  1. In Amplitude, build a Segmentation chart with the metric set to "Revenue" (or "Event Total Revenue" in older chart-type naming) filtered to your test user or device ID, and confirm a non-zero value appears for the transaction you just sent.
  2. Open the raw event in User Look-Up / User Activity and check that it's tagged internally as a revenue event (it should carry Amplitude's revenue-specific fields) rather than showing up only as a generic custom event with a `price` property sitting unused in the properties list.
  3. Send two test transactions with different, deliberately distinct price and quantity values, and confirm the revenue chart's total equals the sum you'd expect by hand - this catches a currency-unit or multiplication bug that a single zero/non-zero check would miss.
  4. If you migrated from plain properties, pull up a chart scoped to the date range before the fix and compare it to a chart scoped to after - a step-change in reported revenue at the exact deploy time confirms the fix, not a coincidence in real purchase volume.

Related: How do I merge Branch.io/Segment attribution data with Amplitude's own user identity so it's not double-counted? · What's the correct setup sequence to get Amplitude tracking events inside a React, React Native, or Expo app?

Frequently asked questions

Do I need to backfill historical events that were sent the wrong way?+

Amplitude doesn't offer a way to retroactively reclassify an already-ingested custom event as a revenue event, so historical revenue reporting for the affected period will stay incomplete. Document the gap with the date the fix shipped, and treat revenue trends spanning that boundary as not directly comparable.

Can I still see revenue by product or SKU, not just a total?+

Yes - the productId you pass into the Revenue object (via setProductId) is exactly what lets you break revenue charts down by product, which is a second reason to use the dedicated interface instead of a generic event property: Amplitude's revenue-specific charting expects that field to exist in the shape it produces.

Still stuck, or want it checked properly?

The free scored audit reads your Amplitude instance across ten dimensions - taxonomy health, identity resolution, revenue tracking, and more - and hands back the three highest-payoff fixes. No commitment, 48-hour turnaround.

Request the free audit