Integrate verified
attention ads
Embed contextual, attention-verified ads on your site in under 5 minutes. No tracking cookies. No bidding complexity. Earn revenue proportional to the genuine attention your readers pay.
How it works
The Indium SDK loads on your page, detects the content context automatically, and requests the best-matching ad from our platform. It then tracks four verified attention signals — time in view, scroll depth, interaction, and repeat visits — and reports them when the reader leaves the page.
You earn 50% of every reward generated on your pages. Rewards are calculated per session based on the reader's verified attention score, not just whether an ad was shown.
Registration
Before integrating, you need a Publisher ID. This is assigned when
we register your site on our platform. Contact us at
publisher@indium.ad with your
site name and URL, and we'll provide your pub_id within 24 hours.
Your Publisher ID looks like this:
pub-techblog-sg-a3f2
Quickstart
This is the complete integration for most publishers — three additions to your HTML:
<!-- 1. Place this div where you want the ad -->
<!-- data-format: landscape_card | banner | native | leaderboard | portrait_card | skyscraper | halfpage -->
<div id="indium-ad" data-format="landscape_card" style="width:100%;max-width:600px"></div>
<!-- 2. Load the SDK scripts (before closing </body>) -->
<script src="https://api.indium.ad/sdk/dist/indium-sdk.js"></script>
<script src="https://api.indium.ad/sdk/badge.js"></script>
<!-- 3. Initialise with your Publisher ID -->
<script>
IndiumSDK.init({
publisherId: 'YOUR_PUB_ID', // replace with your ID
apiKey: 'YOUR_API_KEY', // replace with your API key
apiUrl: 'https://api.indium.ad',
});
IndiumSDK.requestAd('#indium-ad');
</script>
Load the scripts
Two scripts are needed. Place them just before the closing </body> tag
so they don't block page rendering.
1 — Main SDK
Handles ad requests, rendering, and attention tracking.
<script src="https://api.indium.ad/sdk/dist/indium-sdk.js"></script>
2 — Attention badge
Adds the floating Ⓐ badge to your page — a live attention score indicator for readers. Clicking it opens a reward panel for extension users. This script is optional but strongly recommended — it visibly signals to readers that your site participates in the attention rewards network.
<script src="https://api.indium.ad/sdk/badge.js"></script>
Place the ad slot
Add an empty <div> where you want the ad to appear.
Give it a unique id and set its width using CSS.
The SDK reads the div's rendered width and picks the
appropriate layout automatically.
<!-- Full width — renders as leaderboard (>728px) -->
<div id="indium-ad" style="width:100%"></div>
<!-- In-article card — renders as card (320–540px) -->
<div id="indium-ad" style="width:480px;max-width:100%"></div>
<!-- Sidebar — renders as banner (<320px) -->
<div id="indium-ad" style="width:280px"></div>
Initialise the SDK
Call IndiumSDK.init() once per page, then IndiumSDK.requestAd()
for each slot. Both calls must come after the SDK script tag.
<script>
IndiumSDK.init({
publisherId: 'pub-yoursite-xxxx',
apiKey: 'YOUR_API_KEY',
apiUrl: 'https://api.indium.ad',
});
IndiumSDK.requestAd('#indium-ad');
</script>
init() options
| Option | Type | Required | Description |
|---|---|---|---|
publisherId | string | Required | Your assigned Publisher ID. |
apiKey | string | Required | Your API key. Provided when your account is registered. Keep this confidential. |
apiUrl | string | Required | Platform API base URL. Do not include a trailing slash. |
debug | boolean | Optional | Set true to log SDK activity to the browser console. Remove before going live. |
Attention badge
The Ⓐ badge floats on your page and shows a live attention score that updates every second as the reader scrolls and interacts. The badge is fully self-contained — no configuration required beyond loading the script.
Readers with the Indium extension installed can click the badge to view their reward balance and NFT gallery. Readers without the extension see a prompt to install it.
Badge position
By default the badge appears in the top-right corner.
You can override this with the data-position attribute on the
<script> tag. Four positions are supported:
| Value | Corner | Best for |
|---|---|---|
top-right | Top-right Default | Most layouts — away from left-side navigation |
top-left | Top-left | Sites with right-side sidebars or sticky CTAs |
bottom-right | Bottom-right | Sites with top navigation bars or sticky headers |
bottom-left | Bottom-left | Sites with both a header and a right-side widget |
Usage
<!-- Default: top-right (no attribute needed) -->
<script src="https://api.indium.ad/sdk/badge.js"></script>
<!-- Top-left -->
<script src="https://api.indium.ad/sdk/badge.js"
data-position="top-left"></script>
<!-- Bottom-right -->
<script src="https://api.indium.ad/sdk/badge.js"
data-position="bottom-right"></script>
<!-- Bottom-left -->
<script src="https://api.indium.ad/sdk/badge.js"
data-position="bottom-left"></script>
Responsive layouts
The SDK automatically selects the best ad layout based on your slot's
rendered pixel width. No configuration needed — just set the width of your
<div>.
| Layout | Slot width | Description |
|---|---|---|
| Banner | < 320px |
Headline + CTA only. No image. Ideal for sidebars and mobile banners. |
| Card | 320 – 540px |
Vertical layout: image on top, headline, body, and CTA below. Best for in-article placements. |
| Native | 541 – 728px |
Horizontal layout: image on left, headline, body, and CTA on right. Good for mid-width content columns. |
| Leaderboard | > 728px |
Full-width horizontal strip. Ideal for above-the-fold or between-section placements on wide layouts. |
| Portrait Card | 300 × 420px |
Vertical portrait card. Matches deal-card and product-grid layouts. Great for e-commerce and lifestyle publishers. |
| Skyscraper | 160 × 600px |
Tall narrow sidebar unit. IAB standard. Suits tight sidebar rails common on crypto and tech media sites. |
| Half Page | 300 × 600px |
Large sidebar unit with hero image. Highest CPM of all formats. Premium sidebar placement. |
Controlling size
Set the slot width using inline CSS or a stylesheet class.
Use max-width:100% to ensure the ad is responsive on mobile.
<!-- Responsive — fills container, max 900px (leaderboard on desktop) -->
<div id="indium-ad" data-format="leaderboard" style="width:100%;max-width:900px"></div>
<!-- Fixed card for in-article use -->
<div id="indium-ad" data-format="landscape_card" style="width:480px;max-width:100%"></div>
<!-- Via CSS class -->
<style>
.my-ad-slot { width: 100%; max-width: 640px; margin: 24px auto; }
</style>
<div id="indium-ad" data-format="native" class="my-ad-slot"></div>
Keyword context
The SDK auto-detects your page's topic from meta tags, headings, and body text. In most cases no configuration is needed. If you want to override the detected context — for example on a page with mixed content — you can pass keywords manually:
IndiumSDK.requestAd('#indium-ad', {
keywords: ['developer', 'cloud', 'devops'],
category: 'tech',
});
Supported categories
| Category | Matched topics |
|---|---|
tech | Software, APIs, cloud, engineering, SaaS, startups |
finance | Investing, banking, savings, crypto, tax, wealth |
travel | Relocation, expat, visas, international moving |
health | Wellness, fitness, nutrition, medical, therapy |
lifestyle | Fashion, home, food, design, family, parenting |
education | Learning, courses, career, certifications, skills |
Debug mode
Enable debug mode during development to see exactly what the SDK is doing in the browser console.
IndiumSDK.init({
publisherId: 'pub-yoursite-xxxx',
apiKey: 'YOUR_API_KEY',
apiUrl: 'https://api.indium.ad',
debug: true, // ← remove before going live
});
With debug on you will see log lines like:
[IndiumSDK] page context detected: tech ['developer','cloud','api']
[IndiumSDK] slot width: 640px → layout: native
[IndiumSDK] ad received: Ship faster with TechTools Pro
[IndiumSDK] flushing signals: {time_in_view_ms: 18400, max_scroll_pct: 72, ...}
Multiple ad slots
You can display multiple ads on a single page — similar to how Google Ads works. The rules are simple:
| Item | How many times |
|---|---|
| SDK script tags | Once per page |
IndiumSDK.init() | Once per page |
<div> slot | Once per ad — each needs a unique id |
IndiumSDK.requestAd() | Once per slot — one call per div |
Attention scoring — no double counting
Each slot receives a different campaign — the platform selects independently per slot based on your page context. The same campaign will never appear twice on the same page.
Attention is tracked independently per slot. If a reader views slot 1 for 20 seconds and slot 2 for 10 seconds, those are recorded as two separate sessions — neither inflates the other.
Complete example — three ads on one page
<!DOCTYPE html>
<html>
<body>
<!-- Step 1: place divs wherever you want ads -->
<!-- Each div needs a unique id and a width -->
<!-- Leaderboard at the top (full width) -->
<div id="ad-top" data-format="leaderboard" style="width:100%;margin-bottom:24px"></div>
<!-- ...your article content... -->
<!-- Card in the middle of the article -->
<div id="ad-mid" data-format="landscape_card" style="width:480px;max-width:100%;margin:24px 0"></div>
<!-- ...more article content... -->
<!-- Native at the bottom -->
<div id="ad-bottom" data-format="native" style="width:640px;max-width:100%;margin-top:24px"></div>
<!-- Step 2: load scripts once, just before </body> -->
<script src="https://api.indium.ad/sdk/dist/indium-sdk.js"></script>
<script src="https://api.indium.ad/sdk/badge.js"></script>
<!-- Step 3: init once, requestAd once per slot -->
<script>
IndiumSDK.init({
publisherId: 'YOUR_PUB_ID',
apiKey: 'YOUR_API_KEY',
apiUrl: 'https://api.indium.ad',
});
IndiumSDK.requestAd('#ad-top'); // → leaderboard (full width)
IndiumSDK.requestAd('#ad-mid'); // → landscape_card (480px)
IndiumSDK.requestAd('#ad-bottom'); // → native (640px)
</script>
</body>
</html>
id. If two divs share
the same id (e.g. both called indium-ad), the second call to
requestAd() will overwrite the first ad. Use distinct ids:
ad-top, ad-mid, ad-bottom, etc.
Custom card design
If your site renders its own ad card HTML — for example injecting ads into
an existing deal or product grid — you can still attach our full attention
tracker to your elements using attachToElements().
You keep your card design; we handle the tracking.
<div>.
Step 1 — Add campaign ID to your ad data
In your ads data source (e.g. ads.json), add the
campaignId field provided by Indium when your campaign
is registered:
{
"advertiser": "GlobalMove",
"title": "Your stress-free international move",
"image": "https://...",
"url": "https://globalmove.com",
"active": true,
"campaignId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ← add this
}
Step 2 — Add data attributes to your card HTML
In your card render function, add two data- attributes to the
outermost element of each ad card:
// In your renderAdCard() function — add data attributes to the wrapper element
return `<a class="deal-card ad-card"
data-campaign-id="${ad.campaignId}"
data-campaign-name="${ad.advertiser}"
href="${ad.url}"
target="_blank" rel="noopener">
...your existing card HTML...
</a>`;
Step 3 — Call attachToElements() after render
After your grid renders, call attachToElements() once with a
selector that matches your ad cards. The SDK will find all matching elements,
attach an independent tracker to each, and skip any that were already attached.
function render() {
// ... your existing render logic ...
grid.innerHTML = list.map(...).join('');
// Attach Indium tracker to all ad cards after render
if (window.IndiumSDK) {
IndiumSDK.attachToElements('[data-campaign-id]');
}
}
attachToElements() again is safe —
it detects already-attached elements via a data-w4-attached attribute
and skips them automatically.
What gets tracked
Attention tracking on your custom cards is identical to standard
requestAd() slots — the same verified attention signals are
collected and the same reward calculation applies. Your earnings are
credited in exactly the same way.
Troubleshooting
Ad slot is blank
Enable debug mode and check the browser console. Common causes:
- No campaigns are currently active on the platform
publisherIdwas not passed toinit()- The SDK script loaded after
requestAd()was called — ensure script tags come first
CORS error in console
This should not occur — the platform API allows all origins. If you see a CORS
error, check that apiUrl does not have a trailing slash and matches
exactly: https://api.indium.ad
Ad renders at wrong size
The SDK reads slot.offsetWidth at the moment requestAd()
is called. If your slot is hidden or not yet in the DOM at that point, the width
will be 0 and the ad will fall back to the card layout (480px assumed). Ensure
the slot div is visible before calling requestAd().
Earnings not showing
Earnings accumulate per attention session and are visible in your publisher dashboard after the first complete session (user visits page, engages, then navigates away). Sessions in progress do not show until the tab closes.
Single Page App (SPA) — attention score not recording
If your site dynamically rebuilds its content grid without navigating to a new
page (e.g. on filter or sort changes), ensure IndiumSDK.init() is
called as early as possible — ideally as the very first script on the page,
before any async data loading. Calling init() inside an
async function or after an await can cause missed
reward submissions. The SDK handles dynamic re-renders automatically once
initialised correctly.
API reference
IndiumSDK.init(config)
Initialises the SDK. Must be called before any other method. Call once per page.
| Parameter | Type | Description |
|---|---|---|
publisherId | string | Your Publisher ID (required). |
apiUrl | string | Platform base URL, no trailing slash (required). |
debug | boolean | Enable console logging. Default: false. |
IndiumSDK.requestAd(selector, context?)
Fetches and renders an ad into the specified slot. Returns a Promise.
| Parameter | Type | Description |
|---|---|---|
selector | string | CSS selector for the slot element, e.g. '#indium-ad'. |
context.keywords | string[] | Optional keyword override. Auto-detected if omitted. |
context.category | string | Optional category override. Auto-detected if omitted. |
IndiumSDK.getPseudoId()
Returns the pseudonymous reader ID currently in use. Useful for attribution if you run your own backend.
Earnings model
You earn a share of every reward generated on your pages. Rewards are calculated per session based on verified reader attention — not simply whether an ad was displayed.
Our attention scoring considers multiple signals including how long the ad was visible in the reader's viewport, how far they scrolled, whether they interacted with the ad, and whether they are a returning visitor. The platform rate and publisher revenue share are defined in your publisher agreement.