Use CasesAIPostgreSQLMySQL

Building a Revenue Dashboard from Stripe Data Without SQL

Stripe knows everything about your revenue. Every charge, subscription, refund, and payout is sitting in their system. The problem is that Stripe's default d...

Priya Sharma· Product LeadApril 7, 20268 min read

Stripe knows everything about your revenue. Every charge, subscription, refund, and payout is sitting in their system. The problem is that Stripe's default dashboard gives you a narrow window into that data good for a quick check, not for the deeper questions that actually drive decisions.

What was net revenue last month after refunds? Which plan is growing fastest? What's the average subscription lifetime by acquisition channel? How does MRR this quarter compare to last? These questions aren't hard to answer, but they require getting your Stripe data into a database and analyzing it there.

This guide walks through exactly how to do that and how to build a live revenue dashboard from it without writing a line of SQL.

Getting Your Stripe Data into a Database

Stripe gives you two paths to get your data out: their API and their data pipeline product (Stripe Data).

Option 1: Stripe Data (recommended)

Stripe Data syncs your Stripe data directly to a database. It supports Snowflake, Redshift, and BigQuery natively. If you're on one of those, this is the cleanest option Stripe handles the schema, the syncs, and the incremental updates.

Option 2: Sync via API to PostgreSQL or MySQL

If you're on PostgreSQL or MySQL, you can pull the key tables using Stripe's API and insert them yourself. The tables you care most about:

  • charges every payment attempt
  • subscriptions recurring billing records
  • invoices line items and totals
  • customers your subscriber base
  • refunds reversals
  • A lightweight sync for PostgreSQL might look like:

    import stripe
    import psycopg2
    
    stripe.api_key = "sk_live_..."
    
    # Pull all charges from the last 30 days
    charges = stripe.Charge.list(
        created={"gte": int((datetime.now() - timedelta(days=30)).timestamp())},
        limit=100
    )
    
    conn = psycopg2.connect("postgresql://...")
    cur = conn.cursor()
    
    for charge in charges.auto_paging_iter():
        cur.execute("""
            INSERT INTO stripe_charges
              (id, amount, currency, status, customer_id, created_at, refunded)
            VALUES (%s, %s, %s, %s, %s, to_timestamp(%s), %s)
            ON CONFLICT (id) DO UPDATE SET
              status = EXCLUDED.status,
              refunded = EXCLUDED.refunded
        """, (
            charge.id,
            charge.amount / 100,  # Stripe stores in cents
            charge.currency,
            charge.status,
            charge.customer,
            charge.created,
            charge.refunded
        ))
    
    conn.commit()

    Option 3: Third-party connectors

    Tools like Fivetran, Airbyte, or Stitch automate the Stripe-to-database sync entirely. If you're already using one of these for other sources, adding Stripe is a few clicks.

    Once your Stripe data is in a database, you're ready to build.

    The Core Revenue Metrics to Track

    A revenue dashboard that actually gets used answers five questions at a glance:

    1. MRR (Monthly Recurring Revenue) the sum of all active subscription amounts normalized to monthly. This is your north-star number.

    2. Net Revenue total charges minus refunds in a given period. Stripe's gross revenue number can be misleading if refunds are significant.

    3. Churn subscriptions that cancelled in a period, expressed as a percentage of total active subscriptions at the start of that period.

    4. New vs. Expansion vs. Churned MRR breaking MRR movement into its components shows whether growth is coming from new customers or expansion of existing ones.

    5. Revenue by plan which tier is generating the most revenue, and which is growing fastest.

    Here's the SQL for MRR from an active subscriptions table:

    SELECT
      DATE_TRUNC('month', current_date) AS month,
      SUM(
        CASE
          WHEN billing_interval = 'month' THEN amount
          WHEN billing_interval = 'year'  THEN amount / 12
        END
      ) AS mrr
    FROM stripe_subscriptions
    WHERE status = 'active';

    And monthly net revenue (charges minus refunds):

    SELECT
      DATE_TRUNC('month', created_at) AS month,
      SUM(amount) AS gross_revenue,
      SUM(amount_refunded) AS refunds,
      SUM(amount) - SUM(amount_refunded) AS net_revenue
    FROM stripe_charges
    WHERE status = 'succeeded'
    GROUP BY 1
    ORDER BY 1 DESC;

    Building the Dashboard Without Writing SQL

    If you're a SaaS founder, a RevOps analyst, or a product manager, you probably don't want to maintain a library of SQL queries. You want to open a dashboard and see your numbers.

    AI for Database connects directly to your database whether that's PostgreSQL, MySQL, BigQuery, Snowflake, or Supabase and lets you ask revenue questions in plain English. The system translates your question into SQL, runs it, and returns a chart or table.

    For a Stripe revenue dashboard, you'd ask questions like:

  • "What is my MRR for each of the last 12 months?"
  • "Show me monthly net revenue after refunds for this year"
  • "Which pricing plan has the most active subscribers?"
  • "How many subscriptions churned last month vs. the month before?"
  • "What's the average subscription value by plan tier?"
  • "Show me the top 20 customers by lifetime revenue"
  • Each question becomes a panel in your dashboard. You arrange them, give them titles, and set the refresh interval hourly, daily, weekly, whatever makes sense for your business cadence.

    The result is a live revenue dashboard built from your actual Stripe data, not a static export or a screenshot from Stripe's interface.

    Tracking MRR Movement: New, Expansion, and Churn

    The most valuable revenue analysis isn't just the total MRR number it's understanding where it came from and where it went.

    MRR movement breaks into four buckets:

  • New MRR revenue from customers who were not subscribed the previous month
  • Expansion MRR additional revenue from existing customers who upgraded
  • Contraction MRR reduced revenue from customers who downgraded
  • Churned MRR revenue lost from customers who cancelled
  • To calculate this from a subscriptions table, you compare each customer's subscription amount this month to last month:

    WITH monthly_sub AS (
      SELECT
        customer_id,
        DATE_TRUNC('month', created_at) AS month,
        amount,
        LAG(amount) OVER (PARTITION BY customer_id ORDER BY DATE_TRUNC('month', created_at)) AS prev_amount,
        LAG(status)  OVER (PARTITION BY customer_id ORDER BY DATE_TRUNC('month', created_at)) AS prev_status
      FROM stripe_subscriptions
      WHERE status IN ('active', 'canceled')
    )
    SELECT
      month,
      SUM(CASE WHEN prev_amount IS NULL AND amount > 0              THEN amount ELSE 0 END) AS new_mrr,
      SUM(CASE WHEN prev_amount IS NOT NULL AND amount > prev_amount THEN amount - prev_amount ELSE 0 END) AS expansion_mrr,
      SUM(CASE WHEN prev_amount IS NOT NULL AND amount < prev_amount AND amount > 0 THEN amount - prev_amount ELSE 0 END) AS contraction_mrr,
      SUM(CASE WHEN prev_status = 'active' AND status = 'canceled'  THEN -prev_amount ELSE 0 END) AS churned_mrr
    FROM monthly_sub
    GROUP BY 1
    ORDER BY 1 DESC;

    This query is the foundation of a proper MRR waterfall chart. In AI for Database, you can get this breakdown by just asking: "Show me new, expansion, and churned MRR by month for the last 12 months."

    Monitoring Churn and Subscription Health

    Churn is the metric that kills SaaS companies slowly. Tracking it at the aggregate level is table stakes. The more useful analysis is understanding who churns and when.

    Time-to-churn by plan do annual subscribers churn at renewal? Do monthly subscribers leave after 2-3 months?

    SELECT
      plan_name,
      ROUND(AVG(
        EXTRACT(DAY FROM (cancelled_at - created_at))
      ), 0) AS avg_days_to_cancel
    FROM stripe_subscriptions
    WHERE status = 'canceled'
      AND cancelled_at IS NOT NULL
    GROUP BY plan_name
    ORDER BY avg_days_to_cancel;

    Cohort retention for customers who started in a given month, what percentage are still active after 1, 3, 6, and 12 months?

    This requires a cohort analysis query, which is more involved but extremely valuable. In AI for Database, you can ask "Show me 12-month subscription retention by signup cohort" and it generates the query automatically against your Stripe data.

    Cancellation timing are customers cancelling at the end of a billing period or mid-cycle? Mid-cycle cancellations often indicate a specific pain point (a failed integration, a billing dispute, a competitive loss). End-of-period cancellations are more often about budget or product-market fit.

    Segmenting Revenue for Strategic Decisions

    Once your Stripe data is queryable, segmentation opens up. A few questions worth having in your dashboard:

    Revenue by geography if Stripe collects billing country, you can see which markets are generating real revenue vs. which you're investing in speculatively.

    Revenue by acquisition source if you're passing a metadata field on Stripe customers (utm_source, referral code, plan selected during signup), you can trace revenue back to marketing channels. This turns your Stripe data into a lightweight attribution model.

    Failed payment recovery how much revenue is at risk from failed charges? How effective is your dunning sequence?

    SELECT
      DATE_TRUNC('month', created_at) AS month,
      COUNT(*) FILTER (WHERE status = 'failed') AS failed_charges,
      SUM(amount) FILTER (WHERE status = 'failed') AS failed_revenue,
      COUNT(*) FILTER (WHERE status = 'succeeded' AND previous_status = 'failed') AS recovered_charges,
      ROUND(
        COUNT(*) FILTER (WHERE status = 'succeeded' AND previous_status = 'failed')::numeric /
        NULLIF(COUNT(*) FILTER (WHERE status = 'failed'), 0) * 100, 1
      ) AS recovery_rate_pct
    FROM stripe_charges
    GROUP BY 1
    ORDER BY 1 DESC;

    Setting Up Revenue Alerts

    A dashboard you check once a week is useful. An alert that tells you when something important happens is better.

    AI for Database's action workflows let you define conditions on your Stripe data and fire notifications when they're met. Examples:

  • "When MRR drops more than 5% week-over-week, send a Slack message"
  • "When daily new subscriptions fall below 3 for two consecutive days, send an email"
  • "When the failed payment rate exceeds 8% in any week, notify the RevOps channel"
  • These run on a schedule against your live database. No custom code, no cron jobs, no maintaining a separate alerting infrastructure.

    Ready to try AI for Database?

    Query your database in plain English. No SQL required. Start free today.