亚马逊广告API对接实战:从OAuth鉴权到数据稳定同步
2026/9/16 2:01:24 网站建设 项目流程

1. 为什么我要啃下亚马逊广告接口这块硬骨头

先说个背景吧。我在一家做跨境ERP的团队里负责广告数据模块,说白了,就是要把卖家在亚马逊后台投的广告数据抓到我们自己系统里,做成报表、算ROI、做自动化调价。一开始大家觉得这事简单,花了两周去调研,最后得出的结论是:直接对接亚马逊广告接口(amazon advertising API)的难度,比想象中大得多,但收益也比“爬后台”那套老路子高得多。

当时团队里有两种声音。一种是“咱们直接拿卖家的后台账密去抓页面数据不就行了,接口这玩意太麻烦”;另一种是“必须走官方API”。前者确实快,但瓶颈很快就会出现:网页版后台的类目字段经常调整,分页数据抓不全,最重要的是一旦卖家账号开了双重验证,或亚马逊风控升级,整条数据链路就崩了。用户画像里就不乏那种“调得好好的,突然某天就抓不到数据”的卖家。所以我们最终拍板,走官方API。

这篇内容不是官方文档翻译,而是把我自己的接入过程、踩坑经历、调试逻辑写出来。如果你正准备对接亚马逊广告接口,或者已经在对接路上卡住了,希望这篇文章能帮你少走几周弯路。

先说这个API能干什么:拉取广告活动(Campaign)、广告组(Ad Group)、关键词(Keyword)、搜索词报告(Search Term Report)、投放位报告(Placement)、预算和竞价建议、获取账单数据等等。基本覆盖了卖家在广告后台能看到的90%以上的数据维度,而且支持SP(Sponsored Products)、SB(Sponsored Brands)、SD(Sponsored Display)三种广告类型。

适合谁来读?主要是两类人:一类是开发同学,准备写代码对接;另一类是负责广告投放的运营或产品经理,他们不需要写代码,但需要知道“系统里的广告数据是什么逻辑过来的”,因为接口字段的含义直接影响业务报表的解读。

2. 接入前必须搞懂的几个核心概念

如果你直接打开亚马逊广告API的官方文档,大概率第一反应是:文档怎么这么乱?因为亚马逊把API拆分成了两套体系,而且权限模型、数据口径、调用方式都不一样。我在这里把几个会卡住人的点先捋清楚。

2.1 两个API体系:Advertiser API 和 旧版 API

亚马逊广告接口目前有两套体系。一套叫Amazon Advertising API(新版,v3版本之后都走这个),一套是老旧的Ad Product API(也称为旧版API)

新版API是JSON格式的REST接口,支持SP/SB/SD三种广告类型,适配卖家后台的“新版广告平台”;旧版API是SOAP协议,主要用于老的广告平台,数据字段非常有限,亚马逊官方已经在逐步收缩它的能力。

我建议新项目一律直接对接新版API,别纠结。我们当时因为接手了一个老系统,里面有一堆旧版API的调用,后来被迫做了一个兼容层,额外花了不少时间。如果你是新项目,没有什么历史包袱,从第一天开始就只打新版API的端点。

2.2 广告类型字段差异

先看一张我用实际数据整理的对比表,这决定了你建表结构时需要预留哪些字段:

广告类型全称数据对象特有字段
SPSponsored ProductsAdGroup, Keyword, ProductAd关键词匹配类型、ASIN(商品ID)
SBSponsored BrandsCampaign, AdGroup, Brand品牌Logo、创意素材、视频素材
SDSponsored DisplayCampaign, AdGroup受众定向、商品投放、浏览再营销
SSASponsored StoresCampaign店铺页投放

这里有个大坑:同一张报表里,SP和SD的字段含义是不一致的。比如“impressions”在SP里指的是“商品广告展示次数”,在SD里指的是“展示型广告的曝光次数”。两边的分母不同,含义也不同。如果你直接用一套通用字段去接收所有类型的数据,报表里就会出现“这个数字怎么对不上后台”的问题。

2.3 Profile(配置)概念

这是新手最容易懵的地方。亚马逊广告API的鉴权需要通过Login with Amazon(LWA)拿到Access Token,但拿到Token之后还不能直接查数据,你还需要获取“Profile ID”。

每个Profile代表一个广告账户,一个卖家可能同时拥有多个国家站点的广告账户,甚至一个账号下挂了多个品牌授权账户。你在调用接口时,必须在HTTP请求头里带上Amazon-Advertising-API-ProfileId这个参数,否则接口会返回403401

我当时调了一个下午,一直在报401,最后排查发现,是因为获取Profile的接口返回的是一个数组,而我拿了一个空的ProfileId去访问,当然被拒了。ProfileId的获取接口是/v2/profiles(或新版/v2/profiles下的端点),返回的数组里每个元素包含profileIdaccountInfocountryCode等字段。

2.4 时间口径与报表延迟

广告API的报表数据不是实时的。广告活动的曝光量、点击量、花费有大概3-4小时延迟,转化类指标(订单量、销售额、ACOS)最长可能要14小时才能完整产出。所以你在系统里看到的数据,和卖家后台页面上的数据,天然存在时间差。

如果要设计“今日实时数据”看板,我建议你明确告诉产品:不要做秒级实时,做“近24小时汇总”比较合理。否则业务看到系统数据和后台不一致,会天天来问为什么。

3. 从注册到拿到第一批数据的完整接入流程

这一部分我按“顺序”写,每一步都有明确目的,照着走基本能通。这里默认你已经有一个亚马逊广告账号(可以是卖家账号里的广告模块),并且有权限访问广告平台。

3.1 第一步:在亚马逊广告平台里面创建一个“开发者账号”

登录到亚马逊广告平台后,进入“Settings” -> “API” 菜单,先创建一个开发者账号(如果是全球站点,注意站点的区分,美国站和欧洲站的开发者后台是独立的)。创建后会获得一组Client IDClient Secret

这组凭证接下来会用在LWA(Login with Amazon)的OAuth 2.0授权流程中。

3.2 第二步:完成OAuth 2.0授权流程,换取Access Token

整体授权流程是这样的:

  1. 拼接授权URL,让广告主点击并同意授权你的应用访问其广告数据;
  2. 授权成功后,亚马逊会返回一个授权码(Authorization Code),这是一个一次性的临时码;
  3. 后端用这个授权码 + Client ID + Client Secret,换取 Access Token 和 Refresh Token。

Access Token有效期大约是1小时,Refresh Token的有效期很长(官方没有明确说明有时间限制,但实践中部分账号能达到几个月甚至一年,当然也有因风控失效的情况)。

我不建议在代码里硬编码Authorization Code,因为它的有效期非常短(大概几分钟)。正确做法是:把Refresh Token安全地存到数据库,每次刷新Access Token都用Refresh Token去换。

import requests # 1. 用授权码换取 token token_url = "https://api.amazon.com/auth/o2/token" payload = { "grant_type": "authorization_code", "code": authorization_code, # 临时授权码 "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "redirect_uri": REDIRECT_URI } resp = requests.post(token_url, data=payload) token_data = resp.json() # 包含 access_token、refresh_token、expires_in # 2. 用 refresh_token 换取新的 access_token(实际项目中这个步骤会放到定时任务里) refresh_payload = { "grant_type": "refresh_token", "refresh_token": refresh_token, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET } resp = requests.post(token_url, data=refresh_payload) new_token_data = resp.json()

3.3 第三步:获取ProfileId并请求第一批Campaign数据

拿到Access Token后,先获取ProfileId列表:

curl -X GET 'https://advertising-api.amazon.com/v2/profiles' \ -H 'Authorization: Bearer {access_token}' \ -H 'Amazon-Advertising-API-ClientId: {client_id}' \ -H 'Content-Type: application/json'

返回结果类似:

[ { "profileId": 123456789, "countryCode": "US", "accountInfo": { "marketplaceStringId": "ATVPDKIKX0DER", "id": "A1B2C3D4E5F6G7H8", "type": "seller" } } ]

拿到ProfileId之后,再请求活动列表:

curl -X GET 'https://advertising-api.amazon.com/v2/sp/campaigns' \ -H 'Authorization: Bearer {access_token}' \ -H 'Amazon-Advertising-API-ClientId: {client_id}' \ -H 'Amazon-Advertising-API-ProfileId: 123456789'

返回结果就是该广告账户下的SP广告活动列表。到这一步,你算是走通了API调用的基本链路。接下来就可以按业务需求逐步扩展了。

4. 沙箱环境:别在生产环境里练手

这部分我要特别提醒,因为我是真在金账户里跑错过测试请求的。亚马逊广告API提供了沙箱(Sandbox)环境,接口域名是https://advertising-api-test.amazon.com,用一个独立的亚马逊广告账号体系去模拟。

沙箱环境的意义在于:数据和真实环境完全隔离,可以安全地做批量操作测试、调价算法验证、请求参数调试。它不是“感觉上差不多”的模拟器,而是官方提供的测试环境,数据不会污染真实账号。

我当时踩过的一个坑是:沙箱账号也需要有一个独立的授权流程,不是直接用生产环境的Client ID + Secret就能调通的。你需要先在沙箱后台创建一个开发者账号,然后走一遍OAuth 2.0授权。简单说:沙箱和生产环境,除了代码逻辑类似,其他的一切——Client ID、Client Secret、ProfileId、Token——都是独立的。

建议做法:开发环境统一连沙箱,测试通过后再切生产。调价脚本、批量更新广告组这类“写操作”,必须在沙箱里完整验一遍。

5. 高频报错:从401到429的排查链路

接口接通之后,实际运行中最耗时间的不是写逻辑,而是排查各种状态码。我把自己踩过的高频报错整理成了一张排查表:

状态码报错信息根因分析处理方案
401UnauthorizedAccess Token过期或无效检查Refresh Token换Token的逻辑;确认Access Token未过期
403Forbidden权限不足、ProfileId错误、账号没有该广告类型权限检查是否传了正确的ProfileId;核实广告账号权限
404Not Found请求的端点不存在,或参数对应的资源不存在检查请求URL;查看资源ID是否存在
429Too Many Requests触发了接口限流增加退避重试逻辑;请求频率不要超过官方限制
500Internal Server Error亚马逊服务端错误稍后重试;如果是批量接口,拆分请求
400Bad Request请求体参数错误检查日期范围、状态字段等是否合法
413Request Entity Too Large请求体超过限制减少批量操作数量,拆成多个小请求

5.1 401/403是我见过最多的“假报错”

很多开发拿到报错第一反应是改密钥,事实上有60%的概率是:你用错了ProfileId,或者用生产环境的Token去调沙箱接口,反过来也一样。这两个环境是完全隔离的,Token和ProfileId不能混用。

排查链路建议是这样的:

  1. 确认你当前调用的环境域名(生产还是沙箱);
  2. 确认Access Token是用哪个Client ID换的;
  3. 确认请求头里的Amazon-Advertising-API-ProfileId是否属于当前环境;
  4. 如果上面都对,再看这个广告账号是否开通了你请求的广告类型权限(比如一个只投SP的账号,去查SD的活动列表就会报403)。

5.2 429限流的退避策略

亚马逊对API的限流策略比较严格,尤其是批量报表接口。官方建议用指数退避(Exponential Backoff)策略,即第一次失败后等1秒再试,第二次等2秒,第三次等4秒,最多等待不超过一定时间(实操中我设置最大等待120秒)。

我实际测试下来,报表类接口的限流感觉最明显。如果你同时为多个Profile生成报告,很容易触发429。我的解决办法是:将Profile按一定数量分组,每组串行请求,组之间错开几秒发起。这样既能保证请求不堆积,又不会把限流打得满屏飘红。

import time import random def request_with_retry(func, max_retries=5): for i in range(max_retries): try: return func() except RateLimitError: wait_time = (2 ** i) + random.uniform(0, 1) time.sleep(wait_time) raise Exception("请求多次重试仍然被限流")

6. 数据一致性:时区、时差和去重策略

API调通了只是第一步,数据准确性才是业务真正重视的。我见过太多系统上线后,运营拿着后台数据来质疑“你们这数据怎么不对”。大部分问题其实出在下面几个点上。

6.1 时区问题

亚马逊广告API返回的时间戳大多是UTC时区。但国内卖家的“今天”和亚马逊的“今天”是两个概念。你在做日报、周报、月报时,一定要确认业务上说的“今天”是指哪个时区。

我们系统里的做法是:所有API返回的数据统一存UTC时间,在展示层按用户配置的时区转换。绝不把时区转换逻辑散落在各个业务代码里,否则早晚会出现某些报表时间差8小时的情况。

6.2 数据去重与幂等

广告数据是分页拉取的,而且报表生成后支持按日期范围重新请求。如果你的同步任务在上一次执行到一半挂了,下次又从头拉,就会出现部分数据重复入库。

方案很简单但有效:在数据库里给“广告活动ID + 日期 + 广告组ID + 指标维度”建唯一索引。写入数据时用INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或upsert(PostgreSQL),保证同一维度的数据永远只有一条。

这个设计看起来很小,但能省掉后面无数“这数据怎么多了一倍”的排查时间。

6.3 报表生成的异步机制

亚马逊广告API的报表不是请求后立即返回数据的。你调用“请求生成报告”接口后,返回的是一个reportId,然后需要轮询“查询报告状态”接口,等状态变成COMPLETED后,才能去下载报告文件。

这个轮询机制有两个细节:

  • 轮询间隔不要太频繁,建议5秒左右;
  • 报告生成后,文件下载链接有时效性,尽量在状态变成完成后立即下载。

如果你的业务需要每天定时生成大量报告,建议做一个简单的任务队列:定时触发 -> 请求报告 -> 轮询状态 -> 下载并解析 -> 写入数仓。每一步的状态都要记录,方便排查。

请求报告 -> 返回reportId ↓ 轮询状态(每5秒一次,最多10分钟) ↓ 状态=COMPLETED -> 下载报告文件 ↓ 解析CSV/JSON -> 写入数据库

7. 几个容易踩的“业务口径”差异

技术问题解决后,最难的是业务口径对齐。不同部门对同一个指标的理解不同,做出来报表就五花八门。

7.1 ACOS(广告花费销售比)的三种算法

广告后台页面上的ACOS = 广告花费 / 广告销售额。但在系统报表里,我们往往会遇到三种不同的口径:

  • 广告平台直接给的ACOS(用“广告归因销售额”计算);
  • 业务部门算的ACOS(用“广告带来的总销售额”计算,可能包含自然位成交);
  • 老板看的ACOS(把所有广告相关支出都算进去,包括代理服务费、VAT等)。

这三个数据放一起会非常不一致。我建议在数据库字段上明确命名:acos_attributedacos_total_salesacos_including_fees,不要笼统地叫aco。否则产品评审时必然会被问到“这个ACOS为什么和后台不一样”。

7.2 广告类型和投放方式

SP广告下的关键词广告、商品定位广告,虽然都在同一个Campaign里,但数据归属是不同的。你拉关键词报告和商品投放报告,拿到的数据维度完全不一样。如果你只是拉Campaign层面的汇总数据,就看不到“哪些关键词赚钱、哪些关键词烧钱”这个最核心的业务信息。

这就是为什么很多时候接口能通,但业务还是说“你给的数据没用”——因为你没把数据拆到关键词/ASIN粒度。亚马逊广告的数据建模成本主要就卡在这个维度上。

8. 代码层面的一些设计建议

走到这一步,你已经能稳定同步数据了。最后我分享几个工程化层面的设计建议,这些是从我们生产环境踩坑后总结出来的。

8.1 Token管理做成独立服务

Access Token的刷新逻辑分散在各个业务代码里,是极其糟糕的做法。一旦Token刷新逻辑有变化(比如需要用不同的Refresh Token换取不同站点的Token),你就要找全代码里去改。

建议做成一个独立的Token服务,它负责:

  • 统一存储每个广告账号的Refresh Token;
  • 定时刷新Access Token,并缓存到内存(比如Redis);
  • 对外暴露get_access_token(profile_id)接口。

这样其他业务模块不需要关心Token机制,拿过来就是一个可用的Token。

8.2 用“增量同步”代替“全量同步”

初期把数据全部拉一遍没什么问题,但后续建议改成增量同步。广告数据的“增量”逻辑比ERP业务数据难搞一些,因为广告数据是随时间变化的、可以回溯的。

我的做法是:每天定时拉取前一天的完整数据,而不是拉取“今天零点到现在的数据”。原因就是前面说的报表延迟问题——拉“昨天”的数据,能保证数据的完整性和准确性。

这么做会有一个代价:每天的报表依赖前一天的24点数据,也就是最快要到第二天凌晨约2点-4点才能完整。如果你的业务要求“当天实时看当天的数据”,那只能接受数据滞后3-4小时的事实。

8.3 日志与告警

接口对接上线后,最怕的是半夜数据同步失败,早上运营看到报表是空的。所以告警必须配上:

  • 每天报告生成失败 / 数据同步异常;
  • Token刷新失败(可能账号被风控或授权失效);
  • 连续多次429限流触发。

告警方式不用复杂,钉钉/企业微信的Webhook机器人就够了,关键是阈值要合理,别把告警做成“狼来了”的噪音。

9. 最后分享几个实操经验总结

经过这段时间的实际操作,我最大的感受是:亚马逊广告接口的精髓不在“调通”,而在于“稳定地跑”。调通一个接口,写个脚本,可能就半天;但它能不能稳定地跑上三个月、六个月,里面要处理的细节非常多。

几个比较关键的实操心得:

  • 不要把测试和生产环境混在一起调试,一旦爆发问题,不好说清楚是环境问题还是代码问题;
  • Token的安全存储比接口本身更重要,因为这涉及到卖家的广告账户授权,泄露的后果很严重,建议至少做到加密存储和权限隔离;
  • 广告数据的语义校验非常重要,同步完一批数据后,抽几个账户和后台页面做个抽样对比,确认没有结构性的数据偏差;
  • 接口升级前先看变更日志,亚马逊广告API的更新频率不低,有些字段会过期,有些会新增。你最好订阅官方更新通知,避免某天接口突然变掉。

拿我们自己的情况来说,从决定对接API到稳定输出第一批有效报表,大约花了两周时间,其中真正写代码只占了不到一半,剩下的时间全在排查权限、解决报错、对齐字段含义上。所以如果你发现进度慢,别着急,这大概率不是你的问题,而是这个系统本身的复杂度决定的。

如果需要,后续我可以专门写一篇“广告报表数据建模与BI看板搭建”的内容,把从接口数据到业务报表的完整链路再拆细一点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询