Publisher SDK v0.1.0 · badge.js v0.1.0

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.

No cookies required. The SDK uses a pseudonymous identity system. Readers who install the Indium browser extension earn Solana NFT rewards for their attention — but the SDK works for all readers regardless of whether the extension is installed.

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
Keep your Publisher ID safe. It is tied to your earnings wallet. Do not share it publicly or commit it to a public repository without reviewing your site's access controls.

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>
That's it for a single ad. The SDK auto-detects your page's topic from headings, meta tags, and body text — no keyword configuration needed. To display multiple ads, see Multiple ad slots.

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>
Place the slot within your article content, not in a fixed overlay or off-screen container. The attention tracker uses IntersectionObserver — the ad must be visible in the viewport for time-in-view to accumulate.

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

OptionTypeRequiredDescription
publisherIdstringRequiredYour assigned Publisher ID.
apiKeystringRequiredYour API key. Provided when your account is registered. Keep this confidential.
apiUrlstringRequiredPlatform API base URL. Do not include a trailing slash.
debugbooleanOptionalSet 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.

The badge is draggable — readers can reposition it if it overlaps your content. Its position resets on page reload.

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:

ValueCornerBest for
top-rightTop-right DefaultMost layouts — away from left-side navigation
top-leftTop-leftSites with right-side sidebars or sticky CTAs
bottom-rightBottom-rightSites with top navigation bars or sticky headers
bottom-leftBottom-leftSites 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>
The position sets the badge's initial corner only. Readers can still drag it anywhere on the page — the starting position just determines where it first appears on load.

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

LayoutSlot widthDescription
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.
Banner
< 320px · sidebar / mobile
Headline · CTACTA
Card
320–540px · in-article
Headline · Body · CTA
Native
541–728px · content column
Headline · Body · CTA
Leaderboard
> 728px · full width
Headline · Body · CTA CTA
Portrait Card
300×420px · card grid
Headline · Body · CTA
Skyscraper
160×600px · sidebar rail
Headline · Body CTA
Half Page
300×600px · premium sidebar
Headline · Body CTA

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>
Do not set height on the slot div. The SDK sets height automatically based on the layout. Setting a fixed height may clip the ad.

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

CategoryMatched topics
techSoftware, APIs, cloud, engineering, SaaS, startups
financeInvesting, banking, savings, crypto, tax, wealth
travelRelocation, expat, visas, international moving
healthWellness, fitness, nutrition, medical, therapy
lifestyleFashion, home, food, design, family, parenting
educationLearning, 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:

ItemHow many times
SDK script tagsOnce per page
IndiumSDK.init()Once per page
<div> slotOnce 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>
Each slot receives a different campaign — the platform selects independently per slot. The same campaign will never appear twice on the same page.
Each slot div must have a unique 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.

This is the integration path for publishers who blend our ads into their existing content grid rather than using a separate ad slot <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]');
  }
}
Safe to call after every render. If your grid re-renders (e.g. on filter change), calling 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
  • publisherId was not passed to init()
  • 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.

ParameterTypeDescription
publisherIdstringYour Publisher ID (required).
apiUrlstringPlatform base URL, no trailing slash (required).
debugbooleanEnable console logging. Default: false.

IndiumSDK.requestAd(selector, context?)

Fetches and renders an ad into the specified slot. Returns a Promise.

ParameterTypeDescription
selectorstringCSS selector for the slot element, e.g. '#indium-ad'.
context.keywordsstring[]Optional keyword override. Auto-detected if omitted.
context.categorystringOptional 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.

Tip: Place ads within your article body — not in a footer or off-screen position. Ads that are visible and contextually relevant generate higher attention scores and therefore higher earnings per session.