# OpenGoods 公共 API 开发者文档 天工商品档案公共仓(OpenGoods)提供**公开、只读**的商品事实 REST API:按条码/名称查询商品的客观资料(品牌、品类、净含量、产地、配料、营养成分、Nutri-Score、厂商建议零售价快照等)。返回均为 JSON(UTF-8)。**本服务不含任何购买/交易接口。** - 基础地址:`https://goods.tangshasha.com/api/v1` - 机器可读规范(OpenAPI 3):`GET /api/v1/openapi.json` - 交互式文档:站点「API 调用说明」页 ## 鉴权 API 默认**匿名可用**,无需任何凭证即可调用。匿名请求按来源 IP 计入一个较低的默认每分钟额度。 如需更高额度并让用量归属到你,可向运营方申请一枚 **API Key**(形如 `og_live_xxxxxxxx`),请求时二选一携带: ```bash curl -H "X-API-Key: og_live_xxxxxxxx" \ "https://goods.tangshasha.com/api/v1/products/search?q=牛奶" # 或 curl -H "Authorization: Bearer og_live_xxxxxxxx" \ "https://goods.tangshasha.com/api/v1/products/search?q=牛奶" ``` > 仅在创建时返回一次明文 Key,请妥善保存。服务端只存储其 SHA-256 哈希。 ## 限流 采用**固定窗口**限流(每分钟)。每个响应都会回写以下响应头: | 响应头 | 含义 | | --- | --- | | `X-RateLimit-Limit` | 当前窗口允许的最大请求数 | | `X-RateLimit-Remaining` | 当前窗口剩余可用次数 | | `X-RateLimit-Reset` | 窗口重置的 Unix 时间戳(秒) | | `Retry-After` | 仅在超额(429)时返回,建议等待的秒数 | - 超过额度:`429 Too Many Requests`,错误码 `rate_limited`。 - Key 无效或已吊销:`401 Unauthorized`,错误码 `invalid_api_key`。 ## 错误格式 非 2xx 响应体统一为: ```json { "error": { "code": "not_found", "message": "…", "request_id": "…" } } ``` ## 分页 列表类接口支持 `page`(默认 `1`)与 `size`(默认 `20`,最大 `100`),响应含 `page`/`size`/`total`。 ## 端点 ### `GET /products/search` — 搜索商品 按名称做三元组(trigram)模糊搜索,**可容忍错别字**;支持品类/品牌/产地过滤;结果按相关度(名称相似度 × 数据质量分)排序。 | 参数 | 必填 | 说明 | | --- | --- | --- | | `q` | 否 | 关键词(名称/条码),模糊匹配;留空则按质量分返回全部 | | `category` | 否 | 品类编码(含子树),如 `food.beverages` | | `brand` | 否 | 品牌名(模糊匹配),如 `Ferrero` | | `country` | 否 | 产地前缀(不区分大小写),如 `China` | | `page` | 否 | 页码,默认 1 | | `size` | 否 | 每页条数,默认 20,最大 100 | ```bash curl "https://goods.tangshasha.com/api/v1/products/search?q=nutela&country=Italy" ``` ```json { "items": [ { "id": "…", "gtin": "3017624010701", "name": "Nutella", "brand": "Ferrero", "category_path": "food.snacks.chocolate", "country_of_origin": "Italy", "quality_score": 0.81, "score": 0.71 } ], "page": 1, "size": 20, "total": 1 } ``` `score` 为名称相关度(提供 `q` 时返回,0–1),未提供 `q` 时为 `null`。 ### `GET /products/barcode/{gtin}` — 按条码查询 ```bash curl "https://goods.tangshasha.com/api/v1/products/barcode/5449000000996" ``` ### `GET /products/{id}` — 商品详情 按商品 UUID 获取完整档案(含配料、营养、添加剂、图片、MSRP 等)。 ### `GET /products/{id}/nutriments` — 商品营养成分 ### `GET /products/{id}/msrp` — 厂商建议零售价快照 官方建议零售价历史快照,仅供参考,不含任何购买入口。 ### `GET /brands` — 品牌列表(分页) ### `GET /categories` — 品类树 ### `GET /sources/{id}` — 数据来源 ## 档案回流(写接口,需 API Key) > 仅供进销存等机器调用方使用:把档案里**尚未收录**的商品批量回流到站点,进入人工审核队列,审核通过后才会收录。**必须携带 API Key**(与上文同一类 `og_live_` 密钥),不会直接写入商品。 ### `POST /api/public/backflow` — 批量回流未收录商品 - 鉴权:请求头携带 `X-API-Key: og_live_xxxxxxxx`(或 `Authorization: Bearer og_live_xxxxxxxx`)。缺失/无效/已吊销返回 `401`。 - 请求体:商品对象**数组**(与公众投稿同结构),单次最多 `1000` 条。常用字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `name` | 是 | 商品名称 | | `gtin` | 否 | 条码(GTIN)。强烈建议提供,用于去重 | | `brand_name` | 否 | 品牌名 | | `category_id` | 否 | 品类编码,如 `food.beverages` | | `net_content_value` / `net_content_unit` | 否 | 净含量数值 / 单位 | | `country_of_origin` | 否 | 产地 | | `ingredients_text` | 否 | 配料表 | | `nutriments` | 否 | 营养成分对象 | | `msrp` | 否 | 零售价快照数组,元素含 `amount`/`currency`/`region`/`effective_date` | | `note` | 否 | 备注 | > 来源会自动标记为 `source="backflow"`,在后台审核队列中与公众投稿区分,无需调用方提供。 - 去重(按 `gtin` 逐条判断,互不影响): - 该条码已收录为商品 → `exists`,跳过; - 已存在同条码的待审核回流 → `duplicate`,跳过(避免反复刷队列); - 否则入队 → `queued`(status=`pending`,等待后台审核); - 名称为空等 → `invalid`。 ```bash curl -X POST "https://goods.tangshasha.com/api/public/backflow" \ -H "X-API-Key: og_live_xxxxxxxx" \ -H "Content-Type: application/json" \ -d '[ {"name":"某某牛奶 250ml","gtin":"6901234567890","brand_name":"某品牌", "net_content_value":250,"net_content_unit":"ml", "msrp":[{"amount":3.5,"currency":"CNY","region":"CN"}]}, {"name":"已收录商品","gtin":"5449000000996"} ]' ``` ```json { "total": 2, "queued": 1, "exists": 1, "duplicate": 0, "invalid": 0, "results": [ { "gtin": "6901234567890", "name": "某某牛奶 250ml", "status": "queued", "id": "" }, { "gtin": "5449000000996", "name": "已收录商品", "status": "exists", "reason": "该条码商品已收录" } ] } ``` 审核通过后,系统按提交内容**新建商品**;若审核时该条码已存在商品,则**补全**到已有商品(逻辑与公众投稿一致)。 ## 免责声明 数据可能存在误差或滞后,按「现状」提供,不构成医疗/购买建议。商品资料版权归各原始来源所有,请遵循其许可(如 OpenFoodFacts 的 ODbL),引用时请注明天工商品档案公共仓及原始来源。