泥潭日报 uscardforum · 每日精选

[微技术贴]用chrome查看Amex卡的消费奖励类别统计

内容摘要

利用 Amex API 抓取多卡消费奖励类别与剩余额度统计。

1. 关键信息

  • 需使用 Chrome 桌面版,打开目标卡主页,展开下拉框,选中目标卡。
  • 需通过 F12 → Console 粘贴并运行指定 JavaScript 代码。
  • 结果在新标签页以表格形式显示,包含 category、Points、Cash、remainingAmount 等。
  • 默认查询当年 YTD;可修改 yr、queryStart、queryEnd 查询指定日期(最大一年范围)。
  • 支持显示使用统计数据日期;新版/旧版界面需切换代码版本。
  • token 获取方式可能因 UI/A-B 测试变化;若失败可尝试从 window.__INITIAL_STATE__ 或 network beacon 定位。
  • 部分联名卡/COBAND 卡(如 Hilton Aspire、Surpass、BBP)不支持。

2. 羊毛/优惠信息

  • 可统计各类奖励(Bonus、Refer、ADJUSTMENT、INDUSTRY_CATEGORY)。
  • 可查看剩余额度(remainingAmount),便于管理超市/类别额度。
  • 可估算 Refer 使用数量(通过 Bonus 变化)。
  • 无具体卡种 SUB、报销、积分兑换比例、折扣码、限时活动、酒店/航司奖励兑换、银行开户奖励信息。

3. 最新动态

  • 代码持续更新以适配界面变更(#42、#49、#57)。
  • API 曾短暂失效后恢复(#47、#48)。
  • 查询范围仍限于一年内,与 Reward Activity 范围一致(#44)。

4. 争议或不同意见

  • 界面版本差异导致 token 获取失败(#27、#31、#62)。
  • 部分用户误选“所有账户”导致无法抓取(#65)。
  • 新版 Tiles 界面与旧版 List 界面兼容问题(#62、#67)。

5. 行动建议

  • 仅限 Amex 主卡使用,联名卡/COBAND 卡请换用旧版或放弃。
  • 遇到错误优先检查选卡状态与界面版本,必要时从历史记录恢复旧版。
  • 如遇 API 失效,可等待 Amex 更新样本后再尝试。
原始内容
--- 第 1 楼来自 Youshen 的回复 (2023-03-02 18:28:32 PST) ---

受到旁边楼启发,略微研究了一下amex的API后发现可以手动抓取各种卡的消费类别统计,包括bonus奖励(大部分来自开卡,副卡,refer)

最近发现通过奖励项目的变化时间能知道账号升降机准确生效日期

使用说明

以PC版chrome为基础

登录amex网页并切换至想要统计的卡,如果账号里只有一张卡则可跳过
(再)点击一次切换卡的按钮,会弹出下拉框列表,卡多的账号还需要点击View All以显示全部卡。注意,此下拉框必须保持显示可见状态直至代码运行结束
按F12调出开发者工具,切换至console页面
粘贴代码并按回车
结果将在新的浏览器页面显示

备注1,统计结果默认为calendar year to date。举例如果改到2022,需要把首行定义为yr = 2022

Update 9/13/25

即使amex界面大改版,代码依然没有无需改动。但amex限制了历史查询时间,现在无法超过一年(跟reward activity查询范围一样)

Update 11/28/24

代码没有改动, 只是突然发现希尔顿和万豪的卡也能用这个api了

Update 9/27/24

新版本只适用于新amex界面,还是旧界面的用历史记录的版本

Update 4/5/24

更新匹配amex新的接口要求

Update 7/9/23

改进代码逻辑

Update 3/16/23

在报告页面增加显示使用统计数据的日期

Update 3/15/23

发现通过css-r4d5p3来获取token只适用于含有副卡的账户。改进后代码会先尝试使用该字段,若找不到则会重新读取完整页面来匹配token。
增加了fetch请求的次数,来应对某些时候无返回或超时的情况。
改进统计起始和结束日期的输入。

代码在此
yr = new Date().getFullYear();
queryStart = yr + "-01-01";
queryEnd = yr + "-12-31";

// read current active card token
cls = '[aria-controls="simple-switcher-listbox"]';
target = document.querySelector(cls);

img = target.getElementsByTagName('img')[0];
cardnum = target.querySelector('[data-testid="simple_switcher_display_number_val"]')
name = img.getAttribute('alt') + ' (' + cardnum.innerHTML + ')';
const run = +(target.getAttribute('aria-expanded')=='true');

if (run) {
token = target.getAttribute('aria-activedescendant');
token = token.slice(token.lastIndexOf('-')+1);
} else {
console.log('Please expand card switcher list before running');
}

//get card product data
for (var i = 0; i < 3*run; ++i) {
try {
cp = await fetch("https://functions.americanexpress.com/ReadLoyaltyBenefitsCardProduct.v1", {
"body": JSON.stringify({
"accountTokens":[token],
"cardNames":[],
"productType":"AEXP_CARD_ACCOUNT"}),
"method": "POST",
"mode": "cors",
"credentials": "include",
"headers": {
"accept": "application/json",
"content-type": "application/json",
"one-data-correlation-id": (Math.random() + 1).toString(36).substring(2)
},
}).then(res => res.json());
break;
} catch(err) {
setTimeout(() => { console.log(err.message); }, 5000);
}
}
try {
cardName = cp.cardDetails[0].cardName;
} catch(err) {
cardName = '';
}

// request reward summary for startDate to endDate, default ytd
for (var i = 0; i < 3*run; ++i) {
try {
res = await fetch("https://functions.americanexpress.com/ReadLoyaltyTransactionSummaries.v1", {
"body": JSON.stringify({
"accountToken":token,
"productType":"AEXP_CARD_ACCOUNT",
"startDate":queryStart,
"endDate":queryEnd,
"periodType":"CALENDAR_PERIOD",
"category":["REWARD"],
"summariesBy":["category","transactionType"],
"transactionType":["INDUSTRY_CATEGORY","BONUS","ADJUSTMENT"],
"includeSuppCards":true,
"cardProductName":cardName,
"summariesFor":"CARD_NUMBER"}),
"method": "POST",
"mode": "cors",
"credentials": "include",
"headers": {
"accept": "application/json",
"content-type": "application/json",
"one-data-correlation-id": (Math.random() + 1).toString(36).substring(2) }
}).then(res => res.json());
break;
} catch(err) {
setTimeout(() => { console.log(err.message); }, 5000);
}
}

// display in new tab
function display(res, img, name) {
if (!('summary' in res)) {
console.log(res.error);
console.log(name);
console.log(token);
return;
}
if (res.status['code'] != '0000') {
console.log(res.status);
console.log(name);
console.log(token);
return;
}
tab = window.open('about:blank', '_blank');
_table_ = tab.document.createElement('table'),
_tr_ = tab.document.createElement('tr'),
_th_ = tab.document.createElement('th'),
_td_ = tab.document.createElement('td');

_table_.style.border = '1px solid black';
_th_.style.border = '1px solid black';
_td_.style.border = '1px solid black';

// Builds the HTML Table out of myList json data from Ivy restful service.
function buildHtmlTable(arr) {
var table = _table_.cloneNode(false),
columns = addAllColumnHeaders(arr, table);
for (var i=0, maxi=arr.length; i < maxi; ++i) {
var tr = _tr_.cloneNode(false);
for (var j=0, maxj=columns.length; j < maxj ; ++j) {
var td = _td_.cloneNode(false);
cellValue = arr[i][columns[j]];
td.appendChild(document.createTextNode(arr[i][columns[j]] || ''));
tr.appendChild(td);
}
table.appendChild(tr);
}
return table;
}

// Adds a header row to the table and returns the set of columns.
// Need to do union of keys from all records as some records may not contain
// all records
function addAllColumnHeaders(arr, table)
{
var columnSet = [],
tr = _tr_.cloneNode(false);
for (var i=0, l=arr.length; i < l; i++) {
for (var key in arr[i]) {
if (arr[i].hasOwnProperty(key) && columnSet.indexOf(key)===-1) {
columnSet.push(key);
var th = _th_.cloneNode(false);
th.appendChild(document.createTextNode(key));
tr.appendChild(th);
}
}
}
table.appendChild(tr);
return columnSet;
}

data = JSON.parse(JSON.stringify(res.summary));
// modify data for better display
for (i = 0;i < data.length; i++) {
delete data[i]['benefitId'];
delete data[i]['summariesBy'];
delete data[i]['hasTracker'];
delete data[i]['totalAmount'];
if ('pointCount' in data[i]['summariesTotal'][0]) {
data[i]['Points'] = data[i]['summariesTotal'][0]['pointCount'];
}
if ('cash' in data[i]['summariesTotal'][0]) {
data[i]['Cash'] = data[i]['summariesTotal'][0]['cash']['amount'];
}
delete data[i]['summariesTotal'];
if ('tracker' in data[i]) {
data[i]['remainingAmount'] = data[i]['tracker']['remainingAmount']['value'];
}
delete data[i]['tracker'];

}

tab.document.body.append(name);
tab.document.body.appendChild(tab.document.createElement('br'));
tab.document.body.appendChild(tab.document.createElement('br'));
_img_ = tab.document.createElement('img');
_img_.setAttribute('class', img['alt']);
_img_.setAttribute('src', img['src']);
_img_.setAttribute('width', 300);
tab.document.body.appendChild(_img_);
tab.document.body.appendChild(tab.document.createElement('br'));
tab.document.body.appendChild(tab.document.createElement('br'));
tab.document.body.append('Summary from ' + res['period']['startDate'] + ' to ' + res['period']['endDate']);
tab.document.body.appendChild(buildHtmlTable(data));
tab.document.title = name;
}

if (run) display(res, img, name);

效果图集

百夫长
【引用自 Ditto】:
Screen Shot 2023-03-03 at 12.52.59 AM1920×610 98.5 KB
个人金

普金946×385 94.6 KB

玫瑰金
【引用自 RoyWright】:
image1631×388 159 KB
商金

商金1338×638 234 KB

商白

商白1240×410 117 KB

BBP

BBP1053×340 74.9 KB

BBC

bbc752×341 59.9 KB

ED

ed1065×338 60.5 KB

BCP

BCP1169×409 102 KB

--- 第 2 楼来自 RoyWright 的回复 (2023-03-02 20:06:17 PST) ---

钛金预定吧

--- 第 3 楼来自 交大梁朝伟 的回复 (2023-03-02 20:08:28 PST) ---

好强zs

--- 第 4 楼来自 KaitouKiddo 的回复 (2023-03-02 20:15:28 PST) ---

这个很不戳啊,改成油猴脚本。

--- 第 5 楼来自 TrashPanda 的回复 (2023-03-02 20:18:20 PST) ---

不會編碼是不是就是21世紀的文盲 完了

--- 第 6 楼来自 KaitouKiddo 的回复 (2023-03-02 20:19:36 PST) ---

文盲多好,活得潇洒没有负担

--- 第 7 楼来自 miracle 的回复 (2023-03-02 20:25:02 PST) ---

留名zszs

--- 第 8 楼来自 zhhy 的回复 (2023-03-02 20:30:39 PST) ---

好家伙。。火钳刘明

--- 第 9 楼来自 twjeric 的回复 (2023-03-02 20:55:22 PST) ---

很强,不过要怎么使用这结果?

--- 第 11 楼来自 zxrxhzmn 的回复 (2023-03-02 21:02:30 PST) ---

好像很硬核

能再简单描述下使用场景吗?

--- 第 12 楼来自 Chickenrice 的回复 (2023-03-02 21:07:43 PST) ---

卷起来了

--- 第 13 楼来自 a885822 的回复 (2023-03-02 21:22:21 PST) ---

赞赞赞

--- 第 15 楼来自 JPMorgan 的回复 (2023-03-02 21:53:42 PST) ---

火钳刘明

--- 第 16 楼来自 Ditto 的回复 (2023-03-02 21:57:06 PST) ---

Screen Shot 2023-03-03 at 12.52.59 AM1920×610 98.5 KB

--- 第 17 楼来自 alanzhaoxvi 的回复 (2023-03-02 22:17:45 PST) ---

得一批

--- 第 18 楼来自 JimmyG 的回复 (2023-03-02 22:25:48 PST) ---

只有联名卡

--- 第 19 楼来自 EDC 的回复 (2023-03-02 22:34:35 PST) ---

另一个钛金限定?

--- 第 20 楼来自 EDC 的回复 (2023-03-02 22:35:39 PST) ---

不过这个抓出来和官网上写的不是一样的么?

--- 第 21 楼来自 Falanta 的回复 (2023-03-02 22:43:25 PST) ---

嚯 整挺好

--- 第 22 楼来自 ShadyN3mo 的回复 (2023-03-02 22:53:46 PST) ---

火钳刘明

--- 第 23 楼来自 Nongnong 的回复 (2023-03-02 23:14:02 PST) ---

泥潭果然人才济济

--- 第 24 楼来自 Youshen 的回复 (2023-03-03 07:19:18 PST) ---

【引用自 twjeric】:
很强,不过要怎么使用这结果?
【引用自 zxrxhzmn】:
能再简单描述下使用场景吗?
很多时候东西先做出来,用途再慢慢发掘。

比如,看bonus可以估计本年度refer用了多少。又如BCP超市,有显示remainingAmount,方便查看本年度剩余额度
【引用自 EDC】:
不过这个抓出来和官网上写的不是一样的么?
如果官网有显示是一样的,但官网很多卡都没有

--- 第 25 楼来自 Youshen 的回复 (2023-03-03 07:21:56 PST) ---

听说过没用过,有需要下次再学习一下

--- 第 26 楼来自 RoyWright 的回复 (2023-03-03 09:01:21 PST) ---

image1631×388 159 KB

简单快捷

--- 第 27 楼来自 xh91 的回复 (2023-03-03 09:15:29 PST) ---

mac chrome 不work? css-r4d5p3找不到

--- 第 28 楼来自 eleboson 的回复 (2023-03-03 09:41:59 PST) ---

css-r4d5p3 已经没了?

--- 第 29 楼来自 Youshen 的回复 (2023-03-03 09:51:09 PST) ---

这个有点意思,玫瑰金卡的返回里面有超市年度25000统计,但是普通金的却没有显示。

--- 第 30 楼来自 Youshen 的回复 (2023-03-03 09:53:20 PST) ---

【引用自 xh91】:
mac chrome 不work?
这就不清楚了,我没有mac可以测试
【引用自 eleboson】:
css-r4d5p3 已经没了?
或许是忘记开下拉框?或者像上面一样是mac?

--- 第 31 楼来自 eleboson 的回复 (2023-03-03 10:08:55 PST) ---

可能我卡多于4张?

product-row很容易找到,name和image都在(显然的,因为网页上显示着呢),但是没有任何长得像token的东西。试了一下click以后看network但是看不出是怎么传token的。

image1522×1117 76 KB

--- 第 32 楼来自 Youshen 的回复 (2023-03-03 10:30:45 PST) ---

你的div顺序跟我的不一样,我的是role list \ css-bcbh4b \ product-row。你也是pc的chrome吗?我也不太懂为啥网页生成会不一样。

不过关于token,手动找可以看

window.__INITIAL_STATE__,或者看xhr里面经常出现的beacon的payload数据

--- 第 33 楼来自 Youshen 的回复 (2023-03-03 10:36:08 PST) ---

另外你看的应该不会是副卡吧?副卡没有token

--- 第 34 楼来自 eleboson 的回复 (2023-03-03 10:41:18 PST) ---

是PC chrome,主卡。可能不同用户就是会有不同UI的A/B test?

但是确实在beacon里(request里,不是response)找到token了,效果不错

image1636×238 27.4 KB

--- 第 35 楼来自 Youshen 的回复 (2023-03-03 10:48:01 PST) ---

【引用自 eleboson】:
可能不同用户就是会有不同UI的A/B test?
我换了几个号后发现确实有你那个顺序的,但是依然有显示css-r4d5p3,原因不明。小破工具凑合着用

--- 第 37 楼来自 Youshen 的回复 (2023-03-07 06:48:14 PST) ---

突然想起其实可以试试chatgpt,手动就基本上分步骤google

--- 第 38 楼来自 DELTA 的回复 (2023-03-08 11:47:36 PST) ---

2012年黑卡大佬

--- 第 39 楼来自 MasterCard 的回复 (2023-03-14 21:49:19 PDT) ---

Blue Cash Everyday 不work,直接粘代码显示undefined (找不到aria-labelledby)

我自己把token粘贴进去,出来BCE的图,但是下边数据还是Platinum的

image1542×722 236 KB

--- 第 40 楼来自 Reaper-17 的回复 (2023-03-15 01:20:01 PDT) ---

代码用不了,求教这个报错是啥原因?

--- 第 41 楼来自 SatoriKomeiji 的回复 (2023-03-15 01:36:25 PDT) ---

同样遇到这个问题

Uncaught TypeError: Cannot read properties of undefined (reading ‘getAttribute’)

at :7:55

--- 第 42 楼来自 Youshen 的回复 (2023-03-15 11:56:27 PDT) ---

刚申的卡也碰到了同样的问题,于是改进了代码逻辑,这个问题应该是修好了,有空帮忙试用一下哈

@eleboson @MasterCard @Reaper-17 @SatoriKomeiji

--- 第 43 楼来自 eleboson 的回复 (2023-03-15 12:05:48 PDT) ---

可以了!

--- 第 44 楼来自 jaxx 的回复 (2023-03-15 12:18:10 PDT) ---

大佬牛 想问一下有办法查看去年 而非calendar ytd的结果吗

--- 第 45 楼来自 Youshen 的回复 (2023-03-15 12:53:41 PDT) ---

可以,已经更新。你正好提醒了我要修一下日期的bug

--- 第 46 楼来自 MasterCard 的回复 (2023-03-15 13:56:32 PDT) ---

没发现啥新问题,感谢更新。

--- 第 47 楼来自 willsonT 的回复 (2023-03-19 19:23:20 PDT) ---

这个接口好像失效了,连Amex自己的显示都没了。

--- 第 48 楼来自 Youshen 的回复 (2023-03-19 19:37:15 PDT) ---

@amexrat 动作真快

类别奖励确实没了,但起码bonus还留着,还是可以看看每个卡refer了多少。

要等amex网页出新样本才能更新了

--- 第 49 楼来自 Youshen 的回复 (2023-03-20 19:26:50 PDT) ---

刚刚又能用了,结果啥都不用改

--- 第 50 楼来自 QTY 的回复 (2023-03-20 20:48:21 PDT) ---

真好用!感谢楼主

--- 第 51 楼来自 T00SHADOW 的回复 (2023-07-07 13:09:57 PDT) ---

这个弹窗… 不知道为什么

--- 第 52 楼来自 Youshen 的回复 (2023-07-07 13:22:25 PDT) ---

我用没问题,你这是什么卡?

--- 第 53 楼来自 T00SHADOW 的回复 (2023-07-07 13:23:58 PDT) ---

hilton aspire + surpass

--- 第 54 楼来自 Youshen 的回复 (2023-07-07 13:24:53 PDT) ---

cobrand不支持啊

--- 第 55 楼来自 T00SHADOW 的回复 (2023-07-07 13:35:21 PDT) ---

对不起 我捞了。。。。

--- 第 56 楼来自 WackoRabbit 的回复 (2023-07-08 22:08:40 PDT) ---

biz gold无副卡失败, 手动找了token 替换就可以了.

VM291:76 Uncaught TypeError: Cannot read properties of undefined (reading ‘axp-myca-root’)

at :75:36

--- 第 57 楼来自 Youshen 的回复 (2023-07-09 18:40:41 PDT) ---

更新了,因为amex网页有update

--- 第 58 楼来自 coolguy100 的回复 (2024-11-13 21:22:09 PST) ---

有这个error:

image1011×127 4.36 KB

--- 第 59 楼来自 Youshen 的回复 (2024-11-15 18:32:59 PST) ---

你是不是开了其他amex插件

--- 第 60 楼来自 coolguy100 的回复 (2024-11-15 18:51:28 PST) ---

没有别的插件

image785×847 43.7 KB

image799×334 12.4 KB

--- 第 61 楼来自 Youshen 的回复 (2024-11-15 18:53:17 PST) ---

你截图看看选卡的下拉框长啥样

--- 第 62 楼来自 coolguy100 的回复 (2024-11-15 18:54:24 PST) ---

image923×426 57.9 KB

现在是tiles了,不是老的list

--- 第 63 楼来自 Youshen 的回复 (2024-11-15 18:55:55 PST) ---

我的界面还是下拉框,不是这样方格的,测试不了

--- 第 65 楼来自 Youshen 的回复 (2024-11-27 19:25:35 PST) ---

我发现了,你是不是选了all accounts, 你要先选中想看的那一张卡去主界面

--- 第 66 楼来自 teddy517 的回复 (2025-02-13 17:24:45 PST) ---

image1910×209 8.79 KB

我现在也报错,是选的想看的那张卡,也打开了所有的卡,之前还好好的

--- 第 67 楼来自 Youshen 的回复 (2025-02-13 17:40:11 PST) ---

amex文艺复兴了,你试试从历史记录里面找回Update 4/5/24的版本

--- 第 68 楼来自 Reaper-17 的回复 (2026-03-15 19:45:17 PDT) ---

最新版和旧版代码现在都用不了了

--- 第 69 楼来自 Youshen 的回复 (2026-03-15 20:24:26 PDT) ---

我刚刚试了一楼的还可以啊,就是卡名字显示不对,统计还是在的

如果你确定步骤都没错的话,把console的错误贴出来看看

而且amex老弄a/b test界面,很难debug

--- 第 70 楼来自 Reaper-17 的回复 (2026-03-15 22:57:03 PDT) ---

image1377×159 7.62 KB

步骤就是进入特定卡的主页,展开下拉框,输入代码

--- 第 71 楼来自 Youshen 的回复 (2026-03-16 20:40:02 PDT) ---

我今天乱跑的是碰到一次这个error,但再刷新就好了

试试最新油猴版吧

--- 第 72 楼来自 Reaper-17 的回复 (2026-03-16 21:59:52 PDT) ---

可以了,感谢!