Skip to content

Instantly share code, notes, and snippets.

@elado
Last active November 6, 2026 06:57
Show Gist options
  • Select an option

  • Save elado/69fa611d84305dea4b38801880743928 to your computer and use it in GitHub Desktop.

Select an option

Save elado/69fa611d84305dea4b38801880743928 to your computer and use it in GitHub Desktop.
American Express / Amex - Add all offers to card at once (bookmarklet)

American Express - Add all offers to card at once

If you own an AMEX card, you can add a bunch of offers to the card in this link: https://global.americanexpress.com/offers/eligible

There are many offers, and they change all the time. Instead of clicking "Add to card" repeatedly, I created this bookmarklet.

In Chrome, add a new bookmark (right click on bookmarks bar -> "Add Page...") with the following URL:

javascript:(()=>{let q=s=>[...document.querySelectorAll(s)],b=q('.offer-cta');if(!b.length)b=q('button,[role="button"]').filter(x=>{let l=(x.getAttribute('aria-label')||x.title||'').toLowerCase();return l.startsWith('add ')||l.includes(' add ')});if(!b.length)b=q('button').filter(x=>x.querySelector('svg')&&!x.textContent.trim()&&!['chat','search','menu'].some(w=>(x.getAttribute('aria-label')||x.title||'').toLowerCase().includes(w)));b=[...new Set(b)];if(!b.length)return alert('0 offers found.');console.log(`Found ${b.length} offers`);let c=()=>{let o=b.shift();if(!o)return alert('Finished adding offers!');o.scrollIntoView({behavior:'smooth',block:'center'});setTimeout(()=>{o.click();setTimeout(c,Math.random()*400+400)},300)};c();})();

Last updated: 10/27/26

Just run the bookmark while on the offers page, and watch the magic happens!

javascript:(()=>{let q=s=>[...document.querySelectorAll(s)],b=q('.offer-cta');if(!b.length)b=q('button,[role="button"]').filter(x=>{let l=(x.getAttribute('aria-label')||x.title||'').toLowerCase();return l.startsWith('add ')||l.includes(' add ')});if(!b.length)b=q('button').filter(x=>x.querySelector('svg')&&!x.textContent.trim()&&!['chat','search','menu'].some(w=>(x.getAttribute('aria-label')||x.title||'').toLowerCase().includes(w)));b=[...new Set(b)];if(!b.length)return alert('0 offers found.');console.log(`Found ${b.length} offers`);let c=()=>{let o=b.shift();if(!o)return alert('Finished adding offers!');o.scrollIntoView({behavior:'smooth',block:'center'});setTimeout(()=>{o.click();setTimeout(c,Math.random()*400+400)},300)};c();})();
javascript:(() => {
let btns = [...document.querySelectorAll('.offer-cta')];
if (btns.length === 0) {
btns = [...document.querySelectorAll('button, [role="button"]')].filter(b => {
let lbl = (b.getAttribute('aria-label') || b.title || "").toLowerCase();
return lbl.startsWith('add ') || lbl.includes(' add ');
});
}
if (btns.length === 0) {
btns = [...document.querySelectorAll('button')].filter(b =>
b.querySelector('svg') && !b.textContent.trim()
);
btns = btns.filter(b => {
let lbl = (b.getAttribute('aria-label') || b.title || "").toLowerCase();
return !lbl.includes('chat') && !lbl.includes('search') && !lbl.includes('menu');
});
}
btns = [...new Set(btns)];
console.log(`Found ${btns.length} offers to add.`);
if (btns.length === 0) {
alert('0 offers found. Amex may have deeply nested the buttons inside Shadow DOMs.');
return;
}
let c = () => {
let b = btns.shift();
if (!b) {
console.log('Finished adding offers!');
alert('Finished adding offers!');
return;
}
b.scrollIntoView({ behavior: 'smooth', block: 'center' });
setTimeout(() => {
b.click();
setTimeout(c, Math.random() * 400 + 400);
}, 300);
};
c();
})();
@allenwan

allenwan commented Sep 16, 2025 •

Copy link
Copy Markdown

Here's an update that worked for me as of 16SEP2025. It changed to an icon instead of text, so we're targeting something new.

javascript:btns=[...document.querySelectorAll('button[data-testid="merchantOfferListAddButton"]')];c = () => {b = btns.pop();if (!b) return console.log('added all!');b.click();setTimeout(c, Math.random() * 1500 + 300);};c();

The behavior is also different: it no longer auto-scrolls as it clicks the button so don't be alarmed if nothing happens when you run it.

@feiyax

feiyax commented Oct 1, 2025

Copy link
Copy Markdown

As of today, the button's title is add to list card. it works for me after changing it to that:

const buttons = document.querySelectorAll('button[title="add to list card"]'); 
// Loop through each button 
buttons.forEach((button, index) => { 
  setTimeout(() => { button.click(); }, index * 3000); 
});

@panpanbebe1023

Copy link
Copy Markdown

Does the new script add offers to multiple Amex cards?

@panpanbebe1023

Copy link
Copy Markdown

When I apply that script it only applies offers to one credit card? how to apply to all my Amex cards?

@parin95

parin95 commented Dec 2, 2025

Copy link
Copy Markdown

Any solutions to add offers across all cards?

@samwathegreat

Copy link
Copy Markdown

Any solutions to add offers across all cards?

When I apply that script it only applies offers to one credit card? how to apply to all my Amex cards?

You have to think logically about how this works. This is a script that takes advantage of the data on the page and in the simplest terms, simulates button pushes.

Since Amex lists offers on a per-card basis, what your asking for is impossible.

@panpanbebe1023

Copy link
Copy Markdown

Any solutions to add offers across all cards?

When I apply that script it only applies offers to one credit card? how to apply to all my Amex cards?

You have to think logically about how this works. This is a script that takes advantage of the data on the page and in the simplest terms, simulates button pushes.

Since Amex lists offers on a per-card basis, what your asking for is impossible.

Not really, the scripts used to work, you will be able to run the JavaScript and add offers to multiple cards when you are on the Amex Global home page. looks like Amex changed something and that script stop working.

@samwathegreat

samwathegreat commented Dec 2, 2025 via email

Copy link
Copy Markdown

@samwathegreat

samwathegreat commented Dec 4, 2025 •

Copy link
Copy Markdown

FYI, here is the one-liner for your bookmarks. Credit to @feiyax

javascript:btns=[...document.querySelectorAll('button[title="add to list card"]')];c = () => {b = btns.pop();if (!b) return console.log('added all!');b.click();setTimeout(c, Math.random() * 1500 + 300);};c();

Working as of today, 12/4/25

@quankhuc

quankhuc commented Apr 6, 2026 •

Copy link
Copy Markdown

Credit to the script above. I updated the script to their newest layout and easier to read for a dev :)

javascript:(() => {
  const clickNext = () => {
    const b = document.querySelector('button[data-testid="merchantOfferListAddButton"]');
    if (!b) return console.log("added all!");
    b.click();
    setTimeout(clickNext, 20);
  };
 
  clickNext();
})();

@ashleyconnor

Copy link
Copy Markdown

Very nice @quankhuc - works on Firefox 158+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment