The site script
One tag before the closing body tag, and every outbound link on your site becomes a paid link. This page is what it does, line for line, including the parts it deliberately leaves alone.
On this page
What it does
One tag, pasted once. It looks through the page for links that point away from your site, asks Montvo for a short link for each one, and swaps the address in. A visitor who clicks goes through a Montvo ad page and then on to the destination you linked, and the unlock is credited to your account.
That is the whole of it. What the script does not do is worth being just as plain about:
- It reads no cookies and sets none.
- It sends no analytics, and never tells us which page a visitor is reading.
- It never touches a link that points at one of your own pages.
- It never rewrites a link twice, and never rewrites a link that already points at
montvo.link. - It changes the href and nothing else — no wrapper, no interstitial of its own, no click handler.
- It is one file of 4.7 KB, plain enough to run on whatever browser a visitor happens to have, and cached for an hour in the browser and a day at the edge.
Install
- 1
Get your site key. It is on the Developers screen of your dashboard, and it starts
pk_live_. There is one per account. - 2
Paste the tag once, before the closing
</body>of every page you want covered. In a templated site that is one edit to the layout.layout.html<!-- Once, before </body> --><script src="https://montvo.com/m.js" data-key="pk_live_9f2c4b8a" data-exclude="shop.mysite.com, mysite.dev" defer></script>Use your site key (
pk_live_…). Made to be public: it goes into your page and can only create links.Both are on Developers in the dashboard · Which key when
- 3
Load a page and hover an outbound link. The status bar should read
montvo.link/…instead of the original address.
Why defer
defer is what keeps the tag off the critical path: the browser fetches it alongside the page and runs it once the document is parsed. Without it, a slow response from us would hold up your page, which is a poor trade for a script whose entire job happens after the links exist.The attributes
srcrequired- Always
https://montvo.com/m.js. Served from the app rather than a separate CDN host, so it carries the right API address whatever we deploy. Cached for an hour in the browser and a day at the edge. data-keyrequired- Your site key. Without it the script stops immediately and the page is left exactly as it was. See about the site key for what it can and cannot do.
data-excludecomma-separated hosts- Other domains you own, which are to be left alone. The page's own hostname is always excluded, so this is only for the rest: a shop on another domain, a staging host, a docs subdomain. A leading
www.is ignored on both sides, and matching is exact —mysite.comdoes not covershop.mysite.com. data-subyour reference for this reader- Put on every link the script writes, so you can check afterwards that a particular visitor finished — see verifying an unlock. Fill it from whatever renders the page. Left out, nothing is added and the links are exactly as they were.
deferrecommended- Runs the script after the document is parsed instead of blocking the parser where the tag sits.
Which links it rewrites
A link is rewritten when all of these hold:
- It has an
href, and the protocol ishttp:orhttps:. - Its hostname is not the page's own, and is not listed in
data-exclude. - Its hostname is not
montvo.comormontvo.link, or a subdomain of either. - It does not carry
data-montvo="off", and has not already been rewritten.
Everything else is left untouched, including anchors to the same page, mailto: and tel: links, javascript: hrefs, and any link with no href at all.
Leaving one link alone
Add data-montvo="off" to any anchor the script should skip — an affiliate link that must keep its own tracking, a sponsor who paid for a direct click, a download you would rather not put a wait in front of.
<!-- Rewritten: it points off your site. --><a href="https://example.com/download">Download</a> <!-- Left alone: you asked. --><a href="https://sponsor.example.com" data-montvo="off">Our sponsor</a> <!-- Left alone: not http(s). --><a href="mailto:[email protected]">Email us</a>What it leaves on the page
A rewritten anchor keeps the address it started with, so nothing is lost and the script can tell at a glance what it has already handled:
<a href="https://montvo.link/105010" data-montvo-done="1" data-montvo-href="https://example.com/download">Download</a>data-montvo-done"1"- Marks the anchor as handled. The script will not look at it again.
data-montvo-hrefthe original- The address the anchor had before the swap, unchanged.
Which makes putting a page back the way it was a one-liner, if you ever need to:
for (const a of document.querySelectorAll("a[data-montvo-href]")) { a.href = a.dataset.montvoHref;}When it runs
The first pass happens as soon as the document is parsed — on DOMContentLoaded, or immediately if the document is already past that by the time the script loads.
After that it watches the document for new links and scans again, so a feed that loads more posts, a router that swaps a view, or a lightbox that builds its markup on open are all covered. The watch is debounced by 400 milliseconds rather than firing on every mutation: a page that changes constantly — a text editor, a live chat — must not pay for this on every keystroke.
Batching, and the session cache
Addresses are asked about in one request, not one request each. A page with more than 50 outbound links gets the first 50 on the first pass and the rest on the next — the endpoint refuses a longer list rather than truncating it, so the script never sends one. Only one request is ever in flight at a time.
Every answer is kept in sessionStorage, under the key montvo:<your site key>. A visitor reading five pages of your site asks about a shared footer link once rather than five times. It is session storage rather than local storage deliberately: it belongs to that one tab and goes when the tab does, so nothing of ours outlives the visit.
If storage is unavailable
The links it makes
Links the script creates land in your dashboard alongside the ones you make by hand, with two things set so you can tell them apart: their kind is script, and their source is Website.
- A destination you have already linked is reused. The endpoint looks for an existing link of yours before it makes a new one, so a footer link on two thousand pages is one row in your list, not two thousand.
- Addresses are normalized and deduplicated. The same destination written two ways on one page becomes one link.
- The level is your account default — balanced unless you have changed it, which you can do under Monetization level in Settings. You can raise or lower it per link afterwards like any other.
- The slug is derived from the destination. Six characters, the same six every time for the same address. In the rare case that slug is already taken, that one address is skipped and the anchor keeps the href it had.
The script can create links
data-exclude, or mark it data-montvo="off", before the tag goes live.About the site key
The site key sits in a public page, so anybody who views source has it. That is not a leak to be plugged — it is inherent to a tag pasted into HTML, and there is no secret to keep on a page everyone can read. What matters is how little it opens.
| A site key can | A site key cannot |
|---|---|
| Create links on your account, through one endpoint. | Read anything — not your links, not your stats, not your balance. |
| Be called from any origin, which is the point of a tag on your page. | Reach any other part of the API. Every other endpoint answers it the same way it answers a key that does not exist. |
| Spend its own allowance of 3,000 calls a minute. | Touch a payout, a wallet address, or an account setting. |
The worst a stranger who copies your key can do is make links on your account — which pay you, not them. If you want it changed anyway, the Developers screen mints a new one.
Rotating replaces immediately
Knowing who finished
If you run your own flow around these links — a step list, a reward, a download released once — you will want to know that a particular reader went through the gate rather than that somebody did. Add data-sub to the tag with your own reference for whoever is reading, and every link the script writes carries it:
<script src="https://montvo.com/m.js" data-key="pk_live_9f2c4b8a" data-exclude="shop.mysite.com, mysite.dev" data-sub="{{ current_user.id }}" defer></script>Use your site key (pk_live_…). Made to be public: it goes into your page and can only create links.
Both are on Developers in the dashboard · Which key when
They then arrive at your destination carrying a token your server can check with your secret key. The whole of it — what the token says, why a reward should read billable rather than completed, and how to make sure it is only collected once — is on verifying an unlock.
When something fails
The script is built to be the least important thing on your page. Every failure path ends the same way: the anchor keeps the address the author wrote, and the visitor goes where they were always going.
- No key on the tag, or a key we do not recognize: nothing is rewritten.
- The request fails, times out, or the visitor is offline: nothing is rewritten, and it tries again on the next page.
- An address comes back unshortened: that one anchor is left as it was, and the rest still swap.
- Storage is blocked: the cache is skipped, the swapping still happens.
The full list of what the endpoint can answer is on the endpoint reference.
If nothing changed
Work down this list before anything else — it is almost always one of the first three.
| Check | What to look for |
|---|---|
| The tag is on the page | View source and search for m.js. A tag inside a template that never rendered is the commonest cause by a distance. |
data-key is filled in | An empty attribute stops the script before it does anything. A placeholder left in from a copy-paste — pk_live_…, ellipsis and all — is not a key either: the call comes back 401 and nothing is rewritten. |
| The links are actually outbound | A link to your own domain is skipped by design, and so is anything on your data-exclude list. Check the list for a stale entry. |
| The request went out | The network panel should show one POST to https://montvo.com/api/v1/site/links. Its status tells you the rest; the codes are on the endpoint reference. |
| The link was added late | Links inserted after load are picked up within about half a second. If a link is never picked up, check it is a real <a href> and not a div with a click handler — the script only touches anchors. |
| Your dashboard | Links the script created show up under Links with source Website. If they are there but the page still shows old addresses, you are looking at a cached copy of the page. |