Metric

What is course completion rate, and how do you measure it in Metabase?

Definition

Course completion rate is the share of enrolled students who finish a course. Two decisions make or break the number: what 'complete' means — every lesson marked done, a certificate issued, or a final assessment passed — and which enrollments belong in the denominator. A student who enrolled yesterday has not failed to complete; they have not had time to.

Formula: Completion rate = completed enrollments / enrollments in the cohort that have passed the maturity window × 100

What data do you need?

  • Enrollment records with enrolled_at, access type (paid, free, bundled), and refund status
  • Lesson-level progress events with a completion timestamp per lesson
  • The required-lesson list per course, and whether optional lessons count
  • Certificate or final-assessment results if those define completion
  • A maturity window per course — the elapsed time a typical finisher needs

SQL pattern

Completion rate by course and access type, matured cohort onlyPostgreSQL
WITH cohort AS (
  -- Denominator: only enrollments old enough to plausibly finish.
  -- Widen the window for long courses; state it on the dashboard.
  SELECT
    enrollment_id,
    course_id,
    student_id,
    access_type
  FROM creator_enrollments
  WHERE enrolled_at < CURRENT_DATE - INTERVAL '90 days'
    AND status <> 'refunded'
), required AS (
  -- Required lessons per course. If a course adds lessons later,
  -- students who already "finished" quietly drop below 100%.
  SELECT
    course_id,
    COUNT(DISTINCT lesson_id) FILTER (WHERE is_required) AS lessons_required
  FROM creator_lesson_progress
  GROUP BY 1
), done AS (
  SELECT
    enrollment_id,
    COUNT(DISTINCT lesson_id) FILTER (
      WHERE is_required AND completed_at IS NOT NULL
    ) AS lessons_done
  FROM creator_lesson_progress
  GROUP BY 1
)
SELECT
  c.course_id,
  c.access_type,
  COUNT(*) AS matured_enrollments,
  -- LEFT JOIN leaves lessons_done NULL for never-started enrollments;
  -- the comparison is NULL, so FILTER correctly counts them as not done.
  COUNT(*) FILTER (WHERE d.lessons_done >= r.lessons_required)
    AS completed,
  ROUND(
    100.0 * COUNT(*) FILTER (WHERE d.lessons_done >= r.lessons_required)
    / NULLIF(COUNT(*), 0), 1
  ) AS completion_rate_pct,
  ROUND(
    100.0 * COUNT(*) FILTER (WHERE COALESCE(d.lessons_done, 0) = 0)
    / NULLIF(COUNT(*), 0), 1
  ) AS never_started_pct
FROM cohort c
JOIN required r USING (course_id)
LEFT JOIN done d USING (enrollment_id)
GROUP BY 1, 2
ORDER BY matured_enrollments DESC;

Common pitfalls

Dividing by every enrollment ever created.→ Last month's signups drag the rate down mechanically. Use a cohort that has passed a stated maturity window, and report the window next to the number.
Blending free, bundled, and paid enrollments.→ Free and bundle-included seats complete at a fraction of the paid rate. Segment by access type — one blended rate mostly measures your promo mix.
Leaving 'complete' undefined across dashboards.→ Pick one: all required lessons done, certificate issued, or final assessment passed. Publish the definition on the card; the three can differ by 20 points.
Ignoring never-started enrollments.→ Track them as their own number. A low completion rate driven by people who never opened lesson one is an onboarding problem, not a curriculum problem.

Where does this metric apply?

This metric commonly uses data from Kajabi, Thinkific, Teachable, Podia, Circle, plus any warehouse models built at the same grain.

Dashboards

Integrations

Metrics

Analytics

FAQ

What is a good course completion rate?
Paid, cohort-based courses commonly land in the 40–70% range; self-paced evergreen courses often sit well under 20%. Comparing against public benchmarks is mostly noise because definitions differ — compare each course against its own history and against sibling courses using the same definition.
How does completion relate to revenue metrics?
Completion is the leading indicator for refunds, renewals, and word of mouth. Pair it with repeat purchase rate and LTV — students who finish buy the next course at materially higher rates. The course revenue dashboard puts both on one page.
Can Metabase read this from my course platform directly?
Metabase queries SQL databases and warehouses, not SaaS APIs. Sync enrollment and lesson-progress tables from Kajabi, Thinkific, or Teachable into your warehouse (or upload a CSV export), then point Metabase at that.