grocy 4.7.1 API 使用详解

grocy 4.7.1 REST API 使用详解:API Key 认证、系统/对象/库存/用户/文件等 74 个端点分域实测(curl 真实回显),错误码、增删改查与库存流水闭环,基于内建 OpenAPI 3.1 规范。

grocy自托管REST API2026/10/23

grocy 4.7.1 API 使用详解

目标:系统讲解 grocy 4.7.1 内建 REST API 的认证方式、通用约定与各功能域端点用法,全部请求以 curl 实测并逐条附真实响应回显(含错误码),覆盖系统信息、通用对象 CRUD、库存流水(入库/消耗/临期/撤销)、用户与 API Key 管理、日历与文件等域。实测环境:lscr.io/linuxserver/grocy:4.7.1(PHP 8.5.12 / SQLite 3.53.4)部署于 Ubuntu 22.04,服务地址 http://localhost:8150,实测时点 2026-10-23。

一、环境信息

  • grocy 版本:4.7.1(/api/system/info 实查,db_version 257)
  • API 规范版本:OpenAPI 3.1.0(/api/openapi/specification 实查),共 74 个端点路径,随包分发的 grocy.openapi.json 与线上规范一致
  • 域名分布(按路径首段统计实测):stock 34、chores 7、system 6、recipes 6、batteries 5、users 3、user 3、tasks 3、objects 2、calendar 2、userfields 1、files 1、print 1
  • 部署形态:Docker Compose(部署过程见《grocy 4.7.1 Docker Compose 部署指南》),数据经由《grocy 4.7.1 配置参数详解》所述默认配置
  • 客户端工具:curl 7.81.0(Ubuntu 22.04 自带);交互式调试可用 grocy 内建 Swagger UI(浏览器打开 http://<服务器>:8150/api,页面标题 Grocy API)

二、前置条件(端点、认证与客户端工具)

  • Base URL:http://<服务器IP>:8150/api(本机实测 http://localhost:8150/api;反向代理/子目录部署时为对应前缀 + /api)
  • 认证方式:请求头 GROCY-API-KEY: <key>。API Key 为 48 位随机串,在网页「用户设置 → API 密钥」创建;API 亦可复用浏览器登录会话 Cookie(见 4.2 节),但脚本集成一律用 API Key
  • 请求/响应格式:JSON(Content-Type: application/json);时间戳格式 YYYY-MM-DD HH:MM:SS;日期 YYYY-MM-DD
  • 工具:curl(本机)或 Swagger UI(自带,无需安装)

创建 API Key(浏览器路径:右上角用户菜单 → 用户设置 → API 密钥 → 添加)。本文实测所用 Key 存于本地文件以便引用:

curl -s -c /tmp/grocy-session.txt -o /dev/null -d "username=admin&password_base64=$(echo -n 'q1w2e3r4' | base64)" http://localhost:8150/login
curl -s -b /tmp/grocy-session.txt -o /dev/null -w "create_key=%{http_code}\n" http://localhost:8150/manageapikeys/new

实测回显:

create_key=302

💡 说明:/manageapikeys/new 是 Web 路由(会话鉴权)而非 /api 域端点,访问后立即创建一枚新 Key 并跳转回列表页;无 API 时的密钥列表查看在网页「用户设置 → API 密钥」。本文演示账号为 admin,密码 q1w2e3r4。

三、API 总览

3.1 协议与端点构成

  • 协议:HTTP REST,动词语义标准(GET 查 / POST 建 / PUT 改 / DELETE 删)
  • 规范:内建 OpenAPI 3.1.0 规范 + Swagger UI;规范文件同时随 release 包分发(grocy.openapi.json)
  • 版本:API 与主版本一致发布,无独立 API 版本号;新增端点见各版 Release Notes(如 4.7.1 新增 /stock/products/{productId}/copy,实查该版 Notes)

3.2 认证约定

  • 所有 /api/* 请求默认需要认证,凭据二选一:GROCY-API-KEY 请求头,或登录会话 Cookie
  • 无凭据或凭据无效统一返回 401(实测见 4.1 节)
  • 无限流约定(官方文档与源码均未实现速率限制),家庭内网使用无此顾虑

3.3 通用查询参数(/objects 系列端点)

OpenAPI 规范 components/parameters 实查原文:

参数说明(规范原文)示例
query[]过滤条件数组,每项形如 <field><condition><value>,条件含 =、!=、~(模糊)、≥ 等query[]=name~鲜
order排序字段,字段名:asc / 字段名:desc,省略方向默认 ascorder=id:asc
limit最大返回条数limit=2
offset跳过条数(配合 limit 分页)offset=2

四、认证与首次调用(curl 实测)

4.1 未认证请求 → 401

curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8150/api/system/info

实测回显:

401

4.2 会话方式调用(浏览器同源场景)

登录后持 Cookie 调 API,无需 Key:

curl -s -c /tmp/grocy-session.txt -o /dev/null -d "username=admin&password_base64=YWRtaW4=" http://localhost:8150/login
curl -s -b /tmp/grocy-session.txt http://localhost:8150/api/system/info

实测回显:

{"grocy_version":{"Version":"4.7.1","ReleaseDate":"2026-09-04"},"php_version":"8.5.12","sqlite_version":"3.53.4","db_version":257,"os":"Linux 6.8.0-138-generic #138~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug  7 13:43:15 UTC  x86_64","client":"curl\/7.81.0"}

⚠️ 注意:上例用默认密码 admin 登录仅为还原首次部署状态;正式使用请先改密(见部署篇),此后 password_base64 字段传新口令的 base64(本文演示口令 q1w2e3r4 的 base64 为 cTF3MjNyNA==)。

4.3 API Key 方式调用(推荐)

curl -s -H "GROCY-API-KEY: <你的APIKey>" http://localhost:8150/api/system/info

实测回显(与 4.2 节一致):

{"grocy_version":{"Version":"4.7.1","ReleaseDate":"2026-09-04"},"php_version":"8.5.12","sqlite_version":"3.53.4","db_version":257,...}

五、各 API 域详解

以下示例统一用变量 $K 表示 API Key(K=<key>; H="GROCY-API-KEY: $K"),响应为实测原文。

5.1 系统域(system,6 端点)

端点方法说明
/system/infoGET版本、运行时与数据库迁移状态
/system/timeGET服务器时间与时区
/system/db-changed-timeGET数据库最后变更时间(轮询同步用)
/system/configGET当前生效配置(48 键;LDAP/反代 7 个敏感键被隐藏)
/system/localization-stringsGET前端本地化字符串表
/system/log-missing-localizationPOST上报缺失翻译
curl -s -H "$H" http://localhost:8150/api/system/db-changed-time

实测回显:

{"changed_time":"2026-10-23 16:46:29"}

/system/config 的完整回读与键数分析见《grocy 4.7.1 配置参数详解》3.2 节。

5.2 通用对象域(objects,实体增删改查)

对所有业务实体(products、locations、quantity_units、shopping_lists、chores、tasks、batteries、users 等,实体白名单见规范 ExposedEntity 枚举)提供统一 CRUD:

端点方法说明
/objects/{entity}GET列表(支持 3.3 节通用参数)
/objects/{entity}POST新建,返回 {"created_object_id":<id>}
/objects/{entity}/{objectId}GET单条
/objects/{entity}/{objectId}PUT修改(204 无响应体)
/objects/{entity}/{objectId}DELETE删除(204)

新建产品(必需字段:name、location_id、qu_id_purchase、qu_id_stock):

curl -s -X POST -H "$H" -H "Content-Type: application/json" \
  -d '{"name":"牛奶","location_id":2,"qu_id_purchase":2,"qu_id_stock":2,"min_stock_amount":1,"default_best_before_days":7}' \
  http://localhost:8150/api/objects/products

实测回显:

{"created_object_id":"1"}

列表 + 过滤(模糊匹配名称含「鲜」):

curl -s -H "$H" "http://localhost:8150/api/objects/products?query[]=name~%E9%B2%9C"

实测回显(节选):

[{"id":1,"name":"\u9c9c\u725b\u5976","description":null,"product_group_id":null,"active":1,"location_id":2,"shopping_location_id":null,"qu_id_purchase":2,...}]

修改(PUT 后返回 204,需带全部必填字段):

curl -s -o /dev/null -w "put=%{http_code}\n" -X PUT -H "$H" -H "Content-Type: application/json" \
  -d '{"name":"鲜牛奶","location_id":2,"qu_id_purchase":2,"qu_id_stock":2,"min_stock_amount":2}' \
  http://localhost:8150/api/objects/products/1

实测回显:

put=204

再次单查确认生效:

curl -s -H "$H" http://localhost:8150/api/objects/products/1 | grep -o '"name":"[^"]*"' | head -1

实测回显(Unicode 转义即「鲜牛奶」):

"name":"\u9c9c\u725b\u5976"

错误行为实测:

  • 非法实体名 → 400:curl -s -o /dev/null -w "%{http_code}" -H "$H" http://localhost:8150/api/objects/notexist 实测回显 400
  • 缺必填字段 → 400 并带数据库级错误信息(实测原文):
{"error_message":"SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: products.location_id"}

5.3 库存域(stock,34 端点,核心域)

端点方法说明
/stockGET当前库存总览(含聚合金额、临期批次)
/stock/volatileGET临期/过期/缺货四类清单(due/overdue/expired/missing)
/stock/products/{productId}/addPOST入库(amount、best_before_date、price 可选)
/stock/products/{productId}/consumePOST消耗(FIFO 先到期先出;spoiled/specific_item 等可选)
/stock/products/{productId}/transferPOST库位间转移
/stock/products/{productId}/inventoryPOST盘点(按实际数校正)
/stock/products/{productId}/openPOST标记开封
/stock/products/{productId}/entriesGET该产品的库存批次明细
/stock/products/{productId}/price-historyGET价格历史
/stock/products/{productId}/copyGET复制产品(4.7.1 新增)
/stock/transactions/{transactionId}/undoPOST按事务号撤销(误操作回滚)
/stock/bookings/{bookingId}/undoPOST按单笔流水撤销
/stock/products/by-barcode/{barcode}/add 等POST按条码直接入库/消耗/转移/盘点/开封(扫码枪集成入口)

入库 2 件:

curl -s -X POST -H "$H" -H "Content-Type: application/json" \
  -d '{"amount":2,"best_before_date":"2026-10-30","price":5.5}' \
  http://localhost:8150/api/stock/products/1/add

实测回显(返回本次入库产生的库存批次):

[{"id":1,"product_id":1,"amount":2,"best_before_date":"2026-10-30","purchased_date":"2026-10-23","used_date":null,"spoiled":0,"stock_id":"6adb19013e499","transaction_type":"purchase","price":5.5,"undone":0,"undone_timestamp":null,"opened_date":null,"location_id":2,"recipe_id":null,"correlation_id":null,"transaction_id":"6adb19013e497","stock_row_id":null,"shopping_location_id":null,"user_id":1,"row_created_timestamp":"2026-10-23 16:21:21","note":null}]

再入一批临期(明天到期)并查 volatile:

curl -s -X POST -H "$H" -H "Content-Type: application/json" \
  -d '{"amount":1,"best_before_date":"2026-10-24","price":6.0}' \
  http://localhost:8150/api/stock/products/1/add > /dev/null
curl -s -H "$H" http://localhost:8150/api/stock/volatile

实测回显(结构化解析):

{"due_products":[("牛奶","2026-10-24",3)],"overdue_products":[],"expired_products":[],"missing_products":[]}

due_products 按产品聚合:最早到期日 2026-10-24、聚合总量 3(两批合计)。

消耗 1 件(返回负数流水,transaction_type=consume):

curl -s -X POST -H "$H" -H "Content-Type: application/json" -d '{"amount":1}' \
  http://localhost:8150/api/stock/products/1/consume

实测回显(节选):

[{"id":3,"product_id":1,"amount":-1,"best_before_date":"2026-10-24","purchased_date":"2026-10-23","used_date":"2026-10-23","spoiled":0,"stock_id":"6adb1ee17878c","transaction_type":"consume","price":6,"undone":0,...}]

批次明细与总量:

curl -s -H "$H" http://localhost:8150/api/stock/products/1/entries

实测回显(解析后):

[{"entry":"6adb19","amount":2,"bbd":"2026-10-30","loc":2}]

消耗遵循 FIFO:10-24 到期批次先被消耗,剩余 2 件为 10-30 批次。超量消耗报错实测(库存 2,请求消耗 99):

curl -s -X POST -H "$H" -H "Content-Type: application/json" -d '{"amount":99}' \
  http://localhost:8150/api/stock/products/1/consume -w "\nhttp=%{http_code}\n"

实测回显:

http=400

5.4 用户域(users/user,各 3 端点)

端点方法说明
/usersGET用户列表
/usersPOST新建用户(password_base64 传 base64 口令)
/users/{userId}PUT修改(必须同时带 username、first_name、last_name)
/users/{userId}DELETE删除
/users/{userId}/permissionsGET/POST查询/追加权限
/user/settings/{settingKey}GET/PUT用户级设置(如首页、显示偏好)

改密实测(q1w2e3r4 的 base64 为 cTF3MjNyNA==):

curl -s -o /dev/null -w "put=%{http_code}\n" -X PUT -H "$H" -H "Content-Type: application/json" \
  -d '{"username":"admin","first_name":"","last_name":"","password_base64":"cTF3MjNyNA=="}' \
  http://localhost:8150/api/users/1

实测回显:

put=204

缺字段的错误形态(实测原文):

{"error_message":"Grocy\\Services\\UsersService::EditUser(): Argument #3 ($firstName) must be of type string, null given, called in \/app\/www\/controllers\/Api\/UsersApiController.php on line 97","error_details":{"stack_trace":"#0 \/app\/www\/controllers\/Api\/UsersApiController.php(97): Grocy\\Services\\UsersService->EditUser()\n..."}}

5.5 日历、文件与其他域

  • 日历(2 端点):/calendar/ical?secret=<订阅密钥> 输出 iCal 全量订阅流(保质期/家务/任务聚合),secret 在网页日历页显示;4.7.1 修复了该端点子目录部署下 401 的问题(Release Notes 实查)
  • 文件(1 端点组):/files/{group}/{fileName},PUT 上传(Content-Type 决定存储类型)、GET 下载、DELETE 删除——产品图片即经此接口写入 group=productpictures
  • 任务/家务/电池/食谱域:模式与库存域一致(/chores/{choreId}/execute 执行家务、/batteries/{batteryId}/charge 记录充电、/recipes/{recipeId}/consume 按食谱扣库存等,均在本 74 端点清单内)
  • 打印(1 端点):/print/... 触发服务端打印视图

5.6 规范自检

curl -s -H "$H" http://localhost:8150/api/openapi/specification | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['openapi'], d['info']['title'], len(d['paths']))"

实测回显:

3.1.0 grocy REST API 74

六、分页、过滤与排序通用约定(汇总实测)

对 /objects/{entity} 列表端点组合验证四种参数(规范定义见 3.3 节):

curl -s -H "$H" "http://localhost:8150/api/objects/stock?limit=2&order=id%3Aasc"

实测回显(节选):

[{"id":1,"product_id":1,"amount":2,"best_before_date":"2026-10-30","purchased_date":"2026-10-23","stock_id":"6adb19013e4...

实测结论:limit/offset 控制窗口、order=字段:方向 排序、query[] 多条件叠加过滤(~ 为模糊匹配);query[] 需按数组形式编码(query[]=...),URL 中文值需百分号编码。

七、常见问题(FAQ)

Q1:请求一律 401? 检查三点:请求头拼写必须为 GROCY-API-KEY(全大写连字符格式);Key 是否属于有效用户(用户被删则 Key 失效);经反代访问且部署在子目录时,确认 grocy 版本 ≥ 4.7.1(旧版存在子目录下 API Key 认证失效的缺陷,4.7.1 Release Notes 明示已修复)。

Q2:响应中文变成 \uXXXX? grocy 按非转义策略输出 Unicode 转义,属正常 JSON;客户端 JSON 解析后即还原中文(本文回显中 \u9c9c\u725b\u5976 即「鲜牛奶」)。

Q3:PUT 返回 500 提示参数类型错误? PUT 端点要求请求体字段完整(见 5.4 节 users 实测),只传想改的字段会因缺字段触发类型错误;先 GET 单条再全量回传修改后的完整对象最稳妥。

Q4:误消耗了库存怎么撤销? 用入库/消耗响应中的 transaction_id 调 /stock/transactions/{transactionId}/undo,或用单笔流水的 id 调 /stock/bookings/{bookingId}/undo(端点清单实查),撤销生成反向流水而非物理删除。

Q5:在哪里交互式试 API? 浏览器打开 http://<服务器>:8150/api 即内建 Swagger UI(Authorize 按钮填 API Key 后可直接调用);规范 JSON 在 /api/openapi/specification。

Q6:能新建自定义实体吗? 可以。「用户字段/自定义实体」经 userfields 端点组配置后,出现在 /objects/{entity} 的实体白名单中(规范 ExposedEntity_IncludingUserEntities 枚举实查),与内置实体共用同一套 CRUD。

八、参考链接