---
title: "Pacing Dashboard: 8 Examples With Formulas and SQL"
description: "A pacing dashboard shows whether spend (or revenue, or signups) is on track to hit its monthly or flight target, by comparing actual-to-date with…"
canonical: "https://www.aifordatabase.com/blog/pacing-dashboard-examples/"
author: "Dhrumil Barot"
author_bio: "Dhrumil is an enterprise analytics expert. Dhrumil built AI for Database so anyone on a team can get answers from live company data in plain English, without waiting on a data team."
published: 2026-11-12
updated: 2026-11-12
---

# Pacing Dashboard: 8 Examples With Formulas and SQL

**TL;DR:** A pacing dashboard shows whether spend (or revenue, or signups) is on track to hit its monthly or flight target, by comparing actual-to-date with expected-to-date. The core formula is pacing % = actual to date ÷ (budget × days elapsed ÷ days in period). Below are 8 pacing dashboard examples, each with the formula and the SQL behind the tile.

A pacing dashboard answers one question every morning: if nothing changes, where will we land at the end of the period? For a $9,000 monthly ad budget on November 12 (12 of 30 days complete), the expected spend to date is $9,000 × 12 ÷ 30 = $3,600. If the campaign has spent $4,140, it is pacing at 115% and will finish near $10,350. A good budget pacing dashboard shows that number per campaign, flags the daily spikes the ad platforms allow, projects month-end spend, and tells you the daily spend needed to land on budget. The same tiles work for revenue and signup targets, which is why the last two examples are not about ads at all.

Most pacing dashboard pages online are Google Sheets or Looker Studio templates that pull from one ad platform. They work until your spend sits in three platforms and your targets sit in a spreadsheet nobody updates. The examples here assume the data lives in a database (Postgres syntax), so every tile is one query you can schedule, chart and alert on.

## What is pacing in advertising?

Pacing is the rate at which a budget is spent relative to the time that has passed in its period. A campaign is "on pace" when the share of budget spent matches the share of the period elapsed, "overpacing" when it spends faster, and "underpacing" when it spends slower. Pacing matters because ad platforms do not spend your daily budget evenly:

| Platform | Max spend on one day | Cap over the period | Source |
| --- | --- | --- | --- |
| Google Ads | 2x average daily budget | 30.4x daily budget per month | [Google Ads Help](https://support.google.com/google-ads/answer/2375423) |
| Meta Ads | daily budget + 75% | 7x daily budget per week | [Jon Loomer, Meta UI wording](https://www.jonloomer.com/updates-to-meta-ads-budgeting/) |
| LinkedIn Ads | up to 100% over daily budget | 7x daily budget per week, never over lifetime budget | [LinkedIn Help](https://www.linkedin.com/help/lms/answer/a422101) |

Two of these changed and many guides still quote the old rules. Meta raised its daily allowance from 25% to 75% on November 1, 2023. LinkedIn's help page now says daily spend "might be up to 100% higher than the daily budget"; many blog posts still say 50%. And from June 1, 2026, [Google paces campaigns with day-of-week ad schedules toward the full 30.4x monthly limit](https://support.google.com/google-ads/answer/16913225), so a campaign that runs only on weekdays can spend far more per active day than before. Schedules that only switch off certain hours are not affected.

## How do you calculate budget pacing?

Budget pacing is actual spend to date divided by expected spend to date, where expected spend is the budget multiplied by the share of the period elapsed.

```
expected_to_date = budget × days_elapsed ÷ days_in_period
pacing_pct       = actual_to_date ÷ expected_to_date × 100
projected_total  = actual_to_date ÷ days_elapsed × days_in_period
required_daily   = (budget − actual_to_date) ÷ days_remaining
```

![Campaign A is overpacing at 115 percent while Campaign B is underpacing at 78 percent on day 12](https://cdn.sanity.io/images/upsr56wu/production/56969be0975156a4d9880ed7f3b8e02e3265c323-1344x364.png?w=1280)

FIG 01 — Expected = budget x days elapsed / days in month.

Use the last complete day as "today" (yesterday's date), because most ad platform exports and warehouse syncs land a day behind. The ad pacing formula above is linear. Example 5 shows a weighted version for budgets that should spend more on some weekdays.

## The data model behind the examples

All examples use three tables. Rename columns to match your sync (Fivetran, Airbyte, a platform API script or a CSV import all end up looking like this).

```
-- one row per campaign per day, from Google, Meta, LinkedIn exports
CREATE TABLE ad_spend_daily (
  spend_date  date,
  platform    text,      -- 'google' | 'meta' | 'linkedin'
  campaign_id text,
  spend       numeric(12,2)
);

-- one row per campaign per month
CREATE TABLE campaign_budgets (
  campaign_id    text,
  platform       text,
  budget_month   date,   -- first day of the month
  monthly_budget numeric(12,2),
  daily_budget   numeric(12,2)
);

-- campaigns with a fixed flight instead of a monthly budget
CREATE TABLE campaign_flights (
  campaign_id   text,
  start_date    date,
  end_date      date,
  flight_budget numeric(12,2)
);
```

![Spend flows from ad platforms into one table, then pacing queries feed the dashboard and alerts](https://cdn.sanity.io/images/upsr56wu/production/a29c3c84d59e2412ab2e4fc5740d5dfa001014d3-1344x326.png?w=1280)

FIG 02 — Budgets live in campaign\_budgets, not in the ad platforms.

## 1\. Month-to-date pacing by campaign

**Scenario:** the headline tile. One row per campaign with budget, spend to date, expected spend and pacing %.

```
-- Month-to-date pacing per campaign (linear)
WITH params AS (
  SELECT date_trunc('month', current_date - 1)::date AS month_start,
         (date_trunc('month', current_date - 1) + interval '1 month - 1 day')::date AS month_end,
         current_date - 1 AS as_of            -- last complete day
),
mtd AS (
  SELECT s.campaign_id, sum(s.spend) AS spend_mtd
  FROM ad_spend_daily s, params p
  WHERE s.spend_date BETWEEN p.month_start AND p.as_of
  GROUP BY s.campaign_id
)
SELECT b.campaign_id,
       b.monthly_budget,
       coalesce(m.spend_mtd, 0) AS spend_mtd,
       round(b.monthly_budget * (p.as_of - p.month_start + 1)
             / (p.month_end - p.month_start + 1), 2) AS expected_mtd,
       round(100 * coalesce(m.spend_mtd, 0)
             / nullif(b.monthly_budget * (p.as_of - p.month_start + 1)
                      / (p.month_end - p.month_start + 1), 0), 1) AS pacing_pct
FROM campaign_budgets b
CROSS JOIN params p
LEFT JOIN mtd m USING (campaign_id)
WHERE b.budget_month = p.month_start
ORDER BY pacing_pct DESC;
```

**What it shows:** which campaigns are ahead of or behind the straight line. In Postgres, subtracting two dates returns whole days, so `as_of - month_start + 1` is days elapsed ([date functions](https://www.postgresql.org/docs/current/functions-datetime.html)).

**How to adapt it:** show it as a table with conditional color: green between 90% and 110%, amber outside that, red below 75% or above 125%. Those bands are a starting point, not a standard; tighten them for large budgets.

## 2\. Daily spikes against the platform's daily cap

**Scenario:** Meta spent $175 on a $100 daily budget and someone asks if that is a bug. It isn't, but you want to see it.

```
-- Days in the last 14 where spend exceeded 1.5x the daily budget
SELECT s.spend_date, s.platform, s.campaign_id, s.spend, b.daily_budget,
       round(s.spend / nullif(b.daily_budget, 0), 2) AS x_daily_budget,
       CASE s.platform
         WHEN 'google'   THEN 2.00
         WHEN 'meta'     THEN 1.75
         WHEN 'linkedin' THEN 2.00
       END AS platform_daily_cap
FROM ad_spend_daily s
JOIN campaign_budgets b
  ON b.campaign_id = s.campaign_id
 AND b.budget_month = date_trunc('month', s.spend_date)::date
WHERE s.spend_date >= current_date - 14
  AND s.spend > b.daily_budget * 1.5
ORDER BY x_daily_budget DESC;
```

**What it shows:** spikes inside the allowed cap (normal platform behavior) versus spend above it, which on Google usually means overdelivery you will not be billed for. Google's Billed cost report shows the difference as served cost minus billed cost.

**How to adapt it:** a bar chart of daily spend with a reference line at the daily budget and a second at the platform cap.

## 3\. Projected month-end spend

**Scenario:** finance wants to know the landing number, not the percentage.

```
-- Month-end projection: spend to date + trailing 7-day average x days left
WITH d AS (
  SELECT date_trunc('month', current_date - 1)::date AS month_start,
         (date_trunc('month', current_date - 1) + interval '1 month - 1 day')::date AS month_end,
         current_date - 1 AS as_of
)
SELECT s.campaign_id,
       sum(s.spend) FILTER (WHERE s.spend_date >= d.month_start) AS spend_mtd,
       round(avg(s.spend) FILTER (WHERE s.spend_date > d.as_of - 7), 2) AS avg_last_7,
       round(sum(s.spend) FILTER (WHERE s.spend_date >= d.month_start)
             + avg(s.spend) FILTER (WHERE s.spend_date > d.as_of - 7) * (d.month_end - d.as_of), 2)
         AS projected_month_end
FROM ad_spend_daily s
CROSS JOIN d
WHERE s.spend_date BETWEEN d.as_of - 30 AND d.as_of
GROUP BY s.campaign_id, d.month_start, d.month_end, d.as_of;
```

**What it shows:** a run-rate projection that reacts to recent budget changes faster than the simple `actual ÷ days_elapsed × days_in_period` formula. Compare it with `monthly_budget`, and for Google campaigns with the hard ceiling of 30.4 × daily budget.

**How to adapt it:** a single number tile per channel, with the budget as the comparison value. Google's own budget report draws the same idea: solid line for cost to date, dotted line for forecast, gray line for the monthly limit.

VIDEO · [Google Ads Tutorials: How Google Ads Budget Pacing Works](https://www.youtube.com/watch?v=Qul_St1XhJI) (Google Ads)

## 4\. Required daily spend to land on budget

**Scenario:** the campaign is behind on day 18. What daily budget gets it back to plan?

```
-- Daily spend needed for the rest of the month to hit budget exactly
WITH d AS (
  SELECT date_trunc('month', current_date - 1)::date AS month_start,
         (date_trunc('month', current_date - 1) + interval '1 month - 1 day')::date AS month_end,
         current_date - 1 AS as_of
)
SELECT b.campaign_id, b.daily_budget,
       round((b.monthly_budget - coalesce(sum(s.spend), 0))
             / nullif(d.month_end - d.as_of, 0), 2) AS required_daily
FROM campaign_budgets b
CROSS JOIN d
LEFT JOIN ad_spend_daily s
  ON s.campaign_id = b.campaign_id
 AND s.spend_date BETWEEN d.month_start AND d.as_of
WHERE b.budget_month = d.month_start
GROUP BY b.campaign_id, b.daily_budget, b.monthly_budget, d.month_end, d.as_of;
```

**What it shows:** in the worked example, Campaign A has $4,860 left over 18 days, so $270 a day instead of $300. This is the number a media buyer actually types into the platform.

**How to adapt it:** add a column `required_daily / daily_budget`; anything above 2 cannot be recovered on Google or LinkedIn without raising the budget, because the platform will not spend more than twice the daily budget in one day.

## 5\. Weekday-weighted pacing curve

**Scenario:** B2B campaigns spend most on weekdays, so a straight line makes them look behind every Monday and ahead every Friday.

```
-- Share of the month's expected spend that should be done by yesterday,
-- weighted by the last 8 weeks of spend per weekday
WITH dow_weight AS (
  SELECT extract(isodow FROM spend_date) AS dow, sum(spend) AS w
  FROM ad_spend_daily
  WHERE spend_date >= current_date - 56 AND spend_date < current_date
  GROUP BY 1
),
days AS (
  SELECT d::date AS day, extract(isodow FROM d) AS dow
  FROM generate_series(date_trunc('month', current_date - 1),
                       date_trunc('month', current_date - 1) + interval '1 month - 1 day',
                       interval '1 day') AS d
)
SELECT round(sum(w) FILTER (WHERE day < current_date) / sum(w), 4) AS share_elapsed
FROM days JOIN dow_weight USING (dow);
```

**What it shows:** a weighted "share of month elapsed". Multiply it by `monthly_budget` instead of `days_elapsed / days_in_period` in example 1. `generate_series` with an interval step builds the calendar ([set-returning functions](https://www.postgresql.org/docs/current/functions-srf.html)); `isodow` returns 1 for Monday through 7 for Sunday.

**How to adapt it:** compute weights per platform if Meta and Google have different weekly shapes. For Google campaigns with day-of-week schedules, remember the June 2026 change: Google now tries to spend the full monthly limit in the active days, so the curve flattens into fewer, bigger days.

## 6\. Flight (lifetime) budget pacing

**Scenario:** a six-week product launch campaign with a fixed $20,000 budget, not a monthly one.

```
-- Percent of flight budget spent vs percent of flight elapsed
SELECT f.campaign_id, f.flight_budget,
       coalesce(sum(s.spend), 0) AS spend_to_date,
       round(100 * coalesce(sum(s.spend), 0) / f.flight_budget, 1) AS pct_budget_spent,
       round(100.0 * (least(current_date - 1, f.end_date) - f.start_date + 1)
             / (f.end_date - f.start_date + 1), 1) AS pct_flight_elapsed
FROM campaign_flights f
LEFT JOIN ad_spend_daily s
  ON s.campaign_id = f.campaign_id
 AND s.spend_date BETWEEN f.start_date AND least(current_date - 1, f.end_date)
WHERE f.start_date < current_date
  AND f.end_date >= current_date - 1
GROUP BY f.campaign_id, f.flight_budget, f.start_date, f.end_date;
```

**What it shows:** two percentages side by side. If 40% of the flight has passed and 55% of the budget is gone, the campaign will run dry before the end date. LinkedIn caps lifetime budgets, so an overpacing LinkedIn flight simply stops delivering early rather than overspending.

**How to adapt it:** a bullet chart per campaign: bar = % budget spent, marker = % flight elapsed.

## 7\. Revenue pacing against a monthly target

**Scenario:** the same math, pointed at money coming in. A SaaS team keeps a new MRR target per month in a `targets (metric, target_month, target)` table and wants to know on the 15th whether it will hit it.

```
-- New MRR booked this month vs target, pacing and projection
WITH d AS (
  SELECT date_trunc('month', current_date - 1)::date AS month_start,
         (date_trunc('month', current_date - 1) + interval '1 month - 1 day')::date AS month_end,
         current_date - 1 AS as_of
),
booked AS (
  SELECT sum(s.mrr) AS new_mrr
  FROM subscriptions s, d
  WHERE s.started_at::date BETWEEN d.month_start AND d.as_of
)
SELECT t.target AS new_mrr_target,
       b.new_mrr,
       round(100 * b.new_mrr / (t.target * (d.as_of - d.month_start + 1)
             / (d.month_end - d.month_start + 1)), 1) AS pacing_pct,
       round(b.new_mrr / (d.as_of - d.month_start + 1)
             * (d.month_end - d.month_start + 1), 2) AS projected_new_mrr
FROM d
CROSS JOIN booked b
JOIN targets t ON t.metric = 'new_mrr' AND t.target_month = d.month_start;
```

**What it shows:** revenue pacing for founders and RevOps, the same tile structure as ad spend. Revenue tends to bunch at month end in sales-led teams, so pair it with the weighted curve from example 5 built on last quarter's daily bookings. For the metric itself, see our [MRR dashboard guide](https://www.aifordatabase.com/blog/mrr-dashboard-saas-2026/) and [revenue forecasting from your database](https://www.aifordatabase.com/blog/revenue-forecasting-from-database/).

**How to adapt it:** swap `subscriptions` for a `deals` table with `closed_at` and `amount` for sales quota pacing.

## 8\. Signup pacing with an off-pace alert

**Scenario:** a growth team targets 600 signups this month and wants a message when yesterday's pace slips, not a dashboard someone has to remember to open.

```
-- Returns a row only when month-to-date signups are below 90% of pace
WITH d AS (
  SELECT date_trunc('month', current_date - 1)::date AS month_start,
         (date_trunc('month', current_date - 1) + interval '1 month - 1 day')::date AS month_end,
         current_date - 1 AS as_of
),
pace AS (
  SELECT (SELECT count(*) FROM users u
          WHERE u.created_at::date BETWEEN d.month_start AND d.as_of) AS signups_mtd,
         round(600.0 * (d.as_of - d.month_start + 1) / (d.month_end - d.month_start + 1)) AS expected_mtd
  FROM d
)
SELECT signups_mtd, expected_mtd
FROM pace
WHERE signups_mtd < 0.9 * expected_mtd;
```

**What it shows:** nothing on a good day, one row on a bad one. That shape is exactly what a [threshold alert](https://www.aifordatabase.com/glossary/threshold-alert/) needs: schedule it after your nightly sync and send the row to Slack or email.

**How to adapt it:** move the 600 into the `targets` table so the alert doesn't need editing every month.

Writing and scheduling these eight queries is the part teams skip. If your spend, subscriptions and signups already sit in Postgres, MySQL or BigQuery, AI for Database lets you ask "which campaigns are pacing above 110% this month?" in plain English, shows the SQL it ran, pins the answer to a self-refreshing dashboard and sends an alert when a pacing query returns a row. Try AI for Database free at [aifordatabase.com](https://app.aifordatabase.com/signup); sample data is included.

## What should a pacing dashboard show?

A pacing dashboard should show, per campaign or target: the budget, actual to date, expected to date, pacing %, projected end-of-period total, and the daily amount needed to land on target. Add one view of daily spend against the platform's daily cap so spikes are explained, not escalated. Keep it to one screen; anything that needs scrolling to find the red rows is a report, not a pacing dashboard. If you are building the wider marketing view around it, our guides to [tracking campaign performance from your database](https://www.aifordatabase.com/blog/track-marketing-campaign-performance-database/) and [marketing analytics without SQL](https://www.aifordatabase.com/blog/marketing-analytics-database-without-sql-2026/) cover the ROAS and CAC side.

Use this table to pick the tiles:

| You need to know | Use | Chart |
| --- | --- | --- |
| Is each campaign on budget this month? | 1\. Month-to-date pacing | Table with colored pacing % |
| Why did spend jump yesterday? | 2\. Daily spikes vs platform cap | Bars with budget and cap lines |
| Where will we land? | 3\. Projected month-end | Number tile vs budget |
| What should the daily budget be now? | 4\. Required daily spend | Table |
| Are weekday-heavy campaigns really behind? | 5\. Weighted pacing curve | Line: actual vs weighted expected |
| Will a fixed flight run dry early? | 6\. Flight pacing | Bullet chart |
| Will we hit the revenue target? | 7\. Revenue pacing | Number tile + projection |
| Tell me when pace slips | 8\. Signup pacing alert | Alert, no chart |

D

Written by [Dhrumil Barot](https://www.aifordatabase.com/about/#founder)

Dhrumil is an enterprise analytics expert. Dhrumil built AI for Database so anyone on a team can get answers from live company data in plain English, without waiting on a data team.

[LinkedIn](https://www.linkedin.com/in/bdhrumil)[X](https://x.com/fyidhrumil)

## Frequently Asked Questions

### What is pacing in advertising?

Pacing is how fast a campaign spends its budget compared with how much of the budget period has passed. A campaign that has spent 40% of its monthly budget 40% of the way through the month is on pace; spending faster is overpacing, slower is underpacing.

### How do you calculate budget pacing?

Divide actual spend to date by expected spend to date, where expected spend = budget × days elapsed ÷ days in the period. $4,140 spent against a $9,000 budget on day 12 of 30 gives expected $3,600 and pacing of 115%.

### What is a good pacing percentage?

There is no official standard. Many teams treat 90% to 110% as on pace and investigate outside that band, and tighten the band near month end when there is less time to correct.

### Why does Google Ads spend more than my daily budget?

Google can spend up to twice the average daily budget on a single day to catch high-traffic days, but you will not be billed more than 30.4 times the daily budget in a month. Since June 1, 2026 this monthly target also applies to campaigns that only run on certain days of the week.

### Does Meta or LinkedIn overspend the daily budget too?

Yes. Meta may spend up to 75% over the daily budget on a given day and LinkedIn up to 100%, and both cap weekly spend at seven times the daily budget. LinkedIn never exceeds a lifetime budget.

### How often should a pacing dashboard refresh?

Daily is enough for most budgets, because ad platform cost data is usually final a day later. Refresh after your nightly sync and use yesterday as the "as of" date so partial days don't make every campaign look behind.

Read next

*   [DEFAULT PORT FOR SQL SERVER: 1433 AND EVERY OTHER PORT →](https://www.aifordatabase.com/blog/default-port-for-sql-server/)
*   [SQL SERVER ALERT FOR LONG RUNNING QUERIES: FULL SETUP →](https://www.aifordatabase.com/blog/sql-server-alert-long-running-queries/)
*   [WHAT IS A PARAMETERIZED REPORT? DEFINITION AND EXAMPLES →](https://www.aifordatabase.com/blog/parameterized-report/)

Source: https://www.aifordatabase.com/blog/pacing-dashboard-examples/
