API reference // reviewed
Adblock detection API reference
This reference documents the public contract of the AntiAdBlock Core Detection API: the aabc:detection window event, the window.AABC_ON_DETECT callback, the window.AABC_DETECTION snapshot, the four result fields and their values, when the result fires and what silence means. Detection is the same as in Recovery mode, but nothing is shown on screen; your code decides the rest.
The Detection API comes with Max ($12.99 a month) and Business. Free runs the same engine with the notice: 5,000 detections a month, no payment method.
The contract at a glance
- event
aabc:detection, on window- callback
window.AABC_ON_DETECT(result)- snapshot
window.AABC_DETECTION- result
{ detected, blockerType, path, browser }- fires
- once per page, also when
detectedisfalse; again only if the result changes on a tab revisit - silence
- no result means no answer: fail open
<script>
window.AABC_ON_DETECT = function (result) {
// Your logic here.
};
</script>
<script async
src="https://antiadblockcore.com/s/SITE_ID.js"
data-aabc-site="SITE_ID"
crossorigin="anonymous"></script>Enable it
The Detection API is a per-site setting on the Max and Business plans. Every site starts in Recovery mode, the default, which shows our notice. To switch, open the site in your dashboard, go to the Configuration tab and choose Detection API in the Script mode card; the card is available once the domain is verified.
No new tag is needed. Pages opened after the change get the new mode within a minute, while a page that is already open keeps the mode it loaded with. In this mode the notice isn't even part of the script your visitors download. Resetting the site's notice customization to the defaults also switches it back to Recovery mode, and the result stops being sent.
Requires The AntiAdBlock Core script tag on a site in Detection API mode (Max or Business plan).
The Detection API comes with Max ($12.99 a month) and Business. Free runs the same engine with the notice: 5,000 detections a month, no payment method.
The script tag
The same tag runs both modes. Copy it from the Your script tag card in the site's Overview tab and paste it into the <head> of every page where you want a result, as early as you can. It loads with async, so it never blocks rendering.
<script async
src="https://antiadblockcore.com/s/SITE_ID.js"
data-aabc-site="SITE_ID"
crossorigin="anonymous"></script>This example uses SITE_ID. A new site gets first-party delivery by default: once its DNS record is live, your dashboard shows a tag whose src is on a subdomain of your own domain, for maximum detection. Use the tag exactly as the dashboard gives it, without wrapping it in another loader. For each platform, see where the tag goes on WordPress, Shopify, Next.js and more.
The script is tamper-protected and bound to your domain: it runs on your verified domain and its subdomains, and stays silent anywhere else.
The result payload
Every result is a frozen, read-only object with exactly four keys. The same object reaches the event, the callback and the snapshot, and it uses the same blocker types as your dashboard.
{
"detected": true,
"blockerType": "extension",
"path": "…",
"browser": "chromium"
}{
"detected": false,
"blockerType": null,
"path": null,
"browser": "firefox"
}In the first example, path is elided: it holds an internal reference letter.
blockerType values
extension- an ad-blocking browser extension.
brave_shields- the Shields built into the Brave browser.
browser_protection- the browser's own protection, such as a strict tracking protection mode. On Firefox and Edge, network-only blocking is reported here too, the same way your dashboard reports it.
network_dns- blocking outside the browser, at network or DNS level, such as Pi-hole, AdGuard DNS or NextDNS. These visitors usually can't turn it off in one click, so treat them differently: asking them to allow your site only frustrates them.
unknown- a blocker was confirmed but not attributed to one of the types above.
TypeScript
export type AabcResult = Readonly<{
detected: boolean;
blockerType:
| "extension"
| "brave_shields"
| "browser_protection"
| "network_dns"
| "unknown"
| null;
// a reference code; "FORCE" for the #aabc_force test
path: string | null;
browser:
| "chromium"
| "firefox"
| "edge"
| "brave"
| "webkit"
| "other";
}>;
declare global {
interface Window {
AABC_DETECTION?: AabcResult;
AABC_ON_DETECT?: (result: AabcResult) => void;
}
interface WindowEventMap {
"aabc:detection": CustomEvent<AabcResult>;
}
}The payload carries these four keys and nothing else. Because it is frozen, copy it (for example with { ...result }) before you add fields of your own.
Three ways to read the result
Use whichever fits your setup, or several: all three carry the same object. For each result, the snapshot is updated first, then the event fires, then the callback is called. When the decision matters, use the callback: the script calls your function directly.
snapshoteventcallback
Window event: aabc:detection
A CustomEvent dispatched on window, not on document, with the result in event.detail. Add the listener before our script tag so it is ready when the result fires.
<script>
window.addEventListener("aabc:detection", function (event) {
// event.detail = { detected, blockerType, path, browser }
var result = event.detail;
if (result.detected) {
// Your logic here.
}
});
</script>Callback: window.AABC_ON_DETECT
Define this global and the script calls it with the same object. It is looked up each time a result is sent, so it has to exist by then; defining it before our tag is the simple way to be sure. An error thrown inside your function doesn't stop our script; it is caught silently, so wrap your logic in your own try/catch if you want to log it.
<script>
window.AABC_ON_DETECT = function (result) {
if (result.detected &&
result.blockerType !== "network_dns") {
// Your logic here.
}
};
</script>Snapshot: window.AABC_DETECTION
The latest result, kept on window for code that runs late, which is common in single-page apps. It is undefined until the first result and always holds the same object the event and the callback received.
var result = window.AABC_DETECTION;
if (result && result.detected) {
// Your logic here.
}When it fires
- Once per page, after the first full evaluation: roughly 0.1 to 3.6 seconds in, and never before
DOMContentLoaded. Anything you register beforeDOMContentLoadedis in time; code that loads later should read the snapshot. - It fires when
detectedisfalsetoo, so a page without a blocker gets an explicit answer, not silence. - When the visitor comes back to the tab, the script checks again and sends a new result only if
detectedorblockerTypechanged. A change tofalselets you remove a message you already showed. A re-check never counts as a second detection. - A client-side route change doesn't produce a new result (see single-page apps).
- page opens
- DOMContentLoaded
- result, about 0.1 to 3.6 s
- tab revisited
- new result only if it changed
When no result arrives
Silence is not "no ad blocker". No event, callback or snapshot is produced when:
- the site is in Recovery mode (the default, and where it returns after a plan change or a reset of the notice customization): the result is only sent in Detection API mode;
- our tag isn't on the page, or didn't load there;
- the domain isn't verified yet;
- the site or its script is paused or switched off, or the account isn't active;
- the account's monthly detection quota is used up (we email you at 80%, and results come back with the next cycle);
- the tag runs on a domain it isn't bound to;
- the visitor is identified as a bot or an automated browser, including headless and WebDriver-driven test runs;
- our service-wide kill switch is on.
The silent script carries no reason, by design. To see why a site is quiet, check its status in your dashboard and the Last event line in its Script mode card.
Treat a missing result as "no answer" and keep your normal path: in other words, fail open. If a flow waits for the result, give it a time limit and a default; the integration guide has a ready pattern.
detected: trueyour responsefalse, or no resultyour normal path
Testing
Add #aabc_force to any URL on your own site and load or reload the page to force a positive result: detected: true and path: "FORCE", with nothing shown. It lets you run your handler without installing an ad blocker. blockerType and browser still describe the real visit, so without a blocker blockerType normally comes back as unknown. In Recovery mode, the same hash shows the notice instead.
If your handler runs, the integration works. In Detection API mode, the Script mode card also shows a Last event line with when your site last reported (it updates about once a minute; reload the Configuration tab to see it). Test in a regular browser window, the way a visitor opens your page. Automated browsers get no result: in end-to-end tests, call your own handler with a contract object instead.
Single-page apps and frameworks
The result fires when a page is opened or reloaded, and again on a tab revisit only if it changed. A client-side route change doesn't produce a new result. In a single-page app, register your listener once on window and read window.AABC_DETECTION after navigation for the last known result; for a fresh check, load the page again.
In React or Next.js, add the tag once (in Next.js, inside the <head> of the root layout, exactly as the dashboard gives it) and read the result with a small hook:
"use client";
import { useEffect, useState } from "react";
import type { AabcResult } from "./aabc-types";
export function useAdblockResult() {
const [result, setResult] =
useState<AabcResult | null>(null);
useEffect(() => {
const handle = (event: CustomEvent<AabcResult>) =>
setResult(event.detail);
window.addEventListener("aabc:detection", handle);
if (window.AABC_DETECTION) {
setResult(window.AABC_DETECTION);
}
return () =>
window.removeEventListener("aabc:detection", handle);
}, []);
// null until a result arrives; stays null if none does
return result;
}"use client";
import { useAdblockResult } from "./use-adblock-result";
// in a client component
export function Download() {
const result = useAdblockResult();
if (result && result.detected) {
return <YourMessage blockerType={result.blockerType} />;
}
// the default path, also while there is no result
return <DownloadLink />;
}The hook reads the snapshot, so it also catches a result that arrived before your component mounted, and it removes its listener on unmount. The same two steps, listen for the event and read the snapshot, work in Vue, Svelte or plain JavaScript.
Analytics
To see your ad-block rate in your own analytics, forward one result per page. With Google Tag Manager, push it to the data layer and send it with a GA4 event tag on a Custom Event trigger named aabc_detection, reading the fields with three Data Layer Variables:
<script>
(function () {
function send(result) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "aabc_detection",
aabc_detected: result.detected,
aabc_blocker_type: result.blockerType || "none",
aabc_browser: result.browser
});
}
if (window.AABC_DETECTION) {
send(window.AABC_DETECTION);
} else {
window.addEventListener(
"aabc:detection",
function (event) { send(event.detail); },
{ once: true }
);
}
})();
</script>The snippet sends one result per page: the one already in the snapshot, or else the first to arrive, and the listener removes itself. Register aabc_detected, aabc_blocker_type and aabc_browser as event-scoped custom dimensions in GA4 to report on them. Without Tag Manager, send the same fields with gtag("event", "aabc_detection", { ... }). Pages with no result send nothing. Many ad blockers also block Google Tag Manager and GA4, so part of the blocked visits never reach your analytics; your AntiAdBlock Core dashboard still counts every detection.
Quota and plan changes
- A detection counts the same in both modes: one per page view where a blocker is found. Page views without one never count, and a re-check never counts twice.
- The quota belongs to the account and is shared across its sites. We email you at 80%; when it is used up, the script stays silent until the next cycle (see when no result arrives).
- Detections and blocker types keep flowing into your dashboard.
- If a plan change removes the Detection API, the site goes back to Recovery mode on its own, so visitors with a blocker see our notice again. You get an email and a note in your dashboard; your choice is kept and returns when you upgrade.
Contract history
-
Documentation reviewed: more cases with no result (Recovery mode, used-up quota, paused site, tag on another domain, automated browsers), the TypeScript types and the React and analytics examples are added, and
pathis documented as a reference code with no fixed list of letters. The contract doesn't change. -
First public version: the
aabc:detectionevent, theAABC_ON_DETECTcallback, theAABC_DETECTIONsnapshot and the four result fields.
Any change to the contract is listed here with its date.
Keep going
Detection API integration guide
The step-by-step walkthrough: a full example with a time limit, missing results and troubleshooting.
Adblock detection API
What you can build on the result, setup and plans.
Plans and pricing
The Detection API comes with Max and Business; every plan and price is public.
Live demo
A blocked visit, step by step, and an example site that runs the same engine with the notice.