One page covering setup, ad types, integration code, targeting, the API, and payouts — from creating your first instance to your first cashout.
AmanahAds connects two sides: advertisers who create campaigns and pay to reach an audience, and developers (publishers) who embed ad slots on their website or app and earn a share of what advertisers pay.
The flow, end to end:
Instances → New Instance.Use your Development key first. It renders immediately with no domain restriction, so you can confirm the integration works before switching to a verified Production key.
Every ad request specifies a type and size. Each type only supports specific sizes:
| Type | Description | Sizes |
|---|---|---|
| 🖼️ Banner | Static or animated image shown inline | Leaderboard (728×90), Medium Rectangle (300×250), Mobile Banner (320×50), Large Rectangle (336×280), Half Page (300×600) |
| 🎬 Video | Auto-playing inline video, no reward gate | Leaderboard (728×90), Medium Rectangle (300×250), Mobile Banner (320×50), Large Rectangle (336×280), Half Page (300×600) |
| 🪟 Interstitial | Fullscreen ad shown between content transitions; close button appears after a short delay | Fullscreen (Interstitial / Rewarded) |
| 🎁 Rewarded | Fullscreen video the user opts into watching in exchange for an in-app reward | Fullscreen (Interstitial / Rewarded) |
Two ways to embed an ad slot. Both work for Banner/Video; Interstitial/Rewarded automatically render fullscreen regardless of which mode you pick.
Fully isolated — the ad runs in its own document, nothing it does can interfere with your page. No script tag needed.
<iframe src="https://amanahads.com/api/ads/frame?key=YOUR_KEY&type=BANNER&size=LEADERBOARD" width="728" height="90" frameborder="0" scrolling="no" style="border:none;display:block;"> </iframe>
Add the loader script once per page, then drop a <div> wherever you want an ad. Lightweight, renders directly in your page's DOM.
<script src="https://amanahads.com/api/ads/serve.js" data-key="YOUR_KEY" async></script> <div id="amanah-ad-banner-leaderboard" data-type="BANNER" data-size="LEADERBOARD"> </div>
Add data-mode="iframe" on the div to have the script embed via iframe instead of building DOM directly — same isolation benefit as the iframe method above, without hand-writing the iframe tag.
Rewarded ads work like AdMob or Unity Ads: AmanahAds only verifies that the viewer watched for the required time and tells your app — it never decides or grants the actual reward. What the reward is (coins, an unlocked level, extra time, an access pass, anything) is entirely up to your own app's logic.
<script src="https://amanahads.com/api/ads/serve.js" data-key="YOUR_KEY" async></script>
<div
id="amanah-ad-rewarded"
data-type="REWARDED"
data-size="FULLSCREEN"
data-onreward="onAmanahReward">
</div>
<script>
function onAmanahReward() {
// Called only after the server verifies the required watch time elapsed.
// Grant your own in-app reward here — coins, unlock, whatever you want.
}
</script>If you're embedding via a raw iframe instead of the script loader, listen for the postMessage event instead:
<script>
window.addEventListener("message", function (e) {
if (e.data && e.data.type === "amanah-reward-granted") {
onAmanahReward();
}
});
</script>The watch-time check happens server-side, against the actual elapsed time since the ad was served — never trusting anything the browser reports — so it can't be gamed by calling the callback early.
When you register an instance you choose one of two types:
com.example.myapp), plus an optional Play Store / App Store link.You can also optionally declare a category and primary audience languagefor your instance — this is what lets category-targeted campaigns match your traffic (see Audience Targeting).
Ad types can be added or removed after creation too — use the Ad Types button on the instance card in your dashboard.
Every instance gets two keys:
Origin/Referer matching the instance's URL.Keys can be regenerated any time from the instance's Keys dialog if one is ever compromised.
Advertisers can optionally restrict a campaign to a specific audience. As a publisher you don't configure any of this yourself — it's automatic based on the request, except for two fields you can optionally opt into passing.
| Dimension | How it's known |
|---|---|
| Country | Detected automatically from the request's IP. |
| Device / OS | Detected automatically from the browser's User-Agent. |
| Language | Detected automatically from the browser's Accept-Language header. |
| Category | Matched against your instance's declared category (set once when registering, see Instance Setup). |
| Age / Gender | Never inferred. Only used if your app explicitly passes it — add data-age / data-gender on the ad element if your own app already knows this about the viewer (e.g. from your own login). |
Soft targeting: if a signal is unknown for a given request, the campaign still serves — targeting only narrows delivery where the signal actually is available. It never results in a blank ad slot just because a signal wasn't present.
All endpoints are under https://amanahads.com/api/ads/, CORS-enabled for use from any origin.
Params: key, type, size (+ optional age, gender). Returns JSON.
{ "ad": { "id", "creativeUrl", "creativeType", "clickUrl", "adType", "adSize", "instanceId", "destinationUrl" } }
// or { "ad": null } when nothing is eligible to fill the slot
// or { "error": "..." } — see status codes belowSame params as /serve, but returns a complete standalone HTML page (for use in an iframe) with click tracking, the feedback menu, and reward/interstitial behavior already wired in.
No params — returns the embeddable loader script described in Integration Methods.
Params: campaignId, instanceId, creativeId, key. Always redirects to the campaign's destination URL; records the click and CPC earning behind the scenes. You won't call this directly — it's the URL baked into clickUrl.
Body: { impressionId }. Used internally by the reward flow — see Rewarded Ads.
{ "granted": true }
// or { "granted": false, "reason": "too_early" | "not_found" | "not_a_rewarded_campaign" | "missing_impression_id" }| Error | Status | Meaning |
|---|---|---|
| invalid_key | 401 | The key doesn't exist or is deactivated. |
| instance_inactive | 403 | The instance was banned or restricted by an admin. |
| not_verified | 403 | Production key pending admin verification. |
| domain_not_approved | 403 | Request origin doesn't match the instance's registered URL. |
| unsupported_ad_type | 400 | This instance never registered that type/size combo. |
Useful context for why a slot might show nothing even when everything on your end is set up correctly — a campaign only serves once it reaches ACTIVE:
DRAFT → PENDING_REVIEW → APPROVED → PENDING_PAYMENT → ACTIVE → (PAUSED / COMPLETED / REJECTED)
A campaign leaves ACTIVE automatically once its budget is exhausted (→ COMPLETED) or its end date passes.
Bangladesh only for now: both advertiser payments and developer cashouts go exclusively through bKash. There's no card, PayPal, or other international payment method yet, so using AmanahAds in either direction currently requires a Bangladeshi bKash account.
I embedded the code but see "No ads available"
This means the request reached us fine, but no active campaign currently matches that exact ad type + size. It's usually not a setup mistake — check whether any advertiser currently has a live, funded campaign for that specific combination. Try a different registered ad type/size to confirm the pipeline itself works.
I see "Ad type not supported"
Your instance hasn't registered that exact type+size combo. Add it from the instance's Ad Types dialog.
I see "Production Key Not Verified"
Production keys need one-time admin approval before they'll serve ads. Use your Development key while waiting, or while testing anywhere other than your live domain.
I see "Unauthorized Environment"
Your Production key is verified, but the request's origin doesn't match the URL you registered for the instance. This check doesn't apply to the Development key.
The ad renders blank with nothing in it
Automated/bot traffic (headless browsers, crawlers, common scraping tools) is intentionally served an empty response rather than a real ad or an error, so this is often expected behavior for non-human requests.