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

domains

Yes

Comma-separated domains (max 10)

days

Yes

1, 7, or 30

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

device_type

mobile, desktop, or tablet

page

Page path

placement_id

Ad unit identifier

total_imps

Total impressions for this placement

total_viewable

Viewable impressions

total_clicks

Click count

avg_refresh_interval

Average seconds between ad refreshes

viewable_rate

Viewability percentage (0–100)

avg_imps_per_session

Average impressions per session for this placement

ctr

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

device_type

mobile, desktop, or tablet

page

Page path

pageviews

Pageview count

sessions

Session count

users

Unique user count

total_imps

Total impressions

ad_density

Average ads visible per viewport

imps_per_minute

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 total_imps < 1,000

Ad Health

Exclude rows where pageviews < 1,000 or users < 100

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_session field.

  • Site-wide average: Ad Health endpoint (page-level) — total_imps and sessions fields.

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 < 1000

  • From page-level ad health: remove rows where pageviews < 1000 or users < 100

3. Count issues per category

Using the filtered data, count rows exceeding each threshold:

Category

Count rows where...

Refresh

avg_refresh_interval < 15 AND avg_refresh_interval > 0 (from placements)

Viewability

viewable_rate < 50 AND viewable_rate > 0 (from placements)

Impressions/Session

avg_imps_per_session > 50 (from placements)

CTR

ctr > 3 (from placements)

Ad Density

ad_density > 3 for mobile, ad_density > 4 for desktop/tablet (from ad health pages)

Imps/Minute

imps_per_minute > 8 (from ad health pages)

4. Calculate site-wide averages

Using all rows (not just issue rows):

Metric

Formula

Refresh

sum(avg_refresh_interval × total_imps) / sum(total_imps) across placements with avg_refresh_interval > 0

Viewability

sum(total_viewable) / sum(total_imps) × 100 across all placements

Imps/Session

sum(total_imps) / sum(sessions) across ad health pages

CTR

sum(total_clicks) / sum(total_imps) × 100 across all placements

Density

sum(ad_density × pageviews) / sum(pageviews) across ad health pages

Imps/Minute

q_50_imps_per_minute from domain-level ad health (or pageview-weighted average as fallback)

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"

Threshold Summary