Stripe Revenue Overview: Gross, Refunds, Fees and Net From an Export
By MCP Analytics Team | Published , updated
What this tutorial does
You will take a CSV export of your Stripe balance activity and work out four things: gross revenue, refunds, fees, and net revenue, first in total and then month by month. You do not need an API key or any connection to your Stripe account. You work from a file.
The worked example below uses a small file that we made up. Every figure on this page comes from running the code shown, on that made-up file. None of it is real revenue, and none of it is a benchmark for your business.
This is a reading of past activity. It does not forecast.
Step 1: Export the data from Stripe
Stripe describes the export in its documentation for the Balance summary report. In Stripe's words: "You can export an itemized CSV of your complete transaction history, including any associated custom metadata." Stripe also says that "To export data from a report section as a CSV file, click Export in the section's upper-right corner, then select CSV", and that "CSV files contain data only from the section where you start the export."
For this tutorial you want the itemized rows from the Balance change from activity section, because that section has one row per transaction. Stripe's page lists the available columns and says you can customize them. Follow Stripe's page for the exact steps in your account, since the screens can change: Stripe docs: Balance summary report.
Stripe's page also says reports use the date range, currency and time zone you choose, and that Dashboard reports include only complete days. So a month in progress will look smaller than a finished month. Compare finished months.
Step 2: Know the columns you need
Stripe's documentation describes these columns of the itemized report. The descriptions are Stripe's, shortened.
| Column | What Stripe says it is |
|---|---|
created | Time at which the balance transaction was created. |
reporting_category | A categorization of balance transactions, meant to improve on the older type field. Charges and refunds each have their own category. |
gross | Gross amount of the transaction, in major units of the currency (dollars for USD). |
fee | Fees paid for this transaction, in major units of the currency. |
net | Net amount of the transaction, in major units of the currency. |
currency | Three-letter ISO code of the currency in which gross, fee and net are defined. |
Sources: Balance summary report and Reporting categories and types. The second page says reporting_category groups card charges and other payment methods under charge, and refunds under refund.
Check that your file is in one currency before you add anything up. Adding dollars to yen gives a number that means nothing.
Step 3: Worked example on a made-up file
We built a small file with a fixed random seed (42): six months, January to June 2025, with 375 rows. It uses the column names above. The fee rule in it (3% plus 30 cents) is made up for the example and is not Stripe's pricing. In a real export, the signs of refund rows may differ, so look at your own file first. In this made-up file, refunds are negative amounts.
This is the script that makes the file. You do not need it for your own data. It is here so you can reproduce every number on this page.
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
rows = []
for month in range(1, 7): # January to June 2025, made up
n = 40 + 5 * month # a few more charges each month
for _ in range(n):
day = int(rng.integers(1, 28))
gross = round(float(rng.uniform(15, 140)), 2)
fee = round(gross * 0.03 + 0.30, 2) # made-up fee rule, not Stripe's
rows.append((f"2025-{month:02d}-{day:02d}", "charge", gross, fee, round(gross - fee, 2), "usd"))
if rng.random() < 0.06: # about 6 in 100 charges get refunded
rows.append((f"2025-{month:02d}-{min(day + 2, 28):02d}", "refund", -gross, 0.0, -gross, "usd"))
df = pd.DataFrame(rows, columns=["created", "reporting_category", "gross", "fee", "net", "currency"])
df.sort_values("created").to_csv("made_up_stripe_export.csv", index=False)
print(len(df), "rows written")
The first rows of the file:
created,reporting_category,gross,fee,net,currency
2025-01-03,charge,69.86,2.4,67.46,usd
2025-01-03,charge,35.16,1.35,33.81,usd
2025-01-03,charge,105.29,3.46,101.83,usd
2025-01-04,charge,40.3,1.51,38.79,usd
Now the analysis. It reads the CSV, groups by reporting_category, then groups by month. It uses pandas, a free Python library for tables.
import pandas as pd
df = pd.read_csv("made_up_stripe_export.csv", parse_dates=["created"])
df["month"] = df["created"].dt.strftime("%Y-%m")
print(df.head(4).to_string(index=False))
print()
by_cat = df.groupby("reporting_category")[["gross", "fee", "net"]].sum().round(2)
by_cat["rows"] = df.groupby("reporting_category").size()
print(by_cat)
print()
monthly = df.pivot_table(index="month", columns="reporting_category",
values="gross", aggfunc="sum", fill_value=0)
monthly["fees"] = df.groupby("month")["fee"].sum()
monthly["net"] = df.groupby("month")["net"].sum()
monthly["net_change_pct"] = monthly["net"].pct_change() * 100
print(monthly.round(2))
Output (from running the code above)
created reporting_category gross fee net currency month
2025-01-03 charge 69.86 2.40 67.46 usd 2025-01
2025-01-03 charge 35.16 1.35 33.81 usd 2025-01
2025-01-03 charge 105.29 3.46 101.83 usd 2025-01
2025-01-04 charge 40.30 1.51 38.79 usd 2025-01
gross fee net rows
reporting_category
charge 26800.90 907.4 25893.50 345
refund -2146.53 0.0 -2146.53 30
reporting_category charge refund fees net net_change_pct
month
2025-01 3294.62 -153.45 112.35 3028.82 NaN
2025-02 4131.02 -169.83 138.91 3822.28 26.20
2025-03 3826.12 -66.27 131.26 3628.59 -5.07
2025-04 4264.32 -542.04 145.91 3576.37 -1.44
2025-05 5740.65 -927.53 191.68 4621.44 29.22
2025-06 5544.17 -287.41 187.29 5069.47 9.69
Step 4: What each output column means
The first table (by reporting category)
- gross: the sum of the gross column for that category. Charges add up to 26,800.90. Refunds add up to -2,146.53, negative because they take money back.
- fee: the sum of fees. In this made-up file only charge rows carry a fee (907.40) and refund rows carry none. Check your own file to see whether refund rows carry a fee.
- net: gross minus fee, summed.
- rows: how many transactions are in that category (345 charges, 30 refunds).
Putting it together for the whole period: gross charges 26,800.90, minus refunds 2,146.53, minus fees 907.40, gives 23,746.97 net. That equals the net charges (25,893.50) plus the net refunds (-2,146.53). Refunds are about 8.0% of gross charges and fees are about 3.4% of gross charges, in this made-up file only.
The second table (by month)
- charge: gross charges in that month.
- refund: gross refunds, dated by the day the refund row was created, not the day of the original charge. A refund can land in a later month than its charge.
- fees: total fees that month.
- net: net of charges and refunds for that month.
- net_change_pct: the percent change in net from the month before. The first month shows NaN ("not a number") because it has no earlier month.
In this file, net moved from 3,028.82 in January to 5,069.47 in June, with a dip in the months between. Refunds were largest in May (-927.53). Six months is too short to tell a trend from a seasonal pattern, and the file is random, so do not read anything into the shape.
Limits of this method
- A balance export lists transactions that affected your Stripe balance. It does not hold attempts that never produced a charge, so it cannot give you a payment success rate. Stripe's documentation for other reports covers other questions.
- Dates depend on the time zone you chose when exporting. Months near a boundary can shift if you compare with a report built in another time zone.
- Mixed currencies, disputes, payouts and other categories need separate handling. Group by
reporting_categoryand look at each before you sum. - Revenue here means money processed through Stripe. It is not profit.
- Before you trust any total, open your own file and check two things: the sign of the refund rows (negative or positive) and which rows carry a fee. If your file differs from the made-up one, adjust the sums to match.
- The example is made up. A rate such as 8.0% refunds means nothing for your business.
Run it on your own Stripe export
If you would rather not write the code, you upload a CSV export at MCP Analytics and ask for a revenue overview. The method is shown for every result, and the same file gives the same numbers when you run it again. Nothing connects to your Stripe account. Which analysis fits depends on the columns in your file, so check the result against what you expected.
Sources
- Stripe docs: Balance summary report (export steps, columns, reporting periods)
- Stripe docs: Reporting categories and types
- Stripe docs: How to select a report
Explore more: Stripe Analytics: all tools, tutorials, and guides →