| name | gpt-slot-position-targeting |
| description | Implements slot-level 'pos' key-value targeting (pos='1', pos='2') in existing publisher GPT ad unit code and provides step-by-step guidelines for updating Google Ad Manager (GAM) settings (Key-Values, Reportable dimensions, Reporting, Line Items). Use when modifying existing ad code to track position-based viewability decay or configuring GAM inventory settings. |
What is Ad Slot Position Targeting (pos) and Why It Matters
Ad slot position targeting is the implementation of a slot-level dimensional key-value pair (e.g., pos='1', pos='2') attached directly to existing Google Publisher Tag (GPT) ad unit definitions using the .setTargeting() method.
In modern publisher layouts (sidebars, multi-slot article feeds, infinite scroll), multiple ad slots often share the same ad unit name. Without positional key-values, Google Ad Manager (GAM) aggregates all impressions under that single ad unit, making it impossible to see where on the page Active View viewability drops off.
By updating existing ad unit code and configuring GAM settings, publishers can:
- Identify Viewability Decay: Pinpoint the exact slot position (e.g.,
pos=3 vs pos=4) where user engagement and Active View viewability drop below acceptable thresholds.
- Configure Positional Yield in GAM: Target high-viewability top slots (
pos=1) with premium line items and set differentiated pricing rules.
- Cull Low-Yield Requests: Stop requesting or rendering ads for deep positions that consistently fall below advertiser viewability benchmarks.
🛠️ Code Implementation Workflow: Updating Existing Ad Units
When tasked to "Implement pos Key-Value for Existing Ad Units", execute the following steps:
Step 1: Audit Existing Ad Unit Code
Locate where googletag.defineSlot() is called in the publisher's codebase. Identify whether ad slots are defined statically, in a loop/mapping, or dynamically injected on scroll.
Step 2: Refactor Slot Definitions to Add .setTargeting('pos', 'value')
Apply the appropriate code modification pattern based on the publisher's existing architecture:
Pattern A: Refactoring Existing Static Slots (Sidebar / Header / In-Content)
Before (Existing Code without Position):
googletag.cmd.push(() => {
googletag.defineSlot('/1234567/sports_sidebar', [300, 250], 'div-gpt-sidebar-top')
.addService(googletag.pubads());
googletag.defineSlot('/1234567/sports_sidebar', [300, 250], 'div-gpt-sidebar-btm')
.addService(googletag.pubads());
googletag.enableServices();
});
After (Updated Code with pos Key-Value):
googletag.cmd.push(() => {
googletag.defineSlot('/1234567/sports_sidebar', [300, 250], 'div-gpt-sidebar-top')
.addService(googletag.pubads())
.setTargeting('pos', '1');
googletag.defineSlot('/1234567/sports_sidebar', [300, 250], 'div-gpt-sidebar-btm')
.addService(googletag.pubads())
.setTargeting('pos', '2');
googletag.enableServices();
});
Pattern B: Refactoring Looped / Mapped Slot Configurations
Before (Existing Config Array):
const adSlotsConfig = [
{ id: 'div-gpt-ad-1', unit: '/1234567/feed', size: [300, 250] },
{ id: 'div-gpt-ad-2', unit: '/1234567/feed', size: [300, 250] },
{ id: 'div-gpt-ad-3', unit: '/1234567/feed', size: [300, 250] }
];
adSlotsConfig.forEach(cfg => {
googletag.defineSlot(cfg.unit, cfg.size, cfg.id).addService(googletag.pubads());
});
After (Updated Config Loop with Sequential pos):
adSlotsConfig.forEach((cfg, index) => {
const positionString = (index + 1).toString();
googletag.defineSlot(cfg.unit, cfg.size, cfg.id)
.addService(googletag.pubads())
.setTargeting('pos', positionString);
});
Pattern C: Refactoring Dynamic Injection / Infinite Scroll
Updated Dynamic Injection Function:
let dynamicSlotCounter = 1;
function injectDynamicAdSlot(containerId, adUnitPath, size) {
googletag.cmd.push(() => {
const currentPosition = dynamicSlotCounter.toString();
dynamicSlotCounter += 1;
const slot = googletag.defineSlot(adUnitPath, size, containerId)
.addService(googletag.pubads())
.setTargeting('pos', currentPosition);
googletag.display(containerId);
});
}
⚙️ Google Ad Manager (GAM) Configuration Guidelines
After modifying the frontend code, the publisher must update their Google Ad Manager (GAM) settings so the ad server recognizes, records, and reports on the pos key-value.
1. Create & Configure the Key-Value in GAM UI
- In GAM, navigate to Inventory > Key-values.
- Click New key-value.
- Configure the key:
- Name:
pos
- Display name:
Slot Position
- Value type: Select Dynamic (allows any string value passed from code) OR Predefined (manually enter values:
1, 2, 3, 4, 5+).
- CRITICAL SETTING: Under Report on key-values, select Include in reports (Reportable).
⚠️ If the key is not set to "Reportable", GAM will accept the key-value for targeting, but will NOT allow reporting on viewability or impressions by position in GAM Reports.
2. Configure Historical Viewability Reports in GAM
To report on position-level viewability decay:
- In GAM, navigate to Reporting > Reports.
- Click New report and select the Historical report type.
- Set the Date range (e.g., Last 30 days).
- Add Dimensions:
- Ad unit
- Key-values (Select the key
pos)
- (Optional) Device category (Desktop / Mobile)
- Add Metrics:
- Total impressions
- Active View: % viewable impressions (The primary viewability metric)
- Active View: % measurable impressions
- Ad server CTR
- Average eCPM
- Run and Export the report to analyze where the viewability cliff occurs (e.g. identifying if viewability drops below 50% starting at
pos=3).
3. Line Item & Yield Optimization in GAM
Once pos is active in GAM, publishers can leverage it in monetization rules:
- Premium Line Item Sponsorships: Target high-impact above-the-fold inventory by setting Custom Targeting in line items to
pos is 1.
- Viewability-Guaranteed Campaigns: Exclude lower positions (e.g.,
pos is not 4, 5) for line items with strict 70%+ Active View viewability SLAs.
- Pricing & Floor Rules: Under Protection / Pricing Rules, apply lower floor prices to higher positions (
pos=4+) and higher floor prices to pos=1 and pos=2.
📊 Implementation & GAM Verification Matrix
| Area | Check / Blocker | Problem Description | Optimization / Remediation Action |
|---|
| Code Scope | Page-level instead of slot-level | Developer called googletag.pubads().setTargeting('pos', '1'). | Move to slot level. Call .setTargeting('pos', '...') on the individual slot object returned by googletag.defineSlot(). |
| Data Format | Numerical integer passed | Passed a number (e.g. .setTargeting('pos', 1)). | Convert to string. Use .setTargeting('pos', '1') or .setTargeting('pos', index.toString()). |
| GAM Key Setup | Key not marked as Reportable | Key pos was created in GAM, but cannot be added as a dimension in reports. | In GAM under Inventory > Key-values > pos, edit the key and check Include in reports (Reportable). |
| GAM Line Items | Missing from Custom Targeting | Line items cannot find the pos key. | Ensure the key pos is active in GAM. If using Predefined values, ensure values (1, 2, 3) are added to the key. |
| Ad Request Timing | Targeting set after ad display | .setTargeting() called after googletag.display(). | Chain .setTargeting() immediately after defineSlot() before calling display() or refresh(). |