grocy 4.7.1 API 使用详解
grocy 4.7.1 REST API 使用详解:API Key 认证、系统/对象/库存/用户/文件等 74 个端点分域实测(curl 真实回显),错误码、增删改查与库存流水闭环,基于内建 OpenAPI 3.1 规范。
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_version257) - API 规范版本:OpenAPI 3.1.0(
/api/openapi/specification实查),共 74 个端点路径,随包分发的grocy.openapi.json与线上规范一致 - 域名分布(按路径首段统计实测):
stock34、chores7、system6、recipes6、batteries5、users3、user3、tasks3、objects2、calendar2、userfields1、files1、print1 - 部署形态: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,省略方向默认 asc | order=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/info | GET | 版本、运行时与数据库迁移状态 |
/system/time | GET | 服务器时间与时区 |
/system/db-changed-time | GET | 数据库最后变更时间(轮询同步用) |
/system/config | GET | 当前生效配置(48 键;LDAP/反代 7 个敏感键被隐藏) |
/system/localization-strings | GET | 前端本地化字符串表 |
/system/log-missing-localization | POST | 上报缺失翻译 |
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 端点,核心域)
| 端点 | 方法 | 说明 |
|---|---|---|
/stock | GET | 当前库存总览(含聚合金额、临期批次) |
/stock/volatile | GET | 临期/过期/缺货四类清单(due/overdue/expired/missing) |
/stock/products/{productId}/add | POST | 入库(amount、best_before_date、price 可选) |
/stock/products/{productId}/consume | POST | 消耗(FIFO 先到期先出;spoiled/specific_item 等可选) |
/stock/products/{productId}/transfer | POST | 库位间转移 |
/stock/products/{productId}/inventory | POST | 盘点(按实际数校正) |
/stock/products/{productId}/open | POST | 标记开封 |
/stock/products/{productId}/entries | GET | 该产品的库存批次明细 |
/stock/products/{productId}/price-history | GET | 价格历史 |
/stock/products/{productId}/copy | GET | 复制产品(4.7.1 新增) |
/stock/transactions/{transactionId}/undo | POST | 按事务号撤销(误操作回滚) |
/stock/bookings/{bookingId}/undo | POST | 按单笔流水撤销 |
/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 端点)
| 端点 | 方法 | 说明 |
|---|---|---|
/users | GET | 用户列表 |
/users | POST | 新建用户(password_base64 传 base64 口令) |
/users/{userId} | PUT | 修改(必须同时带 username、first_name、last_name) |
/users/{userId} | DELETE | 删除 |
/users/{userId}/permissions | GET/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。
八、参考链接
- 内建 Swagger UI 与规范:<http://<你的grocy地址>/api>、
/api/openapi/specification - 官方 API 文档入口(README「REST API」节):https://github.com/grocy/grocy#rest-api
- 规范文件随包路径:release 包根目录
grocy.openapi.json(与线上规范同源) - 本专栏部署篇:《grocy 4.7.1 Docker Compose 部署指南》、《Ubuntu 22.04 安装 grocy 4.7.1 指南》
- 配置键(含
/system/config暴露范围):《grocy 4.7.1 配置参数详解》 - 项目定位与版本节奏:《grocy 4.7.1 介绍与选型指南》