ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

Stripe Revenue Overview: Gross, Refunds, Fees and Net From an Export

By MCP Analytics Team | Published , updated

Category: Stripe Analytics

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.

ColumnWhat Stripe says it is
createdTime at which the balance transaction was created.
reporting_categoryA categorization of balance transactions, meant to improve on the older type field. Charges and refunds each have their own category.
grossGross amount of the transaction, in major units of the currency (dollars for USD).
feeFees paid for this transaction, in major units of the currency.
netNet amount of the transaction, in major units of the currency.
currencyThree-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)

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)

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

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.

Try it on your data

Sources

Explore more: Stripe Analytics: all tools, tutorials, and guides →

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai