ActivityWatch 0.14.1 API 使用详解

REST API 认证与信息/桶/事件/查询四大域端点全实测:心跳写入、聚合查询、导出备份与错误行为逐条照录。

ActivityWatchActivityWatch时间追踪API2026/10/23

ActivityWatch 0.14.1 API 使用详解

目标:系统讲清 ActivityWatch 0.14.1 的 REST API——认证方式、信息/桶/事件/查询四大域的端点、参数与错误行为,让读者能用 curl 或任意 HTTP 客户端完成"查状态、建桶、写心跳、聚合查询、导出备份"的全部运维动作。所有请求与响应均在 Ubuntu 22.04(22.04.5 LTS)+ aw-server v0.14.1(Python 版)实测采集,逐条照录。

一、环境信息

  • 操作系统:Ubuntu 22.04(22.04.5 LTS,x86_64)
  • 服务端:aw-server v0.14.1(Python/Flask 实现,API 基地址 http://localhost:5600/api/0)
  • 认证:示例开启 [server.auth] api_key = "q1w2e3r4"(演示口令,生产请换强随机值;开启方式与生效行为见《ActivityWatch 0.14.1 配置参数详解》5.6)
  • 客户端工具:系统 curl(本章唯一依赖,无需 SDK)
  • 测试数据:桶 test-bucket(type=test),内含 3 条 not-afk/cli-test 心跳事件(创建过程见第五章)
  • 版本查询日期:2026-10-22(v0.14.1 为官方最新稳定版)

二、前置条件

  • aw-server 运行中(systemd 托管),本机探活通过:
systemctl is-active aw-server
curl -s http://localhost:5600/api/0/info
active
{"hostname": "aw-server01", "version": "v0.14.1", "testing": false, "device_id": "a34e4cfc-7992-4c03-a7a2-7422da090665", "profile": "default"}
  • 若服务端启用了 api_key,准备好鉴权头变量(本文后续示例统一用它):
H="Authorization: Bearer q1w2e3r4"
  • 理解三个名词:bucket(数据桶,按采集来源划分的存储分区)、event(事件,时间戳+时长+data JSON)、canonical query(服务器内置的查询语言,在 /api/0/query/ 端点执行)。三者关系见《ActivityWatch 0.14.1 介绍与选型指南》第三章。

三、API 总览

项说明
协议与风格HTTP REST,请求/响应均为 JSON(Content-Type: application/json)
版本路径/api/0/(当前唯一版本前缀;另有旧版 /api/ 兼容路径,不建议新代码使用)
认证默认无认证;在 [server.auth] 设置 api_key 后,除 GET /api/0/info 与 OPTIONS 预检外全部要求 Authorization: Bearer <key>
限流约定无官方限流参数;watcher 心跳靠客户端合并(pulsetime)控制写入量,查询侧靠 query_cache 缓解重复计算
典型错误形态校验失败返回 400 + {"errors": {...}, "message": "Input payload validation failed"};认证失败 401 + 提示 JSON;资源不存在 404 + 提示 JSON
Web UI与 API 同端口:http://localhost:5600/ 托管 aw-webui 单页应用

四大域端点速查(均在本章/第五章实测):

域端点方法
信息/api/0/infoGET
桶/api/0/buckets/GET
桶/api/0/buckets/<id>GET / POST / DELETE(?force=1)
桶/api/0/buckets/<id>/exportGET
事件/api/0/buckets/<id>/heartbeat?pulsetime=POST
事件/api/0/buckets/<id>/eventsGET
事件/api/0/buckets/<id>/events/countGET
查询/api/0/query/POST

四、认证与首次调用

开启 api_key 后的首次调用实测。不带认证头访问受保护端点:

curl -s http://localhost:5600/api/0/buckets/
{"message": "Missing or invalid API key. Set Authorization: Bearer <key> header."}
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:5600/api/0/buckets/
401

带正确认证头重试(本文演示口令 q1w2e3r4):

curl -s -H "Authorization: Bearer q1w2e3r4" http://localhost:5600/api/0/buckets/
{"test-bucket": {"id": "test-bucket", "created": "2026-10-22T16:14:56.717943+00:00", "name": null, "type": "test", "client": "aw-client-test", "hostname": "aw-server01", "data": {}, "last_updated": "2026-10-22T16:18:24.658000+00:00"}, "aw-watcher-afk_aw-server01": {"id": "aw-watcher-afk_aw-server01", "created": "2026-10-22T16:19:07.214904+00:00", "name": null, "type": "afkstatus", "client": "aw-watcher-afk", "hostname": "aw-server01", "data": {}}}

错误口令同样 401(实测 -H "Authorization: Bearer wrongkey" → 401,响应体同上提示 JSON)。信息端点恒为公开,无需认证(实测 200):

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

五、各 API 域详解

5.1 信息域:GET /api/0/info

服务器健康检查与版本探测的标准端点(监控脚本探活用这个)。无参数:

curl -s -H "$H" http://localhost:5600/api/0/info
{"hostname": "aw-server01", "version": "v0.14.1", "testing": false, "device_id": "a34e4cfc-7992-4c03-a7a2-7422da090665", "profile": "default"}

字段解读:hostname 服务器主机名;version 服务器版本(Rust 实现带 (rust) 后缀,实测 Rust 版返回 "v0.14.1 (rust)");testing 是否 testing 实例;device_id 设备唯一标识(随数据目录生成,换库即变);profile 实例 profile 名(默认 default)。

5.2 桶域:/api/0/buckets

5.2.1 列出全部桶(GET /api/0/buckets/):见第四章回显,键为桶 ID,值含 created/type/client/hostname/data/last_updated。type 是桶的数据语义(如 afkstatus、currentwindow、浏览器扩展的 web.tab.current),做查询过滤时的依据。

5.2.2 查看单个桶(GET /api/0/buckets/<id>):

curl -s -H "$H" http://localhost:5600/api/0/buckets/test-bucket
{"id": "test-bucket", "created": "2026-10-22T16:14:56.717943+00:00", "name": null, "type": "test", "client": "aw-client-test", "hostname": "aw-server01", "data": {}}

5.2.3 创建桶(POST /api/0/buckets/<id>):请求体需 client/type/hostname 三个字段:

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"client": "aw-client-test", "type": "test", "hostname": "aw-server01"}' http://localhost:5600/api/0/buckets/test-bucket
{}

成功返回空 JSON {};创建后立即可在列表中看到(见 4 章)。重复创建同 ID 桶返回同为空 JSON(幂等)。

5.2.4 删除桶(DELETE /api/0/buckets/<id>?force=1):带保护机制——非 testing 模式必须加 ?force=1,否则拒绝。实测不带 force 的拒绝回显(注意状态码仍是 401):

curl -s -H "$H" -X DELETE http://localhost:5600/api/0/buckets/todel
{"message": "Deleting buckets is only permitted if aw-server is running in testing mode or if ?force=1"}

加 force 后真删(实测删除桶 todel,返回空 JSON,列表复查确认消失):

curl -s -H "$H" -X DELETE "http://localhost:5600/api/0/buckets/todel?force=1"
{}
curl -s -H "$H" http://localhost:5600/api/0/buckets/ | python3 -c "import sys,json; print(sorted(json.load(sys.stdin).keys()))"
['aw-watcher-afk_aw-server01', 'test-bucket']

⚠️ 注意:删除桶会连带删除其全部事件且不可恢复,生产上删除前先用 5.2.5 的导出接口备份。

5.2.5 导出桶(GET /api/0/buckets/<id>/export):桶定义 + 全部事件的 JSON(实测节选):

curl -s -H "$H" http://localhost:5600/api/0/buckets/test-bucket/export
{"buckets": {"test-bucket": {"id": "test-bucket", "created": "2026-10-22T16:14:56.717943+00:00", "name": null, "type": "test", "client": "aw-client-test", "hostname": "aw-server01", "data": {}, "events": [{"timestamp": "2026-10-22T10:05:00+00:00", "duration": 0.0, "data": {"status": "not-afk"}}, {"timestamp": "2026-10-22T10:00:00+00:00", "duration": 30.0, "data": {"status": "not-afk"}}]}}}

导出文件可直接作为备份;/api/0/buckets/export(不带桶 ID)导出全部桶。

5.3 事件域:心跳与读取

5.3.1 写入心跳(POST /api/0/buckets/<id>/heartbeat?pulsetime=):心跳是"某状态持续中"的信号;pulsetime(秒)声明本心跳的有效窗口,窗口内到达的同 data 心跳会与之前的事件合并(时长累加):

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"timestamp": "2026-10-22T10:00:00+00:00", "duration": 0, "data": {"status": "not-afk"}}' "http://localhost:5600/api/0/buckets/test-bucket/heartbeat?pulsetime=120"
{"id": 1, "timestamp": "2026-10-22T10:00:00+00:00", "duration": 0.0, "data": {"status": "not-afk"}}

合并行为实测:再发两条时间差 30 秒与 4.5 分钟的心跳(pulsetime 均为 120),随后查询聚合得到 duration 合计 30.0(相邻合并)与 merge_events_by_keys 合并后总时长 30.0(回显见 5.4.3)。客户端时间戳用 ISO 8601 带时区格式。

5.3.2 读事件(GET /api/0/buckets/<id>/events):

curl -s -H "$H" "http://localhost:5600/api/0/buckets/test-bucket/events?limit=5"
[{"id": 3, "timestamp": "2026-10-22T16:18:24.658000+00:00", "duration": 0.0, "data": {"status": "cli-test"}}, ...]

支持参数(见第六章约定):limit、starttime、endtime、orderby。过滤实测(限定起点后只返回 1 条且配了 limit=1):

curl -s -H "$H" "http://localhost:5600/api/0/buckets/test-bucket/events?starttime=2026-10-22T10%3A00%3A30%2B00%3A00&limit=1"
[{"id": 3, "timestamp": "2026-10-22T16:18:24.658000+00:00", "duration": 0.0, "data": {"status": "cli-test"}}]

5.3.3 事件计数(GET /api/0/buckets/<id>/events/count):

curl -s -H "$H" http://localhost:5600/api/0/buckets/test-bucket/events/count
3

5.4 查询域:POST /api/0/query/

查询域是 ActivityWatch 的分析核心:POST 一个 JSON,query 数组里是一段 canonical query 脚本,对 timeperiods 里每个时段各执行一次。

5.4.1 请求体字段

字段类型必填说明
querystring 数组是查询脚本(多行合成一段执行,最后 RETURN 结果)
timeperiods"start/end" 字符串数组是每个元素一个 ISO 8601 时段
namestring否查询命名(配 cache 用)
cachebool否是否使用服务端查询缓存

5.4.2 最小查询实测:

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"query": ["events = query_bucket(\"test-bucket\"); RETURN = events;"], "timeperiods": ["2026-10-22T00:00:00+00:00/2026-10-23T00:00:00+00:00"]}' http://localhost:5600/api/0/query/
[[{"data":{"status":"not-afk"},"duration":0.0,"id":1,"timestamp":"2026-10-22T10:00:00+00:00"}]]

返回值为数组的数组:外层对应 timeperiods(1 个时段),内层为 RETURN 的值。

5.4.3 聚合算子实测(merge_events_by_keys 按 data.key 合并相邻事件;sum_durations 求时长和):

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"timeperiods": ["2026-10-22T00:00:00+00:00/2026-10-23T00:00:00+00:00"], "query": ["events = query_bucket(\"test-bucket\"); merged = merge_events_by_keys(events, [\"status\"]); dur = sum_durations(events); RETURN = [merged, dur];"]}' http://localhost:5600/api/0/query/
[[[{"data":{"status":"not-afk"},"duration":30.0,"id":null,"timestamp":"2026-10-22T10:05:00+00:00"}],30.0]]

解读:内层 [merged, dur] = 合并后的事件(3 条相邻 not-afk 合成 1 条 30 秒)+ 总时长 30.0。带 name/cache 的调用实测(返回 [30.0],与上面 dur 一致):

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"query": ["events = query_bucket(\"test-bucket\"); RETURN = sum_durations(events);"], "timeperiods": ["2026-10-22T00:00:00+00:00/2026-10-23T00:00:00+00:00"], "name": "sum-test", "cache": false}' http://localhost:5600/api/0/query/
[30.0]

5.4.4 错误行为实测(两类高频错误):

缺 timeperiods → 400 校验错误:

curl -s -H "$H" -X POST -H "Content-Type: application/json" -d '{"query": ["events = query_bucket(\"test-bucket\"); RETURN = events;"]}' http://localhost:5600/api/0/query/
{"errors": {"timeperiods": "'timeperiods' is a required property"}, "message": "Input payload validation failed"}

脚本内语法/参数错误 → QueryParseException(实测 query_bucket 用了 limit=1 命名参数写法):

{"type": "QueryParseException", "message": "Expected , between arguments to query_bucket, got: =1"}

结论:query_bucket 的 limit 要用位置参数 query_bucket("<bucket>", 1),不能写 limit=1。常用算子速览:query_bucket(id, limit?) 取桶事件、filter_period_intersect 交集(如窗口事件剔除 AFK)、merge_events_by_keys(events, keys) 合并、sum_durations(events) 求和、concat_events_by_data 拼接——完整函数表见官方查询文档(参考链接)。

六、分页/过滤/排序通用约定

  • 事件读取参数(GET /api/0/buckets/<id>/events):limit(最大返回条数)、starttime/endtime(ISO 8601 时间过滤,URL 编码 + 为 %2B)、orderby(排序键)、orderby_value(配合 orderby 的取值);不传 limit 时返回时段内全部事件,大桶务必带 limit(实测见 5.3.2);
  • 时间一律 ISO 8601 带时区(如 2026-10-22T10:00:00+00:00);省略时区的裸时间在不同端点行为不一致,统一带时区最稳;
  • 查询域无分页:timeperiods 切时段即天然"分批",需要大范围统计时按天/周拆时段循环调用;
  • 心跳去重:同 data 的连续心跳在 pulsetime 窗口内自动合并,调用方无需自己去重;窗口外到达的心跳生成新事件(5.3.1 实测:间隔 4.5 分钟的两条在 pulsetime=120 下生成了独立事件)。

七、常见问题(FAQ)

Q1:所有请求都返回 Missing or invalid API key?

服务端开了 api_key 而请求没带认证头(或口令不对)。补 -H "Authorization: Bearer <key>";只有 /api/0/info 与 OPTIONS 预检豁免。注意 5.2.4 的"删除未加 force=1"也以 401 形态返回,看响应体区分。

Q2:DELETE 桶返回 401 但响应体说 testing/force?

这是桶删除保护:非 testing 实例必须 ?force=1(实测回显见 5.2.4)。force 删除不可恢复,先 export。

Q3:timeperiods 到底怎么写?

"起/止" 的 ISO 8601 区间字符串,放进数组可一次给多个时段(如整周按天 7 个),响应外层数组与时段一一对应。实测缺失该字段返回 400 校验错误(5.4.4)。

Q4:查询脚本里怎么传桶 ID/字符串?

canonical query 里字符串用双引号;经 curl -d 传 JSON 时需转义为 \"(5.4.2 命令示例即此形态)。脚本语法错误返回 QueryParseException,按 message 定位。

Q5:有没有官方 Python/JS 客户端?

有:aw-client(Python,pip 包,其 CLI 六个子命令实测见《ActivityWatch 0.14.1 常用 CLI 命令与工具详解》第五章);浏览器扩展与 webui 走同一套 REST API。第三方语言按本文端点自封装即可——API 本身极简(增删查 + query)。

八、参考链接