前言
在亚马逊卖家开发中,Listings Items API (v2021-08-01) 是核心接口之一,用于程序化创建 / 更新商品 Listing。本文基于Java 语言 ,结合亚马逊官方 SP-API SDK,实现商品 Listing 创建(基础信息)+ 图片批量上传完整流程,代码可直接复用,适配北美站点(美国站),同时对敏感密钥做脱敏处理,保障安全。
适用场景:批量上架商品、自动化 Listing 维护、ERP 系统对接亚马逊 SP-API 依赖:亚马逊 SP-API Java SDK、LWA 授权组件
一、核心知识点前置说明
- 授权方式 :使用亚马逊 LWA(Login with Amazon)授权,通过
RefreshToken获取访问令牌,是 SP-API 标准授权方式 - 接口分工
putListingsItem:创建 / 全量更新商品 Listing 基础信息(标题、五点、价格、属性等)patchListingsItem:增量更新商品信息(图片上传专用,不覆盖原有数据)
- 关键约束 :创建 Listing 时不能直接携带图片,必须分两步:先创建商品 → 再单独上传图片
- 站点适配 :代码默认美国站(
ATVPDKIKX0DER),可修改MARKETPLACE_ID适配其他站点
二、完整代码解读(脱敏版)
1. 依赖导入(Maven)
首先在pom.xml中引入亚马逊 SP-API 官方依赖,这是调用接口的基础:
<dependency>
<groupId>software.amazon.spapi</groupId>
<artifactId>spapi-sdk</artifactId>
<version>1.5.0</version>
</dependency>
2. 代码整体结构
代码分为4 大核心模块,逻辑清晰,适合二次开发:
- 配置参数模块:存储 SP-API 授权、卖家、站点信息(密钥已脱敏)
- 主流程模块:授权初始化 → 创建商品 → 校验结果 → 上传图片
- 商品属性构建模块:封装 Listing 所有基础信息(标题、五点、价格等)
- 图片上传模块:批量上传主图 + 附图,支持请求间隔防限流
3. 逐模块详细解读
(1)敏感配置参数(脱敏处理)
所有密钥、ID 均做脱敏加密,实际使用时替换为自己的信息即可:
// ==================== 脱敏配置参数(请替换为自己的真实信息)====================
// LWA授权客户端ID(脱敏)
private static final String CLIENT_ID = "amzn1.application-oa2-client.**************************";
// LWA授权客户端密钥(脱敏)
private static final String CLIENT_SECRET = "amzn1.oa2-cs.v1.**************************************************";
// 刷新令牌(脱敏)
private static final String REFRESH_TOKEN = "Atzr|IwEBI*********************************************************************************************************";
// 卖家ID
private static final String SELLER_ID = "A1AZHTI4Q*****";
// 美国站市场ID(固定值)
private static final String MARKETPLACE_ID = "ATVPDKIKX0DER";
// 商品SKU(自定义)
private static final String SKU = "QB3V8**A63-1";
✅ 安全说明:绝对不要在生产环境明文存储密钥,建议配置到环境变量 / 配置中心。
(2)LWA 授权初始化
SP-API 所有接口都需要先完成 LWA 授权,这是调用的前提:
// 1. 配置LWA授权凭证
LWAAuthorizationCredentials credentials = LWAAuthorizationCredentials.builder()
.clientId(CLIENT_ID) // 客户端ID
.clientSecret(CLIENT_SECRET) // 客户端密钥
.refreshToken(REFRESH_TOKEN) // 刷新令牌
.endpoint("https://api.amazon.com/auth/o2/token") // LWA授权地址
.build();
// 2. 创建ListingsApi实例(指定北美站点接口地址)
ListingsApi listingsApi = new ListingsApi.Builder()
.lwaAuthorizationCredentials(credentials)
.endpoint("https://sellingpartnerapi-na.amazon.com")
.build();
- 授权地址:全球通用
https://api.amazon.com/auth/o2/token - 接口端点:北美站点
na、欧洲站点eu、远东站点fe
(3)第一步:创建商品 Listing 基础信息
调用putListingsItem接口,提交商品核心属性,不含图片:
// 构建商品属性(标题、五点、价格、材质等)
Map<String, Object> attributes = buildProductAttributes();
// 组装请求体
ListingsItemPutRequest requestBody = new ListingsItemPutRequest();
requestBody.setProductType("KITCHEN"); // 商品类型(厨房类)
requestBody.setRequirements(ListingsItemPutRequest.RequirementsEnum.LISTING); // 基础Listing要求
requestBody.setAttributes(attributes); // 商品属性
// 调用创建接口
ListingsItemSubmissionResponse response = listingsApi.putListingsItem(
requestBody, SELLER_ID, SKU, Collections.singletonList(MARKETPLACE_ID),
null, null, null, null
);
- 商品类型 :必须填写亚马逊后台认可的 Product Type(本文用厨房类
KITCHEN) - 结果校验:创建后自动检查是否有错误,有错误直接终止流程,避免无效图片上传
(4)核心:商品属性构建
buildProductAttributes()方法封装了Listing 所有必填 + 常用选填属性,直接复用:
- 基础信息:标题、品牌、描述、型号、颜色、材质
- 营销信息:五点描述、搜索关键词
- 交易信息:价格、库存、重量
- 合规信息:产地、危险品声明(厨房用品无需危险品认证)
重点:创建 Listing 时禁止添加图片属性,亚马逊接口不支持,必须单独上传!
(5)第二步:PATCH 方式上传图片
这是本文的核心亮点,专用patchListingsItem接口上传图片,支持主图 + 附图批量上传:
// 定义图片(公开可访问的HTTPS地址,亚马逊必须能直接访问)
List<ImageInfo> images = Arrays.asList(
new ImageInfo("主图HTTPS地址", "main_product_image_locator", true),
new ImageInfo("附图1HTTPS地址", "other_product_image_locator_1", false),
new ImageInfo("附图2HTTPS地址", "other_product_image_locator_2", false)
);
// 循环上传图片
for (ImageInfo img : images) {
PatchOperation patchOp = new PatchOperation();
patchOp.setOp(PatchOperation.OpEnum.REPLACE); // 增量更新
patchOp.setPath("/attributes/" + img.attributeName); // 图片属性路径
patchOp.setValue(Collections.singletonList(Map.of(
"marketplace_id", MARKETPLACE_ID,
"media_location", img.url
)));
// 调用PATCH接口上传
listingsApi.patchListingsItem(...);
Thread.sleep(500); // 间隔500ms,防接口限流
}
✅ 图片要求:
- 必须是HTTPS 协议的公开访问地址
- 主图属性:
main_product_image_locator - 附图属性:
other_product_image_locator_数字 - 图片格式:JPG/PNG,符合亚马逊图片规范
(6)自定义图片实体类
简化图片参数管理,代码更优雅:
private static class ImageInfo {
final String url; // 图片地址
final String attributeName; // 亚马逊图片属性名
final boolean isMain; // 是否主图
ImageInfo(String url, String attributeName, boolean isMain) {
this.url = url;
this.attributeName = attributeName;
this.isMain = isMain;
}
}
三、代码运行效果
执行main方法,控制台输出清晰的执行日志:
- 第一步:创建商品基本信息 → 返回提交 ID、状态
- 第二步:批量上传图片 → 主图 / 附图逐个上传成功
- 最终:全部完成,商品创建 + 图片上传完毕
错误处理:
- API 异常:打印状态码 + 响应体,快速定位问题(如属性错误、授权失败)
- 业务错误:检查亚马逊返回的
Issues,获取具体错误提示
四、常见问题避坑
- 创建 Listing 报错:图片属性不支持 → 解决方案:创建时不要加图片,必须用 PATCH 单独上传
- 图片上传失败:亚马逊无法访问图片地址 → 解决方案:使用公开 HTTPS 地址,禁止本地路径 / 私有链接
- 授权失败:401 Unauthorized → 解决方案:检查
CLIENT_ID/CLIENT_SECRET/REFRESH_TOKEN是否正确 - 接口限流:429 Too Many Requests → 解决方案:保留代码中的
Thread.sleep(500),降低请求频率 - 商品类型错误 → 解决方案:替换为自己类目对应的合法 Product Type
五、总结
本文提供的代码是亚马逊 SP-API Listing 管理的标准实战方案,具备以下优势:
- 完整可用:从授权到创建商品、上传图片,一站式实现
- 安全规范:敏感密钥脱敏,符合开发安全规范
- 易扩展:可轻松改造为批量上架工具,支持多 SKU、多类目
- 稳定可靠:严格遵循亚马逊接口规范,错误处理完善
如果你需要对接亚马逊 SP-API 进行商品管理,这份代码可以直接作为核心模块使用!
💡 原创不易,欢迎点赞 + 收藏 + 关注,后续更新更多亚马逊 SP-API 实战教程!
附:完整可运行代码
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials;
import com.amazon.SellingPartnerAPIAA.LWAException;
import software.amazon.spapi.ApiException;
import software.amazon.spapi.api.listings.items.v2021_08_01.ListingsApi;
import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemPutRequest;
import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemSubmissionResponse;
import software.amazon.spapi.models.listings.items.v2021_08_01.ListingsItemPatchRequest;
import software.amazon.spapi.models.listings.items.v2021_08_01.PatchOperation;
import java.util.*;
public class CreateListingPro {
// ==================== 配置参数(使用你已验证过的凭证)====================
private static final String CLIENT_ID = "amzn1.application-oa2-client.d4099ee7a42f480d8a42fa63c15297d26";
private static final String CLIENT_SECRET = "amzn1.oa2-cs.v1.a940db0aafee4c23d81fa1f1f933f6647d6adb5f2aaf2e1d992cf4a6829972992";
private static final String REFRESH_TOKEN = "Atzr|IwEBIJ6fz00AKqA5eU166lsnFAMuzcGnYRJN18Q0BpQ4Rx85VwViDVgWJO35FFhU7wkD_hjrM7PCugYbsXZobnpCYCmYnUGLs4C-UVFVSnvLP2wG028l8vn4EKyjxm2faK8vrCszwCPY8FU0V4X7ZdxWf-ugDSvvwoQiO-PFi81Q1OAiSSw6doluNuw8logWW4qMMQdy5Dc2aNBnFKLHRy2LlG8503_LYqefpsbUpmJC8jYq0KBk9Q6IEFT0DhUaZQ_VQhVCwh51tIFPCVU5zR9-uoVDG3xpCu3ny-sVRzQkBz2I35FoHH7EtZksG7LUoZTl6Zi0";
private static final String SELLER_ID = "A1AZHTI4Q7W3AY"; // 你的卖家ID
private static final String MARKETPLACE_ID = "ATVPDKIKX0DER"; // 美国站
private static final String SKU = "QB345GFA63-1"; // 从你的表格中选取
public static void main(String[] args) {
try {
// 1. 配置 LWA 凭证
LWAAuthorizationCredentials credentials = LWAAuthorizationCredentials.builder()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.refreshToken(REFRESH_TOKEN)
.endpoint("https://api.amazon.com/auth/o2/token")
.build();
// 2. 创建 ListingsApi 实例
ListingsApi listingsApi = new ListingsApi.Builder()
.lwaAuthorizationCredentials(credentials)
.endpoint("https://sellingpartnerapi-na.amazon.com")
.build();
// ========== 第1步:创建商品(不含图片)==========
System.out.println(">>> 步骤1:创建商品基本信息...");
Map<String, Object> attributes = buildProductAttributes();
ListingsItemPutRequest requestBody = new ListingsItemPutRequest();
requestBody.setProductType("KITCHEN");
requestBody.setRequirements(ListingsItemPutRequest.RequirementsEnum.LISTING);
requestBody.setAttributes(attributes);
List<String> marketplaceIds = Collections.singletonList(MARKETPLACE_ID);
ListingsItemSubmissionResponse response = listingsApi.putListingsItem(
requestBody,
SELLER_ID,
SKU,
marketplaceIds,
null, null, null, null
);
System.out.println("✅ 商品创建请求已提交!");
System.out.println("SubmissionId: " + response.getSubmissionId());
System.out.println("Status: " + response.getStatus());
// 检查创建结果
if (response.getIssues() != null && !response.getIssues().isEmpty()) {
boolean hasError = false;
for (var issue : response.getIssues()) {
if ("ERROR".equalsIgnoreCase(String.valueOf(issue.getSeverity()))) {
hasError = true;
}
System.out.println(" - [" + issue.getSeverity() + "] " + issue.getCode() + ": " + issue.getMessage());
}
if (hasError) {
System.out.println("❌ 创建商品时有错误,停止上传图片。");
return;
}
}
// ========== 第2步:上传图片(使用 PATCH)==========
System.out.println("\n>>> 步骤2:上传商品图片...");
uploadImages(listingsApi, credentials);
System.out.println("\n🎉 全部完成!商品已创建,图片已上传。");
} catch (ApiException e) {
System.err.println("❌ API 调用异常");
System.err.println("状态码: " + e.getCode());
System.err.println("响应体: " + e.getResponseBody());
e.printStackTrace();
} catch (Exception e) {
System.err.println("❌ 其他错误: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 上传图片 - 使用 Listings API 的 PATCH 方式
* 参考:https://developer-docs.amazon.com/sp-api/docs/listings-items-api-v2021-08-01-reference#patch-listings2021-08-01itemsselleridsku
*/
private static void uploadImages(ListingsApi listingsApi, LWAAuthorizationCredentials credentials) throws ApiException {
List<String> marketplaceIds = Collections.singletonList(MARKETPLACE_ID);
// 定义图片:主图 + 附图
// 图片必须是可以公开访问的 HTTPS URL
List<ImageInfo> images = Arrays.asList(
new ImageInfo("https://m.media-amazon.com/images/I/51f0Hh4toYL._SL1500_.jpg", "main_product_image_locator", true),
new ImageInfo("https://m.media-amazon.com/images/I/81WUIDw2vwL._SL1500_.jpg", "other_product_image_locator_1", false),
new ImageInfo("https://m.media-amazon.com/images/I/71+VPo-TOuL._SL1500_.jpg", "other_product_image_locator_2", false)
);
for (ImageInfo img : images) {
try {
// 构建 PATCH 请求体
ListingsItemPatchRequest patchRequest = new ListingsItemPatchRequest();
patchRequest.setProductType("KITCHEN");
// 构建 patch 操作
PatchOperation patchOp = new PatchOperation();
patchOp.setOp(PatchOperation.OpEnum.REPLACE);
patchOp.setPath("/attributes/" + img.attributeName);
// 图片属性值结构
List<Map<String, Object>> imageValues = Collections.singletonList(Map.of(
"marketplace_id", MARKETPLACE_ID,
"media_location", img.url
));
patchOp.setValue(imageValues);
patchRequest.setPatches(Collections.singletonList(patchOp));
// 执行 PATCH
ListingsItemSubmissionResponse patchResponse = listingsApi.patchListingsItem(
patchRequest, // body
SELLER_ID, // sellerId
SKU, // sku
marketplaceIds, // marketplaceIds
null, // includedData (可选)
null, // mode (可选)
null, // issueLocale (可选)
null // restrictedDataToken (可选)
);
System.out.println(" ✅ " + (img.isMain ? "主图" : "附图") + " 上传成功");
System.out.println(" URL: " + img.url);
System.out.println(" Status: " + patchResponse.getStatus());
// 检查是否有问题
if (patchResponse.getIssues() != null && !patchResponse.getIssues().isEmpty()) {
for (var issue : patchResponse.getIssues()) {
System.out.println(" ⚠️ [" + issue.getSeverity() + "] " + issue.getMessage());
}
}
// 避免请求过快,间隔 500ms
Thread.sleep(500);
} catch (ApiException e) {
System.err.println(" ❌ " + (img.isMain ? "主图" : "附图") + " 上传失败");
System.err.println(" 状态码: " + e.getCode());
System.err.println(" 响应: " + e.getResponseBody());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (LWAException e) {
throw new RuntimeException(e);
}
}
}
/**
* 图片信息内部类
*/
private static class ImageInfo {
final String url;
final String attributeName;
final boolean isMain;
ImageInfo(String url, String attributeName, boolean isMain) {
this.url = url;
this.attributeName = attributeName;
this.isMain = isMain;
}
}
private static Map<String, Object> buildProductAttributes() {
Map<String, Object> attributes = new HashMap<>();
String language = "en_US";
String marketplace = MARKETPLACE_ID;
// ==================== 1. 标识符 ====================
attributes.put("merchant_suggested_asin", List.of(Map.of("value", "QB3V01")));
// 外部产品 ID(GTIN/UPC/EAN)- 请替换为真实有效的 GTIN
attributes.put("externally_assigned_product_identifier", List.of(
Map.of("value", "01234567890123", "type", "GTIN")
));
// ==================== 2. 危险品信息 ====================
// 厨房水龙头通常不需要危险品声明,删除此行或保留正确值
// attributes.put("supplier_declared_dg_hz_regulation", List.of(Map.of("value", "not_applicable")));
attributes.put("supplier_declared_dg_hz_regulation", List.of(Map.of("value", "other")));
// ==================== 3. 基础信息 ====================
attributes.put("item_name", List.of(
Map.of("value", "Stylish 22 Inch High-Arc Pull-Down Faucet with Spring for Commercial Kitchen Sinks, Polished Chrome",
"language_tag", language, "marketplace_id", marketplace)
));
attributes.put("product_description", List.of(
Map.of("value", "This stylish high-arc pull-down faucet features a commercial spring design...",
"language_tag", language)
));
attributes.put("brand", List.of(Map.of("value", "TGM", "language_tag", language)));
attributes.put("manufacturer", List.of(Map.of("value", "TGM", "language_tag", language)));
attributes.put("part_number", List.of(Map.of("value", "QB3V8GFA69-2")));
attributes.put("condition_type", List.of(Map.of("value", "new_new")));
attributes.put("material", List.of(Map.of("value", "Brass")));
attributes.put("size", List.of(Map.of("value", "One Size")));
attributes.put("color", List.of(Map.of("value", "Polished Chrome")));
attributes.put("country_of_origin", List.of(Map.of("value", "CN")));
attributes.put("item_type_keyword", List.of(Map.of("value", "kitchen-faucet")));
attributes.put("model_name", List.of(Map.of("value", "Kitchen Faucet Spring")));
attributes.put("model_number", List.of(Map.of("value", "QB3V8GFA69-2")));
attributes.put("care_instructions", List.of(Map.of("value", "Clean with soft cloth")));
attributes.put("included_components", List.of(Map.of("value", "Faucet, Installation Kit, Instructions")));
// 布尔 / 数字
attributes.put("is_refurbished", List.of(Map.of("value", false)));
attributes.put("number_of_boxes", List.of(Map.of("value", 1)));
attributes.put("contains_liquid_contents", List.of(Map.of("value", false)));
attributes.put("number_of_items", List.of(Map.of("value", 1)));
// ==================== 4. 五点描述 ====================
attributes.put("bullet_point", List.of(
Map.of("value", "Elegant and Functional Design: This 22-inch high-arc kitchen faucet features a sleek, single-handle design..."),
Map.of("value", "Premium Quality Construction: Crafted from 100% lead-free brass for all water-contact parts..."),
Map.of("value", "Enhanced Flow Rate: Experience the efficiency of a 1.8 GPM flow rate..."),
Map.of("value", "Long-Lasting Seal Valve: Engineered to international brand standards..."),
Map.of("value", "Versatile 3-Function Spray Head: Our pull-down sprayer offers three modes...")
));
// ==================== 5. 搜索关键词 ====================
attributes.put("generic_keyword", List.of(
Map.of("value", "Farmhouse; Kitchen Faucet; 22 Inch; High Arc; Commercial; Spring Faucet; Pull Down Sprayer; Sink; Polished Chrome;")
));
// ==================== 6. 价格与库存 ====================
attributes.put("list_price", List.of(Map.of("value", 99.99, "currency_code", "USD")));
attributes.put("fulfillment_availability", List.of(
Map.of("fulfillment_channel_code", "DEFAULT", "quantity", 1)
));
// ==================== 7. 重量 ====================
attributes.put("item_weight", List.of(Map.of("value", 5.9, "unit", "pounds")));
// ==================== 8. 图片(已移除,改用 PATCH 上传)====================
// 注意:Listings API putListingsItem 不支持直接传 images 属性
// 图片通过 patchListingsItem 单独上传
return attributes;
}
}
