** OR **
to attach an onclick handler to the link
(if you want to redirect to the link when the user clicks on it):
3. To only target links for specific programs, you can call them like this:
4. To ignore links, you can add `data-geniuslink-ignore` to the link:
Amazon
*/
!function(){"use strict";const IGNORE_ATTRIBUTES=["georiot-ignore","data-geniuslink-ignore"];
/**
* In HTML, any boolean attributes should be allowed to not have 'true', and
* even the presence an attribute (with no value) should be considered "true",
* yet for legacy reasons, we allow an explicit 'false' to be set.
*/function isIgnoredLink(link){return IGNORE_ATTRIBUTES.some(attribute=>link.hasAttribute(attribute)&&"false"!==link.getAttribute(attribute))}
// Check if we're in a browser environment
if("undefined"!=typeof window&&void 0===window.Georiot){window.Georiot=window.Georiot||{};var Georiot=window.Georiot;window.Genius=window.Genius||{};
// Performance optimization: Cache for link type detection
var linkTypeCache=new Map,REGEX_PATTERNS={apple:/^(https?:)?\/\/(search\.)?(itunes|search|books|music|apps|podcasts)\.apple\.com\/.*/i,tradeDoubler:/^(https?:)?\/\/clk[uk]*\.tradedoubler\.com\S*\?\S*url=[https%3A%2F%2F|http%3A%2F%2F]*itunes\.apple\.com/i,linkshareApple:/^(https?:)?\/\/click\.linksynergy\.com\S*?\S*RD_PARM1=[https%3A%2F%2F|https%253A%252F%252F|http%253A%252F%252F|http%3A%2F%2F]*itunes\.apple\.com/i,dgmPerf:/^(https?:)?\/\/t.dgm-au.c\S+\?+\S*u=[https%3A%2F%2F|http%3A%2F%2F|https%253A%252F%252F|http%253A%252F%252F]*itunes\.apple\.com/i,amazon:/^(https?:)?\/\/(www\.)?(?!amazon\.jobs)(amazon\.|amzn\.com|amzn\.co\.uk|amzn\.to|amzn\.eu|amzn\.asia|amzn\.in|a\.co(?![\w.])|smile\.amazon\.)/i,amazonLocal:/^(https?:)?\/\/local\.amazon\./i,google:/^(https?:)?\/\/google\.prf\.hn|^(https?:)?\/\/play\.google\.com/i,linkshareMSFT:/^(https?:)?\/\/click\.linksynergy\.com\S*?\S*murl=\S*microsoft\.com.*/i,microsoftStore:/^(https?:)?\/\/clk.*\.tradedoubler\.com\S*\?\S*url=[https%3A%2F%2F|http%3A%2F%2F].*microsoftstore\.com/i,microsoftStore2:/^(https?:)?\/\/(www\.)?microsoftstore\.com/i,microsoftOnline:/^(https?:)?\/\/clk.*\.tradedoubler\.com\S*\?\S*url=[https%3A%2F%2F|http%3A%2F%2F].*microsoft\.com.*/i,microsoftOnline2:/^(https?:)?\/\/(www\.)?microsoft\.com\/.*(store|p)\/.*/i,chewyPartnerize:/^(https?:)?\/\/(www|chewy\.)?prf\.hn\/click\/.*destination:.*chewy\.com/i,chewyRaw:/^(https?:)?\/\/(www\.)?chewy\.com/i,georiot:/^(https?:)?\/\/target\.georiot\.com|geni\.us/i};
// Pre-compiled regex patterns for better performance
/**
* Determines the type of affiliate link based on URL patterns
* @param {string} currentLinkHref - The URL to analyze
* @returns {string} Link type: "apple", "amazon", "google", "microsoft", "chewyPartnerize", "chewyRaw", "tradedoubler", "linkshareApple", "linkshareMSFT", "dgmperf", or "unknown"
*/
Georiot.getLinkType=function(currentLinkHref){
// Check cache first
return linkTypeCache.has(currentLinkHref)?linkTypeCache.get(currentLinkHref):
// Early exit for Georiot/Genius links
REGEX_PATTERNS.georiot.test(currentLinkHref)?(result="unknown",linkTypeCache.set(currentLinkHref,result),result):(
// Test patterns in order of most common to least common
result=REGEX_PATTERNS.apple.test(currentLinkHref)?"apple":REGEX_PATTERNS.tradeDoubler.test(currentLinkHref)?"tradedoubler":REGEX_PATTERNS.linkshareApple.test(currentLinkHref)?"linkshareApple":REGEX_PATTERNS.dgmPerf.test(currentLinkHref)?"dgmperf":REGEX_PATTERNS.amazon.test(currentLinkHref)&&!REGEX_PATTERNS.amazonLocal.test(currentLinkHref)?"amazon":REGEX_PATTERNS.google.test(currentLinkHref)?"google":REGEX_PATTERNS.linkshareMSFT.test(currentLinkHref)?"linkshareMSFT":REGEX_PATTERNS.microsoftStore.test(currentLinkHref)||REGEX_PATTERNS.microsoftStore2.test(currentLinkHref)||REGEX_PATTERNS.microsoftOnline.test(currentLinkHref)||REGEX_PATTERNS.microsoftOnline2.test(currentLinkHref)?"microsoft":REGEX_PATTERNS.chewyPartnerize.test(currentLinkHref)?"chewyPartnerize":REGEX_PATTERNS.chewyRaw.test(currentLinkHref)?"chewyRaw":"unknown",
// Cache the result
linkTypeCache.set(currentLinkHref,result),result);var result},
/**
* Clears the link type cache (useful for testing or when links change)
*/
Georiot.clearLinkTypeCache=function(){linkTypeCache.clear()},
/**
* Extracts the destination URL from various affiliate link formats
* @param {HTMLAnchorElement} currentLink - The link element to process
* @param {string} linkType - The type of affiliate link (from getLinkType)
* @returns {string|Object} For most link types: the destination URL. For chewyPartnerize: {destination, pubref}
*/
Georiot.extractLinkFromAffiliateUrl=function(currentLink,linkType){
// Handle Partnerize Chewy links (special case - not query params)
if("chewyPartnerize"===linkType){var parts=currentLink.href.split("/"),destination="",pubref="";for(var part of parts)part.startsWith("destination:")?destination=decodeURIComponent(part.replace("destination:","")):part.startsWith("pubref:")&&(pubref=part.replace("pubref:",""));return{destination:destination,pubref:pubref}}
// Use native URLSearchParams for query parameter parsing
var url=new URL(currentLink.href),params=new URLSearchParams(url.search);return"tradedoubler"===linkType?params.get("url")||"":"linkshareApple"===linkType?params.get("RD_PARM1")||"":"dgmperf"===linkType?params.get("u")||"":"linkshareMSFT"===linkType&¶ms.get("murl")||""},Georiot.baseDomain="buy.geni.us",
// Shared utility functions
/**
* Builds a Genius Link URL with optional parameters
* @param {number} tsid - The Genius Links TSID
* @param {string} destination - The destination URL to encode
* @param {Object} options - Configuration options
* @param {string} [options.domain] - Custom domain (defaults to Georiot.baseDomain)
* @param {string} [options.pubref] - Publisher reference for tracking
* @param {boolean} [options.passDtb] - Whether to include DTB parameter
* @param {boolean} [options.forOnClick] - Whether this is for onclick handlers (affects domain formatting)
* @returns {string} The complete Genius Link URL
*/
Georiot.buildGeniusLinkUrl=function(tsid,destination,options){var domain=options.domain||Georiot.baseDomain;
// Ensure domain is absolute with protocol
/^https?:\/\//.test(domain)||(domain=window.location.protocol+(domain.startsWith("//")?"":"//")+domain.replace(/^\/\//,""));var base=domain+"/Proxy.ashx?TSID="+tsid+"&GR_URL="+encodeURIComponent(destination);return options.pubref&&(base+="&track="+encodeURIComponent(options.pubref)),options.passDtb&&(base+="&dtb=1"),base},
/**
* Handles optional parameters with consistent defaults
* @param {string} [domain] - Custom domain
* @param {boolean} [passDtb] - Whether to pass DTB parameter
* @returns {Object} Object with domain and passDtb properties
*/
Georiot.handleOptionalParameters=function(domain,passDtb){return void 0===domain&&(domain=Georiot.baseDomain),void 0===passDtb&&(passDtb=!1),{domain:domain,passDtb:passDtb}},
/**
* Creates an onclick function that handles target="_blank" appropriately
* @param {HTMLAnchorElement} linkOnPage - The link element
* @param {string} destLink - The destination URL
* @returns {Function} The onclick handler function
*/
Georiot.createOnClickFunction=function(linkOnPage,destLink){return linkOnPage.setAttribute("data-geniuslink",destLink),function(){return"_blank"===linkOnPage.target?window.open(destLink,"_blank"):window.location.assign(destLink),!1}},
/**
* Core function that converts links to Genius Links
* @param {number} tsid - The Genius Links TSID
* @param {Array} types - Array of link types to process
* @param {boolean} passDtb - Whether to include DTB parameter
* @param {string} domain - Domain for Genius Links
* @param {boolean} useOnClick - Whether to add onclick handlers or change href
* @param {Function} [specialProcessingForLinkTypes] - Optional callback for special link processing
*/
Georiot.baseConvertLinks=function(tsid,types,passDtb,domain,useOnClick,specialProcessingForLinkTypes){
// Input validation
if(tsid&&"number"==typeof tsid)if(Array.isArray(types))if("boolean"==typeof useOnClick){if(!Georiot.utility.isThrive()){
// Handle optional parameters
var params=Georiot.handleOptionalParameters(domain,passDtb);domain=params.domain,passDtb=params.passDtb;
// Batch process all links in a single iteration
for(
// Cache document.links to avoid repeated DOM queries
var links=document.links,numberOfLinks=links.length,currentLinkIndex=0;currentLinkIndex-1}}},
/**
* Factory function to create link conversion modules
* @param {string} moduleName - Name of the module
* @param {Array} linkTypes - Array of link types to process
* @param {Function} [specialProcessing] - Optional special processing function
* @returns {Object} Module with convertLinks, convertToGeoRiotLinks, and addOnClickRedirect methods
*/
Georiot.createLinkModule=function(moduleName,linkTypes,specialProcessing){return{
/**
* Converts links to Genius Links
* @param {number} tsid - The Genius Links TSID
* @param {boolean} [passDtb] - Whether to include DTB parameter
* @param {string} [domain] - Custom domain
*/
convertLinks:function(tsid,passDtb,domain){this.convertToGeoRiotLinks(tsid,passDtb,domain)},
/**
* Converts links to Genius Links (direct href modification)
* @param {number} tsid - The Genius Links TSID
* @param {boolean} [passDtb] - Whether to include DTB parameter
* @param {string} [domain] - Custom domain
*/
convertToGeoRiotLinks:function(tsid,passDtb,domain){Georiot.baseConvertLinks(tsid,linkTypes,passDtb,domain,!1,specialProcessing)},
/**
* Adds onclick handlers to links
* @param {number} tsid - The Genius Links TSID
* @param {boolean} [passDtb] - Whether to include DTB parameter
* @param {string} [domain] - Custom domain
*/
addOnClickRedirect:function(tsid,passDtb,domain){Georiot.baseConvertLinks(tsid,linkTypes,passDtb,domain,!0,specialProcessing)}}};
// --- Batching logic for link conversion ---
var batchQueue={convert:{},onclick:{},tsid:null,domain:null,passDtb:null,scheduled:!1};function scheduleBatchProcessing(){batchQueue.scheduled||(batchQueue.scheduled=!0,
/**
* We set a timeout so that we wait for a moment to see if multiple
* calls to convertLinks() or addOnClickRedirect() are made. Then we
* can batch them together to only scan links once.
*/
setTimeout(()=>{
/**
* Make sure the document is loaded before running the batch processing
*
* @todo - It's perfectly feasible to "patch" links "live" as links get
* dynamically added. We should add a mutation observer if this would
* be valuable to people.
*/
requestAnimationFrame(Georiot.runBatchProcessing)},0))}
// Update runBatchProcessing to handle special processing for legacy types
function runBatchProcessing(){var configConvert=Object.assign({},batchQueue.convert),configOnClick=Object.assign({},batchQueue.onclick),tsid=batchQueue.tsid,domain=batchQueue.domain,passDtb=batchQueue.passDtb;if(batchQueue.convert={},batchQueue.onclick={},batchQueue.tsid=null,batchQueue.domain=null,batchQueue.passDtb=null,batchQueue.scheduled=!1,tsid)for(var decoded,proxylink,links=document.links,numberOfLinks=links.length,i=0;iqueueBatch("convert",type,tsid,passDtb,domain,!1))},addOnClickRedirect:function(tsid,passDtb,domain){types.forEach(type=>queueBatch("onclick",type,tsid,passDtb,domain,!0))}}}Georiot.amazon=makeBatchedModule("amazon"),Georiot.itunes=makeBatchedModule("itunes"),Georiot.google=makeBatchedModule("google"),Georiot.microsoft=makeBatchedModule("microsoft"),Georiot.chewy=makeBatchedModule("chewy");
// Patch the snippet module to batch all types
var SNIPPET_TYPES=["apple","amazon","google","microsoft","linkshareApple","linkshareMSFT","tradedoubler","dgmperf","chewyRaw","chewyPartnerize"];Georiot.snippet={convertLinks:function(tsid,passDtb,domain){SNIPPET_TYPES.forEach(type=>{queueBatch("convert",type,tsid,passDtb,domain,!1)})},addOnClickRedirect:function(tsid,passDtb,domain){SNIPPET_TYPES.forEach(type=>{queueBatch("onclick",type,tsid,passDtb,domain,!0)})}},Georiot.convertLinks=Georiot.snippet.convertLinks,Georiot.addOnClickRedirect=Georiot.snippet.addOnClickRedirect,
// Public API - Expose Georiot and set Genius to point to it
window.Georiot=Georiot,window.Genius=Georiot,Georiot.runBatchProcessing=runBatchProcessing,Georiot._batchQueue=batchQueue}
// The shield to avoid running the code twice
}();
It’s not technically a tax. Taxes produce valuable public benefits, like medical research and parks. This is simply legal theft.
Amazon makes nearly a billion dollars in profit from search ads. Every week. Each week, they sell merchants and publishers enough search-distorting ads to capture a billion dollars in revenue. Amazon makes enough in search ad revenue to give every single one of their employees a $35,000 cash bonus and still have change left over.
My publisher is terrific, and they’re working hard to introduce people to my new book. Last week, they began buying search ads on Amazon.
At first glance, this is compelling. Someone who isn’t sure what they’re looking for, who is looking for a book or a kitchen appliance, might find one if the right ad showed up at the right time.
But of course, that’s not what yields, or what most of the ads you see on Amazon do.
If you’re searching for an air fryer, Amazon already knows quite a bit. They know the best-reviewed, least-returned, best-priced model. The only purpose of the ads is to get you to pick an air fryer that isn’t that one (or for the best air fryer, to keep you on track to buy the one you wanted in the first place). The ads make the search worse. [Cory wrote about this three years ago, and the scale has already doubled.]
When there are plenty of ads, the maker of the best air fryer now has to bid on ads as well, if only to protect the sales they were entitled to in the first place. Businesses continue to buy the ads—not because they’re dumb, but because the system has created a situation with few options. Folklore implies that buying the ads somehow shifts how search responds in the long run, even after the ads stop running, but there’s little data to confirm this.
Traditional ads increase demand. We see something that’s clearly an ad, it might spark desire, and sales go up. But zero-sum search ads aren’t like that–the total sales in the category stay the same, and merchants are merely competing for a share of a static pie. This study argues that an ecommerce site with search ads actually sells fewer items than the same site without ads.
The highest-yielding ad my publisher has tested so far is the search “Seth Godin The Knot“. It costs about a dollar per click. My publisher is paying Amazon a dollar to show you an ad for the book you went to buy in the first place.
Who ends up paying the more than $50 billion a year spent on these ads? It’s not the sellers. Sellers can’t make heartfelt donations for long. It’s you. By making the marketing of products significantly less efficient, Amazon’s theft makes products more expensive or sucks the energy out of the development of new products.
It leads to two perverse side effects. First, producers realize that if brand reputation matters less than a budget for clicks, they will shift to shoddy and cheap versions of their products so they have a bigger budget for clicks. And second, Amazon (and Google before it) have an incentive to make their organic search results worse–giving producers more incentive to buy more ads.
For decades, Amazon created value for consumers by lowering the price of just about everything. And they opened the doors to merchants who didn’t have sufficient distribution. They claimed to be customer-centric, and they were.
I don’t think they can claim this any longer. The ad system they built isn’t illegal, but it’s pretty clear who it’s for.
Amazon is stealing from the customers they said they were here to serve.
August 18, 2026
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here:
Cookie Policy