Inventory Quality: Definitions & Methodology
Last updated: June 2, 2026
This document defines each metric shown on the Gamera Inventory Quality page and explains how to reproduce them using the Gamera Data API.
All API requests in this document use the base URL https://portal.gamera.app/api/data/v1 and require a Bearer token:
Authorization: Bearer gam_your_api_key_here
Quick Reference: Getting Pre-Calculated Scores
The Scorecard endpoint returns pre-calculated quality scores and averages for up to 10 domains in a single request:
GET /api/data/v1/scorecard?domains=example.com&days=7
Parameter | Required | Values |
|---|---|---|
| Yes | Comma-separated domains (max 10) |
| Yes |
|
The response includes a numeric score (0–100), a letter grade, and the site-wide average for every metric described below. If that's all you need, you can stop here.
The rest of this document explains exactly how those numbers are derived, so you can reproduce or extend them from raw data.
Raw Data Endpoints
Two endpoints provide the underlying data for all quality metrics:
Placements
Returns per-placement ad performance data. Used for Refresh Rate, Viewability, Impressions per Session, and CTR.
GET /api/data/v1/{domain}/placements?start_date=2026-04-20&end_date=2026-04-22&order_by=total_imps:desc&limit=10000
Key fields returned per row:
Field | Description |
|---|---|
|
|
| Page path |
| Ad unit identifier |
| Total impressions for this placement |
| Viewable impressions |
| Click count |
| Average seconds between ad refreshes |
| Viewability percentage (0–100) |
| Average impressions per session for this placement |
| Click-through rate percentage |
Ad Health
Returns page-level ad health data. Used for Ad Density and Impressions per Minute.
GET /api/data/v1/{domain}/adhealth?start_date=2026-04-20&end_date=2026-04-22&dimensions=domain,device_type,page&order_by=pageviews:desc&limit=10000
Key fields returned per row:
Field | Description |
|---|---|
|
|
| Page path |
| Pageview count |
| Session count |
| Unique user count |
| Total impressions |
| Average ads visible per viewport |
| Impressions served per minute of activity |
To also retrieve the domain-level median impressions per minute (q_50_imps_per_minute), make a second ad health request with dimensions=domain:
GET /api/data/v1/{domain}/adhealth?start_date=2026-04-20&end_date=2026-04-22&dimensions=domain
Date Ranges
The Quality page uses the 2 most recent complete days (yesterday and the day before). To match it:
start_date = today - 2 days
end_date = today - 1 day
The Scorecard endpoint accepts days=1, days=7, or days=30 and always ends on yesterday.
Minimum Traffic Filters
Before analyzing the data, exclude low-traffic rows that would produce unreliable metrics:
Data source | Filter |
|---|---|
Placements | Exclude rows where |
Ad Health | Exclude rows where |
Out-of-page placements (type=oop) are automatically excluded by the placements endpoint.
Metric Definitions
1. Refresh Rate
What it measures: The average time (in seconds) between ad refreshes for a placement. Longer intervals (15+ seconds) are better. Fast refresh rates can violate ad network policies, reduce CPMs, and inflate impression counts without providing real value to advertisers.
Data source: Placements endpoint — avg_refresh_interval field.
Site-wide average:
Compute an impression-weighted average across all placements:
sum(avg_refresh_interval * total_imps)
refresh_average = ————————————————————————————————————————
sum(total_imps)
Only include placements where avg_refresh_interval > 0 in the weighted average (zero means the placement doesn't refresh).
Issue detection — a placement is flagged when:
avg_refresh_interval < 15 AND avg_refresh_interval > 0
Issue threshold: < 15 seconds
2. Viewability
What it measures: The percentage of ad impressions that meet IAB viewability standards (50% of pixels visible for 1+ second). Higher is better. Advertisers increasingly require viewability guarantees, and low rates indicate poor ad placement or user engagement issues.
Data source: Placements endpoint — viewable_rate, total_viewable, and total_imps fields.
Site-wide average:
sum(total_viewable)
viewability_average = ————————————————————— × 100
sum(total_imps)
Sum total_viewable and total_imps across all placements for the domain, then divide.
Issue detection — a placement is flagged when:
viewable_rate < 50 AND viewable_rate > 0
Placements with zero viewability are excluded (likely unmeasured).
Issue threshold: < 50%
3. Impressions per Session
What it measures: The average number of ad impressions served during a user session. Moderate values indicate healthy monetization. Extremely high values suggest ad overload, which can lead to banner blindness, poor user experience, and reduced advertiser ROI.
Data sources:
Per-placement issue detection: Placements endpoint —
avg_imps_per_sessionfield.Site-wide average: Ad Health endpoint (page-level) —
total_impsandsessionsfields.
Site-wide average:
sum(total_imps) [from ad health rows]
impressions_per_session_avg = ——————————————————
sum(sessions) [from ad health rows]
This uses the ad health page-level data (not placements) because it reflects total site-wide impression delivery relative to sessions.
Issue detection — a placement is flagged when:
avg_imps_per_session > 50
Issue threshold: > 50 impressions per session
4. Click-Through Rate (CTR)
What it measures: The percentage of ad impressions that result in a click. Normal range is 0.1–1%. Abnormally high CTR (>3%) may indicate accidental clicks from poor ad placement, intrusive formats, or potentially invalid traffic—all of which concern advertisers.
Data source: Placements endpoint — ctr, total_clicks, and total_imps fields.
Site-wide average:
sum(total_clicks)
ctr_average = ——————————————————— × 100
sum(total_imps)
Sum across all placements for the domain.
Issue detection — a placement is flagged when:
ctr > 3
Issue threshold: > 3%
5. Ad Density
What it measures: The average number of ads visible per viewport on a page. Lower is generally better. High ad density can trigger Google's "Better Ads Standards" penalties, reduce share of voice for individual advertisers, and create a cluttered user experience that hurts engagement.
Data source: Ad Health endpoint (page-level) — ad_density and device_type fields.
Site-wide average:
Compute a pageview-weighted average across all pages:
sum(ad_density * pageviews)
density_average = ——————————————————————————————
sum(pageviews)
Issue detection — a page is flagged when:
ad_density > threshold_for_device
Device-specific thresholds:
Device Type | Threshold |
|---|---|
Mobile | > 3 ads/viewport |
Desktop | > 4 ads/viewport |
Tablet | > 4 ads/viewport |
Unknown device types use the desktop threshold (4).
6. Impressions per Minute
What it measures: The rate at which ad impressions are served per minute of user activity on a page. Values under 8 are optimal. High rates often indicate aggressive refresh policies or ad overload, which can inflate impression counts without delivering real advertiser value.
Data source: Ad Health endpoint — imps_per_minute (page-level) and q_50_imps_per_minute (domain-level).
Site-wide average:
The preferred value is the domain-level median (q_50_imps_per_minute from the dimensions=domain ad health query). If that field is unavailable, fall back to a pageview-weighted average:
sum(imps_per_minute * pageviews)
imps_per_minute_average = ———————————————————————————————————
sum(pageviews)
Issue detection — a page is flagged when:
imps_per_minute > 8
Issue threshold: > 8 impressions per minute
Quality Score Calculation
The quality score is a single number (0–100) derived from a penalty-based system. Each metric category contributes a penalty based on how many "issue" rows were found.
Step 1: Count Issues per Category
For each metric, count the number of rows (placements or pages) that exceed the issue threshold, after applying the minimum traffic filters.
Step 2: Calculate Per-Category Penalty
Each category has a weight representing its maximum possible penalty:
Category | Weight |
|---|---|
Refresh Rate | 25 |
Ad Density | 25 |
Impressions per Minute | 25 |
Impressions per Session | 15 |
Viewability | 5 |
CTR | 5 |
For each category, compute the penalty:
imp_in_millions = max(total_impressions / 1,000,000, 0.001)
normalized_count = issue_count / imp_in_millions
scaled_penalty = min(1, log10(normalized_count + 1) / 2)
penalty = round(scaled_penalty × weight, 1)
Where total_impressions is the site's total impressions for the period (from ad health totals).
This formula normalizes by traffic volume (so larger sites aren't penalized for having more absolute issues), applies logarithmic scaling (first issues matter most), and caps each category at its weight.
Step 3: Apply Impressions per Minute Severity Multiplier
If the domain-wide average impressions per minute exceeds 8, the Impressions per Minute penalty gets an additional severity multiplier:
Average Imps/Min | Multiplier |
|---|---|
8 or below | 1.0x (no extra penalty) |
8–10 | 1.0x to 1.5x (linear) |
10–15 | 1.5x to 3.0x (steeper ramp) |
Above 15 | 3.0x+ (exponential — doubles every 5 imps/min) |
The adjusted penalty is capped at the category weight (25).
Step 4: Compute Final Score
quality_score = max(0, round(100 - sum_of_all_penalties))
Step 5: Assign Letter Grade
Score | Grade | Label |
|---|---|---|
90–100 | A | Excellent |
75–89 | B | Good |
60–74 | C | Fair |
40–59 | D | Poor |
0–39 | F | Critical |
Complete Example: Reproducing a Domain's Quality Score
Here is a step-by-step walkthrough using the API to reproduce the quality score for example.com over the last 7 days.
1. Fetch the raw data
Make three API calls:
# Placements data (all placements for the domain)
curl "<https://portal.gamera.app/api/data/v1/example.com/placements?\\\\>
start_date=2026-04-15&end_date=2026-04-22&\\\\
order_by=total_imps:desc&limit=10000" \\\\
-H "Authorization: Bearer gam_your_api_key_here"
# Ad Health — page level (for density, imps/minute, and traffic totals)
curl "<https://portal.gamera.app/api/data/v1/example.com/adhealth?\\\\>
start_date=2026-04-15&end_date=2026-04-22&\\\\
dimensions=domain,device_type,page&\\\\
order_by=pageviews:desc&limit=10000" \\\\
-H "Authorization: Bearer gam_your_api_key_here"
# Ad Health — domain level (for median imps/minute)
curl "<https://portal.gamera.app/api/data/v1/example.com/adhealth?\\\\>
start_date=2026-04-15&end_date=2026-04-22&\\\\
dimensions=domain" \\\\
-H "Authorization: Bearer gam_your_api_key_here"
2. Filter out low-traffic rows
From placements: remove rows where
total_imps < 1000From page-level ad health: remove rows where
pageviews < 1000orusers < 100
3. Count issues per category
Using the filtered data, count rows exceeding each threshold:
Category | Count rows where... |
|---|---|
Refresh |
|
Viewability |
|
Impressions/Session |
|
CTR |
|
Ad Density |
|
Imps/Minute |
|
4. Calculate site-wide averages
Using all rows (not just issue rows):
Metric | Formula |
|---|---|
Refresh |
|
Viewability |
|
Imps/Session |
|
CTR |
|
Density |
|
Imps/Minute |
|
5. Compute penalties and score
Apply the penalty formula from the Quality Score Calculation section to each category, sum the penalties, and subtract from 100.
6. Compare with the Scorecard
Verify your result against the pre-calculated value:
curl "<https://portal.gamera.app/api/data/v1/scorecard?\\\\>
domains=example.com&days=7" \\\\
-H "Authorization: Bearer gam_your_api_key_here"