CTR (Click-Through Rate)
Click-through rate is clicks divided by impressions for a link, ad or recommendation; repeat impressions and clicks can involve the same person. It is one of the basic measures in digital marketing, online advertising and recommender systems.
\[ \text{CTR} = \frac{\text{clicks}}{\text{impressions}} \times 100\% \]
An ad shown 10,000 times and clicked 300 times has a CTR of 3%. A higher CTR means more clicks per recorded impression. It is commonly used as a proxy for engagement, but placement and the audience also affect it, as the examples below show.
It shows up in advertising, where expected CTR can contribute to quality and auction outcomes; in email, where the base is usually delivered recipients rather than impressions (Mailchimp, for example, defines click rate as the percentage of delivered emails that registered at least one click); in search, as a signal of which results are relevant; and in recommender systems, as the fraction of recommended items that get clicked. Because the denominator and the duplicate rule depend on the channel, state whether the base is impressions or delivered recipients and whether clicks are all clicks or unique, before comparing rates.
Where CTR misleads
The creatives, CTRs, conversion rates, position factors and sample sizes in this section are hypothetical inputs chosen to illustrate each point, not measurements from real campaigns.
Clicks are not orders
Four hypothetical creatives each receive 1,000,000 impressions. An order is worth \$40 and the gross margin is 25%, so each order adds \$10 of gross profit. For each creative, clicks are impressions times CTR, orders are clicks times the conversion rate, and gross profit is orders times \$10.
impressions, order_value, margin = 1_000_000, 40.0, 0.25
ads = (("clickbait", 0.085, 0.004), ("bold claim", 0.052, 0.011),
("plain offer", 0.024, 0.038), ("specific price", 0.017, 0.062))
for name, ctr, cvr in ads:
clicks = impressions * ctr
orders = clicks * cvr
profit = orders * order_value * margin
print(f"{name}: CTR {ctr:.1%}, conversion {cvr:.2%} -> "
f"{clicks:,.0f} clicks, {orders:,.0f} orders, gross profit {profit:,.2f}")
clickbait: CTR 8.5%, conversion 0.40% -> 85,000 clicks, 340 orders, gross profit 3,400.00
bold claim: CTR 5.2%, conversion 1.10% -> 52,000 clicks, 572 orders, gross profit 5,720.00
plain offer: CTR 2.4%, conversion 3.80% -> 24,000 clicks, 912 orders, gross profit 9,120.00
specific price: CTR 1.7%, conversion 6.20% -> 17,000 clicks, 1,054 orders, gross profit 10,540.00
A click is not a conversion. Ranked by CTR the clickbait creative wins; ranked by gross profit it comes last, earning a third of what the lowest-CTR ad earns. In these hypothetical inputs, clickbait does not convert slightly worse; it converts at 0.40% against 6.20% for the best creative. One possible explanation is that it attracts a different population, one less likely to be shopping, but the numbers alone do not show why. Optimizing a campaign on CTR alone would select for such creatives, which is why the usual advice is to pair it with conversion rate. The stronger form of the advice is to optimize on revenue per impression, which is CTR times conversion times value and contains all three.
The same creative at different positions
Take one creative whose own click probability is 3.000% wherever it appears, and suppose the placement multiplies that probability by a position factor: 1.00 at the top slot and smaller factors further down. The observed CTR is the creative’s own 3.000% times the factor, so every difference in the observed CTR comes from the placement alone.
| Position | Position factor | Observed CTR | Creative’s own CTR |
|---|---|---|---|
| 1 | 1.00 | 3.000% | 3.000% |
| 2 | 0.55 | 1.650% | 3.000% |
| 3 | 0.36 | 1.080% | 3.000% |
| 5 | 0.18 | 0.540% | 3.000% |
| 8 | 0.08 | 0.240% | 3.000% |
This is why CTR figures can be hard to compare. Position can affect CTR and confound a comparison: in the table above, the same creative earns 3.000% at the top slot and 0.540% at position 5. A comparison that mixes placements — different pages, different feed positions, a search ad against a display ad — may therefore be partly measuring the placement rather than the creative. Google Ads Help likewise says that a good CTR is relative to what is being advertised and on which networks, so a single benchmark does not transfer across contexts; the size of any placement effect has to be measured for the channel and period in question.
Sample size and power
Sample size can undermine a creative test: with too few impressions, a real difference in CTR is often missed. Suppose the baseline CTR is 3.00% and the test creative’s true CTR is 3.30%, 4.00% or 6.00%, with the same number of impressions in each arm. Power is the probability that a two-sided test at the 5% level detects the difference. With the standard error of the difference between two proportions, the normal approximation gives
\[ \text{power} \approx 1 – \Phi\!\left(z_{0.975} – \frac{p_2 – p_1}{\text{SE}}\right), \qquad \text{SE} = \sqrt{\frac{p_1(1-p_1)}{n} + \frac{p_2(1-p_2)}{n}} \]
where \(\Phi\) is the standard normal cumulative distribution function, so norm.sf in the code is \(1 – \Phi\). Setting the power to 80% and solving for \(n\) gives the sample size per arm for a target power.
import numpy as np
from scipy.stats import norm
def power(p1, p2, n):
se = np.sqrt(p1 * (1 - p1) / n + p2 * (1 - p2) / n)
return norm.sf(norm.ppf(0.975) - (p2 - p1) / se)
print("impressions each | 3.00% vs 3.30% | 3.00% vs 4.00% | 3.00% vs 6.00%")
for n in (10_000, 50_000, 200_000, 1_000_000):
powers = [power(0.030, p2, n) for p2 in (0.033, 0.040, 0.060)]
print(f"{n:>16,} | " + " | ".join(f"{p:>14.4f}" for p in powers))
p1, p2 = 0.030, 0.033
n_80 = (norm.ppf(0.975) + norm.ppf(0.80)) ** 2 * (p1 * (1 - p1) + p2 * (1 - p2)) / (p2 - p1) ** 2
print(f"impressions per arm for 80% power at 3.00% vs 3.30%: {n_80:,.0f}")
impressions each | 3.00% vs 3.30% | 3.00% vs 4.00% | 3.00% vs 6.00%
10,000 | 0.2280 | 0.9706 | 1.0000
50,000 | 0.7751 | 1.0000 | 1.0000
200,000 | 0.9997 | 1.0000 | 1.0000
1,000,000 | 1.0000 | 1.0000 | 1.0000
impressions per arm for 80% power at 3.00% vs 3.30%: 53,208
In the power output above, 50,000 impressions an arm give about 77.5% power to separate 3.00% from 3.30%, and about 53,200 reach the conventional 80%; at 10,000 the power is about 23%, so the test misses a real 10% lift roughly three times in four. The calculation assumes independent impressions that each click with a fixed probability over the test period, a two-sided test at the 5% level (counting only the tail in the direction of the lift), and a normal approximation to the difference of two proportions. Repeated impressions to the same people are not independent, so clustering would lower the real power. Large differences are easy to detect, while the small differences that matter on a large budget need traffic that a one-day test may not have.
What it is good for
Within a fixed placement, CTR is a fast, high-volume signal — there are far more impressions than conversions, so it moves out of the noise much sooner than downstream metrics. That makes it useful for early screening of headlines, thumbnails and creatives, provided the screen is followed by a check on what those clicks did.
In the simplified cost-per-click auction example, bid times predicted CTR is a ranking value per impression. A higher predicted CTR lowers the clearing CPC only with the same competing rank, slot and pricing rule. Real auctions also use quality, thresholds and context; higher CTR does not universally guarantee a cheaper click.
References
- SciPy documentation,
scipy.stats.norm. docs.scipy.org - Google Ads Help, Clickthrough rate (CTR): Definition. support.google.com
- Mailchimp Help Center, About Open and Click Rates. mailchimp.com
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
