Back to blog
Business

Metrics that matter for dental clinics (and that the website must track)

By Flávio Emanuel · · 10 min read

A dentist in Curitiba called me frustrated. “I invested R$ 3k in a website + Local SEO and I’m not seeing new patients”. I asked: “How many bookings did you receive through the website?”.

He didn’t know. He had a website, but zero tracking.

Spent R$ 3k, had no idea if it was generating leads or not.

That’s the reality of 90% of clinics. Website works (theoretically), but nobody watches the numbers. Nobody knows:

  • How many people visit
  • How many click “book”
  • How many actually book
  • How many of those bookings become real patients

Without these numbers, you’re navigating in the dark.

I’ll show you which metrics really matter, real benchmarks from clinics, and how to set up a simple dashboard in 2 hours.

The 5 metrics that matter

You don’t need 50 KPIs. You need 5 that you watch religiously:

1. Cost Per Lead (CPL)

How much you spend (on marketing, website, etc) to get one potential patient contact.

Formula: (Total marketing investment) / (Total leads generated in the month)

Example: you invested R$ 2,500 in website + Local SEO. This month you received 10 bookings via website. CPL = R$ 2,500 / 10 = R$ 250 per booking.

Realistic benchmark:

  • Small clinic (startup): CPL R$ 150-400
  • Medium clinic: CPL R$ 100-200
  • Large clinic (established brand): CPL R$ 50-100

If your CPL is above R$ 400, something’s wrong (bad website, poor Local SEO, or very competitive market).

2. Average Ticket

How much you make per patient on average.

Formula: (Total revenue in the month) / (Total patients seen)

Example: you made R$ 36k, saw 40 patients. Average ticket = R$ 36k / 40 = R$ 900 per patient.

Realistic benchmark:

  • Clinic with cleaning/check-up only: R$ 200-400
  • General clinic (cleaning + fillings + whitening): R$ 600-1,200
  • Cosmetic clinic (implant, prosthetic, veneer): R$ 1,500-4,000+

Tip: new patient usually comes for just initial consultation (R$ 100-300). Average ticket goes up when they do treatment. So don’t expect R$ 1,500 from a new patient. Expect R$ 400 (consultation + X-ray + small filling).

3. Show-up Rate

Percentage of patients who actually show up for their booked appointment.

Formula: (Total consultations where patient attended) / (Total appointments booked)

Example: you booked 25 appointments. 22 patients showed up. 3 no-showed. Show-up rate = 22/25 = 88%.

Realistic benchmark:

  • Without automation (just paper schedule): 70-80%
  • With WhatsApp/SMS reminders: 85-92%
  • With online booking + automatic confirmation: 90-95%

If your rate is below 85%, implement reminder automation. Every no-show is money you lose.

4. Conversion Rate: Site → Booking

How many website visitors turned into actual bookings.

Formula: (Total bookings created via website) / (Total website visitors in the month)

Example: your site got 500 visitors this month. 18 booked. Rate = 18/500 = 3.6%.

Realistic benchmark:

  • Bad site (no clear CTA, slow, ugly): 0.5-1%
  • Average site (good, but no local optimization): 2-3%
  • Well-optimized site (Local SEO, mobile-first, good CTA): 4-7%

If your rate is below 1%, something’s very wrong with the website. If above 5%, congrats, you’re above average.

5. Conversion Rate: Booking → Consultation → Recurring Patient

How many bookings turned into patients who came back.

This one’s hard to measure, but it’s the most important.

Formula: (Total patients who returned 2+ times) / (Total new patients)

Example: you gained 20 new patients. 15 came back. 5 disappeared. Rate = 15/20 = 75%.

Realistic benchmark:

  • Clinic with poor service: 30-50%
  • Clinic with good service: 60-80%
  • Clinic with excellent service + retention program: 80-95%

Every patient who doesn’t come back is money left on the table. One recurring patient is worth 3-5x more than a new patient (lower acquisition cost, they know you, they refer friends).

Table: Benchmarks by clinic type

MetricSmall ClinicMedium ClinicLarge Clinic
CPLR$ 200-400R$ 100-200R$ 50-100
Average ticketR$ 400-800R$ 800-1,500R$ 1,200-2,500+
Show-up rate80-85%85-90%90-95%
Conversion (site → booking)2-3%3-5%5-8%
Patient retention60-70%70-85%85-95%

How to measure with lightweight tools

You don’t need Google Analytics (heavy, asks for consent). There are alternatives that are:

  • Lightweight (won’t slow down site)
  • Privacy-first (no LGPD headache)
  • Affordable

Option 1: Plausible

Plausible is privacy-focused analytics.

Cost: R$ 80-150/month Setup: 2 minutes (copy 1 line of JS) Data: pageviews, source, conversions, time on site

<script defer data-domain="clinic.com" src="https://plausible.io/js/script.js"></script>

Done. Starts recording visitors.

Advantage: beautiful reporting, real-time data, complete Brazil support. Disadvantage: doesn’t show specific customer data (privacy, obviously).

Option 2: Umami

Umami is open-source, you run it on your own server.

Cost: R$ 0 (self-hosted) or R$ 50/month (cloud) Setup: 5 minutes (run Docker container or attach JS) Data: pageviews, source, custom events, funnels

<script async src="https://your-umami.com/script.js" data-website-id="XXXXX"></script>

Advantage: cheap, your data, customizable, events. Disadvantage: technical setup (if self-hosted), less intuitive than Plausible.

Option 3: Supabase + custom event

If you already use Supabase, use it for free as event database.

// src/lib/tracking.ts
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(URL, KEY);

export async function trackEvent(
  eventName: string,
  metadata: Record<string, any>
) {
  await supabase.from('events').insert({
    event_name: eventName,
    metadata,
    timestamp: new Date().toISOString(),
    user_agent: navigator.userAgent
  });
}

On the site, track events:

// When patient clicks "book"
trackEvent('click_booking_button', {
  procedureType: 'implant',
  source: 'homepage'
});

// When patient fills form
trackEvent('form_submitted', {
  patientName: 'John',
  procedureType: 'cleaning'
});

// When booking is created
trackEvent('appointment_created', {
  procedureType: 'prosthetic',
  appointmentDate: new Date()
});

Then create a dashboard in Supabase:

-- View bookings per week
SELECT 
  date_trunc('week', (metadata->>'appointmentDate')::timestamp) as week,
  count(*) as bookings
FROM events
WHERE event_name = 'appointment_created'
GROUP BY week
ORDER BY week DESC;

-- Conversion rate: clicks to bookings
WITH clicks AS (
  SELECT count(*) as total FROM events 
  WHERE event_name = 'click_booking_button'
),
bookings AS (
  SELECT count(*) as total FROM events
  WHERE event_name = 'appointment_created'
)
SELECT 
  (bookings.total::float / clicks.total * 100)::int as conversion_rate_pct
FROM clicks, bookings;

Advantage: free, your data, detailed insights. Disadvantage: requires basic SQL, no ready UI.

My recommendation: Plausible + Supabase

Plausible for overall traffic metrics (visitors, source, time on site).

Supabase for specific conversion events (booking button clicks, forms, bookings created).

Combined: you’re seeing everything that matters.

Cost: R$ 150/month (Plausible) + R$ 0 (Supabase free tier).

Practical example: Tracking ROI of a website

You invested R$ 2,500 in website + Local SEO in May.

August (3 months later):

  • Visitors/month: 250
  • Conversion rate: 4% (10 bookings)
  • Show-up rate: 90% (9 consultations)
  • Average ticket: R$ 800
  • Revenue generated: 9 x R$ 800 = R$ 7,200/month

ROI:

  • Investment: R$ 2,500 (one-time)
  • Revenue in 3 months: R$ 7,200 x 3 = R$ 21,600
  • Profit: R$ 21,600 - R$ 2,500 = R$ 19,100
  • ROI: 760%

Of course, that’s optimistic. More realistic:

  • Visitors/month: 150
  • Conversion rate: 2.5% (3-4 bookings)
  • Show-up rate: 85% (3 consultations)
  • Average ticket: R$ 600
  • Revenue generated: 3 x R$ 600 = R$ 1,800/month

ROI:

  • Investment: R$ 2,500
  • Revenue in 3 months: R$ 1,800 x 3 = R$ 5,400
  • Profit: R$ 5,400 - R$ 2,500 = R$ 2,900
  • ROI: 116%

Still excellent (most clinic investments don’t return 100%).

Simple dashboard in Supabase

If you want something ready-made, create a view in Supabase showing this month:

CREATE VIEW dashboard_clinic AS
SELECT 
  date_trunc('month', created_at)::date as month,
  count(CASE WHEN status = 'attended' THEN 1 END) as patients_attended,
  count(CASE WHEN status = 'no_show' THEN 1 END) as no_shows,
  count(CASE WHEN status = 'attended' THEN 1 END)::float / count(*) as show_up_rate,
  avg((amount)::numeric) as average_ticket,
  sum(amount) as total_revenue,
  count(DISTINCT patient_id) as unique_patients
FROM appointments
WHERE created_at >= date_trunc('month', current_date)
GROUP BY month;

Then create an Astro page that reads this view:

---
// src/pages/dashboard.astro
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  import.meta.env.PUBLIC_SUPABASE_URL,
  import.meta.env.SUPABASE_SERVICE_ROLE_KEY
);

const { data: dashboard } = await supabase
  .from('dashboard_clinic')
  .select('*')
  .single();
---

<html>
  <body>
    <div class="dashboard">
      <h1>Dashboard Clinic - {dashboard.month}</h1>
      
      <div class="card">
        <h3>Patients Attended</h3>
        <p class="number">{dashboard.patients_attended}</p>
      </div>

      <div class="card">
        <h3>Show-up Rate</h3>
        <p class="number">{(dashboard.show_up_rate * 100).toFixed(1)}%</p>
      </div>

      <div class="card">
        <h3>Average Ticket</h3>
        <p class="number">R$ {dashboard.average_ticket.toFixed(2)}</p>
      </div>

      <div class="card">
        <h3>Total Revenue</h3>
        <p class="number">R$ {dashboard.total_revenue.toFixed(2)}</p>
      </div>
    </div>
  </body>
</html>

<style>
  .dashboard {
    padding: 2rem;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 2rem;
  }

  .card {
    background: white;
    border: 1px solid #eee;
    padding: 1.5rem;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
  }

  .number {
    font-size: 2rem;
    font-weight: bold;
    color: #2563eb;
    margin: 0.5rem 0;
  }
</style>

Result: dashboard showing real-time updated numbers.

Checklist: implement tracking in 2 hours

  • Choose tool: Plausible or Umami
  • Copy JS snippet to site
  • Create events table in Supabase (eventName, metadata, timestamp)
  • Track 3 events: click_booking, form_submit, appointment_created
  • Test: click booking button, see event in dashboard
  • Create SQL query for CPL, conversion rate, average ticket
  • Set up protected dashboard page
  • Watch numbers next week

Read also: Online booking for dental clinics with Astro + Supabase | Local SEO for dental clinics | How much does a custom web system cost

Conclusion

Metrics aren’t for MBAs, they’re for you making more money.

If you know your CPL is R$ 250 and average ticket is R$ 800, you know each new patient from the website nets you R$ 550 (after amortizing the website over 6 months).

If you know your show-up rate is 85%, you know you need reminder automation (can gain 5% show-up = 10-20 patients per year).

If you know your conversion rate (site to booking) is 1%, you know your site urgently needs improvement.

Watch these 5 metrics. Next week you’re seeing patterns. Next month you’re making data-driven decisions, not guesses.

Start now. Install Plausible, turn on tracking. Next Monday you have numbers in hand.

Next step

Need a dev who truly delivers?

Whether it's a one-time project, team reinforcement, or a long-term partnership. Let's talk.

Chat on WhatsApp

I reply within 2 hours during business hours.