Install the pixel in any stack
One script tag measures the audience for every website, web app, portal, dashboard, or embedded widget that displays FXMacroData content. It is the same tag everywhere; what changes between frameworks is where you put it and how you hand it route changes. Apps, bots, and assistants have no browser, and use the backend meter instead.
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
No key, no site id, no account number. The pixel identifies your account from the browser Origin, which is why the domain has to be registered in API Management first. An event from an unregistered origin is rejected with origin is not an approved redistribution domain.
Start with your own channel
This page is the full reference for websites and web apps, covering every platform at once. If you only want the steps for one surface, open its install guide instead: it names the meter that applies to you, and the meter that does not, before any instructions. Find your channel.
1. Configuration attributes
| Attribute | Values | What it does |
|---|---|---|
data-fxmd-meter-mode | page (default), exposure | Whether every page view counts, or only views of a tagged FXMacroData component. See below. |
data-fxmd-attribution-mode | verify (default), inject | verify reports whether your own attribution element is present and visible. inject adds a small fixed badge in the bottom-right corner if none is found. |
data-fxmd-endpoint | An absolute URL | Overrides where events are sent. Needed only if you relay the collector through your own domain, which has its own rules: see below. |
<script
async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"
data-fxmd-attribution-mode="inject"></script>
The shorter forms data-meter-mode and data-attribution-mode are also accepted, for platforms whose tag editors strip vendor prefixes.
What it sends
A page_view on load and on each route change, a module_view when a tagged component becomes visible, one heartbeat 15 seconds in, and an attribution_click if someone clicks your credit link. Each carries an anonymous visitor id, the path, the viewport, timezone and language, and whether the attribution element was visible. Transport is navigator.sendBeacon with a fetch fallback, so it never blocks rendering and never fails loudly. No personal data, no cross-site identifier, and nothing about the individual beyond a random id your browser stores.
2. Which domains to register
- Apex and www are one entry. Registering
https://example.comalso acceptshttps://www.example.com, and the reverse. - Every other subdomain is its own entry.
app.example.com,portal.example.com, anddashboard.example.comeach need registering. Adding a domain never adds a licence fee. - The scheme and port matter. Register the origin you actually serve. A non-standard port is part of the origin.
- Register staging too if you want to test there, or accept that staging events are rejected. Rejected events are not billed, so an unregistered staging site is harmless, just invisible.
- Localhost is not registerable. Verify on a deployed origin.
The site identity in your reporting is the hostname the page was served from, so a single licence covering five subdomains still shows you five rows and one combined billable total.
3. Page mode vs exposure mode
Page mode (default)
Every human page view counts the visitor. Correct when the pixel only loads on pages that show FXMacroData content: a rates page, a calendar page, a set of market templates.
Do not put a page-mode pixel in a global layout on a site where most pages have no FXMacroData content. You will meter your marketing traffic.
Exposure mode
Only a view of a component you have tagged counts. Correct for a single-page app or a global layout, where you load the pixel once and let the tagging decide.
Exposure mode without tagging measures nobody. If you choose it, you must tag; the two halves are one decision.
There is a safety net: if an exposure-mode site records no tagged component views at all in a period, billing falls back to counting its human page views, so an untagged site is never silently metered at zero. It is a backstop, not a plan. Once you tag correctly the fallback stops applying and your marketing-page visitors go back to being uncounted, which is the whole point of choosing exposure mode.
4. Tagging the data components
Put both attributes on the outermost element of each FXMacroData-powered component. The pixel watches it with an IntersectionObserver and reports the first time at least 10% of it enters the viewport, once per route.
<section data-fxmd-metered data-fxmd-module="release-calendar">
<!-- your rendering of the FXMacroData calendar -->
</section>
<div data-fxmd-metered data-fxmd-module="usd-cpi-chart">...</div>
<aside data-fxmd-metered data-fxmd-module="rate-differentials">...</aside>
- Both attributes are required.
data-fxmd-modulealone names a component in the page payload but does not meter it;data-fxmd-meteredis what arms the observer. - Module names are lowercase, up to 64 characters, from
a-z 0-9 _ . : -. Anything else is dropped silently, which looks exactly like nothing happening. Keep them stable: they are how you read your own usage later. - Content added later is picked up. A MutationObserver rescans on DOM changes and on class, style, hidden, and aria-hidden changes, so tabs, accordions, modals, and lazy-loaded panels all work without extra code.
- Up to 16 module names are collected per event. Tag components, not every row in a table.
5. The JavaScript API
When a component is drawn to a canvas, rendered inside a web component, or only exists after a user action, tag it in code instead.
// Report a module view explicitly.
window.FXMacroDataPixel.trackModule("release-calendar");
// Options: dedupe defaults to true (once per module per route).
window.FXMacroDataPixel.trackModule("usd-cpi-chart", {
dedupe: false,
source: "chart-render",
element_label: "USD CPI, 12m",
});
// Read the current anonymous context, e.g. for your own debugging.
window.FXMacroDataPixel.getContext();
// { anonymous_visitor_id, anonymous_session_id, site_id, meter_mode, meter_version }
The script is loaded async, so it may not have booted when your code runs. Use the queue and nothing is lost:
window.FXMacroDataPixel = window.FXMacroDataPixel || { q: [] };
window.FXMacroDataPixel.q.push(["trackModule", "release-calendar"]);
Queued commands run as soon as the pixel initialises, and the same q.push form keeps working afterwards.
6. The attribution link
The licence requires visible FXMacroData credit wherever the data appears. Mark your own element and the pixel confirms it is present, on screen, and not hidden by CSS.
<a href="https://fxmacrodata.com/?utm_source=example.com&utm_medium=partner"
data-fxmd-attribution>Data powered by FXMacroData</a>
The pixel reports the element position, size, text, and whether it was visible in the viewport at the time of the event. It never moves or restyles your element. Set data-fxmd-attribution-mode="inject" only if you would rather we add a small fixed badge for you; a real credit inside your own layout is better for both sides.
7. Single-page frameworks
Route changes are handled for you. The pixel wraps history.pushState and history.replaceState and listens for popstate and hashchange, so every client-side navigation produces a fresh page view and rescans for tagged components. You do not need a router hook. Load the script once in the shell, choose exposure mode, and tag.
Plain HTML
<!-- before </body>, on pages that display FXMacroData content -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
React (Vite, Create React App)
<!-- index.html -->
<script
async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
// ReleaseCalendar.tsx
export function ReleaseCalendar({ events }) {
return (
<section data-fxmd-metered data-fxmd-module="release-calendar">
{events.map((e) => <EventRow key={e.id} event={e} />)}
</section>
);
}
Next.js, app router
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
strategy="afterInteractive"
data-fxmd-meter-mode="exposure"
/>
</body>
</html>
);
}
App router navigation uses the History API, so route changes are picked up without a usePathname effect. Keep strategy="afterInteractive"; beforeInteractive would run the pixel before your app renders anything to observe.
Next.js, pages router
// pages/_document.tsx
<Head>
<script
async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"
/>
</Head>
Vue 3
<!-- index.html -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
<!-- ReleaseCalendar.vue -->
<template>
<section data-fxmd-metered data-fxmd-module="release-calendar">
<EventRow v-for="e in events" :key="e.id" :event="e" />
</section>
</template>
Nuxt 3
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
script: [{
src: "https://fxmacrodata.com/static/js/fxmacrodata-pixel.js",
async: true,
"data-fxmd-meter-mode": "exposure",
}],
},
},
});
Angular
<!-- src/index.html, before </body> -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
<!-- release-calendar.component.html -->
<section data-fxmd-metered data-fxmd-module="release-calendar">
<app-event-row *ngFor="let e of events" [event]="e"></app-event-row>
</section>
SvelteKit
<!-- src/app.html, inside %sveltekit.body% wrapper -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
Astro
---
// src/layouts/Base.astro
---
<slot />
<script is:inline async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
is:inline stops Astro from bundling and rewriting the tag, which would strip the data attributes.
Remix and React Router
// app/root.tsx
<body>
<Outlet />
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure" />
<Scripts />
</body>
Gatsby
// gatsby-ssr.js
export const onRenderBody = ({ setPostBodyComponents }) => {
setPostBodyComponents([
<script key="fxmd" async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure" />,
]);
};
Solid, Qwik, Preact, Alpine, htmx
<!-- Same tag in the shell HTML. Anything that navigates through the
History API or replaces DOM in place is already handled. -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
8. Server-rendered frameworks
With classic server rendering you have a real choice: put the tag in a partial included only by the templates that show FXMacroData content and stay in page mode, or put it in the base layout and switch to exposure mode. The first is simpler and usually more accurate.
Django
{# templates/partials/fxmd_pixel.html #}
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
{# templates/markets/calendar.html #}
{% include "partials/fxmd_pixel.html" %}
Flask / Jinja
{# templates/base.html #}
{% block fxmd_pixel %}{% endblock %}
{# templates/calendar.html #}
{% block fxmd_pixel %}
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
{% endblock %}
Ruby on Rails
<%# app/views/layouts/application.html.erb %>
<%= yield :fxmd_pixel %>
<%# app/views/markets/calendar.html.erb %>
<% content_for :fxmd_pixel do %>
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
<% end %>
Laravel / Blade
{{-- resources/views/layouts/app.blade.php --}}
@stack('fxmd')
{{-- resources/views/markets/calendar.blade.php --}}
@push('fxmd')
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
@endpush
ASP.NET Core, Razor
@* Views/Shared/_Layout.cshtml *@
@await RenderSectionAsync("FxmdPixel", required: false)
@* Views/Markets/Calendar.cshtml *@
@section FxmdPixel {
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
}
Spring Boot, Thymeleaf
<!-- templates/fragments/fxmd.html -->
<script th:fragment="pixel" async
src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
<!-- templates/calendar.html -->
<div th:replace="fragments/fxmd :: pixel"></div>
Express with EJS, Handlebars, Pug
<%- include('partials/fxmd-pixel') %> <!-- EJS -->
{{> fxmd-pixel}} <!-- Handlebars -->
script(async, src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js") //- Pug
Plain PHP
<?php if ($page_shows_fxmd_data): ?>
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
<?php endif; ?>
9. CMS and no-code platforms
Most hosted platforms give you a custom-code box for the site head or footer. Paste the tag there, then decide the mode: site-wide box plus exposure mode plus tagging, or a page-level box on just the data pages plus page mode.
WordPress, no plugin
// functions.php in a child theme
add_action('wp_footer', function () {
if (!is_page(['market-calendar', 'rates'])) return; // data pages only
echo '<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>';
});
WordPress, site-wide
<!-- Any header/footer scripts plugin, or theme options -> custom code -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
<!-- then wrap the shortcode/block output -->
<div data-fxmd-metered data-fxmd-module="release-calendar">[fxmd_calendar]</div>
Shopify
<!-- layout/theme.liquid, before </body> -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
Use the theme file, not a Custom Pixel in the Customer Events sandbox: the sandbox has no access to your DOM, so tagged components would never be seen.
Webflow
Project Settings -> Custom Code -> Footer Code
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
Then on the wrapper element: Settings -> Custom attributes
data-fxmd-metered = (leave blank)
data-fxmd-module = release-calendar
Squarespace
Settings -> Advanced -> Code Injection -> Footer
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
Per page: Page Settings -> Advanced -> Page Header Code Injection
Wix
Settings -> Custom Code -> Add Custom Code
Place in: Body - end
Load on: All pages, or Choose specific pages
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
Ghost
Settings -> Code injection -> Site footer
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"
data-fxmd-meter-mode="exposure"></script>
Drupal
// mytheme.theme
function mytheme_page_attachments_alter(array &$attachments) {
$attachments['#attached']['html_head'][] = [[
'#tag' => 'script',
'#attributes' => [
'src' => 'https://fxmacrodata.com/static/js/fxmacrodata-pixel.js',
'async' => TRUE,
'data-fxmd-meter-mode' => 'exposure',
],
], 'fxmd_pixel'];
}
Joomla, HubSpot, Framer, Bubble, Webnode
Site settings -> custom head or footer code -> paste the tag.
Add the two data attributes to the container element in whichever
element-settings panel the platform provides.
10. Static site generators
Hugo
<!-- layouts/partials/fxmd-pixel.html -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
<!-- layouts/_default/baseof.html -->
{{ if .Params.fxmd }}{{ partial "fxmd-pixel.html" . }}{{ end }}
Jekyll
<!-- _includes/fxmd-pixel.html -->
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
<!-- _layouts/default.html -->
{% if page.fxmd %}{% include fxmd-pixel.html %}{% endif %}
Eleventy
<!-- _includes/layouts/base.njk -->
{% if fxmd %}
<script async src="https://fxmacrodata.com/static/js/fxmacrodata-pixel.js"></script>
{% endif %}
Docusaurus, MkDocs, VitePress
// docusaurus.config.js
scripts: [{
src: "https://fxmacrodata.com/static/js/fxmacrodata-pixel.js",
async: true,
"data-fxmd-meter-mode": "exposure",
}]
11. Tag managers
Google Tag Manager
Tag type: Custom HTML
<script>
var s = document.createElement("script");
s.async = true;
s.src = "https://fxmacrodata.com/static/js/fxmacrodata-pixel.js";
s.setAttribute("data-fxmd-meter-mode", "exposure");
document.body.appendChild(s);
</script>
Trigger: All Pages (exposure mode), or a Page View trigger
limited to your data pages (page mode).
Build the element in code rather than pasting a raw script tag: GTM strips unknown attributes from injected tags, which would silently drop your mode setting. Do not tick Support document.write.
Segment, Tealium, and similar
// Any "custom JavaScript" destination, loaded once per page:
!function(){
var s = document.createElement("script");
s.async = true;
s.src = "https://fxmacrodata.com/static/js/fxmacrodata-pixel.js";
s.setAttribute("data-fxmd-meter-mode", "exposure");
document.body.appendChild(s);
}();
A tag manager adds a layer that can be misconfigured, blocked by consent tooling, or deployed late. If you control the site template, put the tag in the template.
12. CSP, consent, and privacy
Content Security Policy
script-src 'self' https://fxmacrodata.com;
connect-src 'self' https://fxmacrodata.com;
connect-src is the one people forget. Without it the script loads, runs, and every beacon is blocked, so the pixel looks installed and measures nothing. If you use nonces, put the nonce on the script tag; the pixel adds no inline script of its own.
Pinning the script
There is no version-pinned URL and no published integrity hash for the copy on fxmacrodata.com. The file is served with a short cache and is replaced in place whenever the meter is updated, so an integrity attribute pinned against that URL would stop the pixel loading on the next release. If your policy requires a pinned hash, host the file on your own domain unmodified and pin your own: see self-hosting and relaying. Whichever copy runs, it reports its own build on every event as meter_version, so your reporting always shows which version produced a figure.
What it stores on the visitor device
| Key | Where | Lifetime | Contents |
|---|---|---|---|
fxmd_partner_vid | Cookie, first-party, SameSite=Lax, Secure on HTTPS | 395 days | A random id generated in the browser |
fxmd_partner_vid | localStorage | Until cleared | The same random id, so a cleared cookie does not double-count |
fxmd_partner_sid | sessionStorage | The tab session | A random session id |
All three are first-party to your domain and meaningless anywhere else. Nothing is read from or written to any other site, there is no cross-site identifier, and no advertising use. If storage is unavailable the pixel falls back to an in-memory id for that page view, which over-counts rather than under-counts.
Consent
This is a licence-compliance meter, not analytics or advertising, and most operators treat it the way they treat any first-party measurement they are contractually obliged to run. Where your counsel decides it needs consent, gate the tag the same way you gate the rest, and understand the trade: an unconsented visitor is not metered, is not billed, and is not visible in your reporting.
Disclose it in your privacy policy the way you disclose other first-party measurement: the cookie name, the retention, and that it counts unique readers for a data licence and nothing else.
Ad blockers and network filtering
Some visitors block third-party scripts by hostname. That under-counts you, which is not a problem for us. If it is a problem for your own reporting, host the file on your own domain and relay the collector through your own server. Both are supported, and both have rules worth reading before you wire them in: see self-hosting and relaying. Do not modify the script itself: a changed meter is not a meter.
13. What the collector validates
Worth reading before you put anything between the browser and the collector, and before you conclude that an event which never appeared was lost in transit.
How an event finds your account
By origin, and only by origin. The collector reads the Origin request header, falls back to the origin field inside the event body when there is no header, and matches the result against the domains registered in API Management. No match means origin is not an approved redistribution domain, and nothing is stored or billed.
| Signal | Checked against your registered domains? | What it is used for |
|---|---|---|
Origin header | Yes, first choice | Identifies the account and the site row in your reporting |
origin field in the body | Yes, when no Origin header is present | The same, for events that reach us without a browser origin |
Referer header | No | Recorded alongside the event, never used to accept or reject it |
| Connecting IP address | No | Not matched against your domains at all. Used to derive the country shown in your reporting, and as one part of the per-visitor rate check |
Because the address is never checked, events are accepted from wherever you send them, including your own servers. What must always be right is the origin, and it must be one you registered.
How automated traffic is filtered
A public web page is crawled, prefetched, previewed, and scraped by clients you never chose to serve, so every event is classified before anything billable is written. Traffic classed as automated is kept in your reporting and left out of the billable count.
| Weighed | Not weighed |
|---|---|
The User-Agent: a missing one, a known crawler or preview fetcher, or a non-browser HTTP client such as curl, wget, or a server-side request library |
The connecting IP address. A small pool of server addresses is not penalised for being small, or for being a hosting range |
| The browser automation flag reported by headless and driver-controlled browsers | The Referer, the country, and the hosting provider |
| Prefetch and preview request headers, so a link preview never counts as a reader | How many distinct visitors share one address |
| An implausible burst of events carrying the same visitor and session id | Anything in the event body you chose, such as module names or the path |
The one that catches people out
The User-Agent is the strongest signal, and it is the one a server in the middle is most likely to replace with its own. If events arrive carrying your HTTP client's user agent instead of the visitor's, every real reader is classified as automated and none of them are billable, with no error and no rejected request. If you relay, forward the visitor's User-Agent. See the next section.
14. Self-hosting and relaying
Two independent changes, both supported. Host the script on your own domain to survive hostname-based blocking and to pin your own integrity hash. Relay the collector through your own server so the browser only ever talks to you. Do one, the other, or both.
Hosting the script yourself
Serve fxmacrodata-pixel.js from your own origin, byte for byte as published. You then own the URL and the hash, so you can version it and pin it however your policy requires, and you decide when to take an update. Do not modify the file: a changed meter is not a meter.
<script
async
src="https://example.com/assets/fxmacrodata-pixel.<version>.js"
integrity="sha384-<your hash of the file you host>"
crossorigin="anonymous"></script>
A self-hosted copy still sends to https://fxmacrodata.com unless you also relay, so keep connect-src https://fxmacrodata.com in your policy. Check for a newer build when you next review the page; the version each event carries as meter_version tells you what you are running.
Relaying the collector
Point the pixel at your own endpoint and forward each request to the collector from your server.
<script
async
src="https://example.com/assets/fxmacrodata-pixel.js"
data-fxmd-endpoint="https://example.com/fxmd-collect"></script>
POST https://example.com/fxmd-collect
-> POST https://fxmacrodata.com/fxmacrodata-pixel/v1/events
Relayed events reach us from your infrastructure rather than the visitor's browser. That is fine, and the address they arrive from is never held against you. What matters is that the signals the collector reads from the request survive the hop.
| What to forward | Why |
|---|---|
The request body, unchanged apart from path | It is the event. Send the JSON object the pixel produced, not a wrapper around it, and keep it under 16 KB. |
The visitor's User-Agent | Your HTTP client's own user agent classifies every reader as automated and silently zeroes your billable count. |
Origin unchanged, or no Origin at all | A header you set wins over the body. If you set one it must be a registered domain; if you set none, the body's origin field is used, and it is already correct. |
anonymous_visitor_id and anonymous_session_id, untouched | They are how one person is counted once. They also keep the rate check per visitor, so a shared relay address does not merge your readers into one bucket. |
What you may change on the way through
The path field, and we would rather you did. If your URLs carry record ids, account numbers, or anything else belonging to your customers, replace them before forwarding. path feeds the per-page breakdown in your reporting and nothing else: it never identifies a person, and it never affects what you are billed. A normalised path such as /reports/:id gives you a cleaner breakdown than one row per document, and keeps your identifiers on your side.
Leave the rest as the pixel produced it. The visitor and session ids, the origin, the module names, and the meter mode are all metering inputs, and editing them changes what you are billed.
One side effect to expect: the country on each event is derived from the address the request arrives from, so a relayed event is attributed to your hosting region. The geography column in your reporting will reflect that. It has no effect on measured users or on billing.
15. Verify and troubleshoot
Open a data page in a normal browser, then check the Network tab for a POST to /fxmacrodata-pixel/v1/events returning 200. Then open the redistribution panel in API Management: it shows registered surfaces, combined users, pixel status, and the estimated next invoice. Pixel status turns active after the first accepted event.
| Symptom | Cause | Fix |
|---|---|---|
origin is not an approved redistribution domain | The hostname is not registered, or you registered a different subdomain | Add the exact origin in API Management. Apex and www alias each other; nothing else does. |
| No network request at all | The script was not loaded, or a bundler rewrote the tag and dropped the attributes | Check the tag in view-source, not in your source file. Astro needs is:inline; GTM needs the createElement form. |
| Script loads, requests blocked | connect-src missing from your CSP | Add https://fxmacrodata.com to connect-src. |
| Events accepted, zero users counted | Exposure mode with no tagged components | Add data-fxmd-metered data-fxmd-module="..." to the data containers, or switch to page mode. |
| Module views never fire | Module name has uppercase, spaces, or is over 64 characters, or only one of the two attributes is present | Lowercase, a-z 0-9 _ . : -, both attributes on the same element. |
| Counted far higher than expected | Page mode in a global layout across a whole marketing site | Switch to exposure mode and tag, or load the tag only on data pages. |
| Some visitors classified as bots | Automated tests, headless browsers, prefetch or preview fetches, or a burst of events from one visitor | Expected. Bot traffic is never billed. Verify with a real browser. |
| Every visitor classified as a bot | A relay is sending its own HTTP client user agent instead of the visitor's | Forward the visitor's User-Agent through the relay. See self-hosting and relaying. |
| Relayed events rejected as an unapproved origin | The relay set an Origin header of its own, which takes priority over the origin in the body | Forward the browser's Origin unchanged, or send no Origin header and let the body carry it. |
| Attribution reported as not visible | The element is hidden, zero-size, fully transparent, or below the fold at event time | Put the credit where a reader can see it on the data screen. |
Related
- Commercial Redistribution setup - the licence, the billing meter, and the checklist.
- App and bot meter - for mobile apps, desktop apps, extensions, bots, and assistants.
- API reference and rate limits - for the data calls themselves.
- Commercial Redistribution Terms - server-side key requirement, no API mirroring, no marketplace resale.