# Cell V2 Public API

所有公开 Cell V2 接口统一使用 `/api/cell/v2/` 前缀，并接受现有 Feed API key。
本文档只描述公开请求和响应，不描述 Cell/Feed 内部通信。

## Dataset discovery

```http
GET /api/cell/v2/discovery/datasets
X-API-Key: <key>
```

返回 `protocol`、`datasets`、`count` 和 `time_unit:"ms"`。Dataset 条目不包含本地
文件路径。目录列出 Cell 当前仍可读取的 Published version；采用 current-only retention
的 Source Dataset 不承诺保留旧版本。需要查询当前版本时，使用 LLM 或 typed analytics
catalog 返回的实际 `data_version`。

当前批量目录可加法式包含 `social.posts`（金融社交帖子）和
`fund.valuation`（逐交易日证券估值），并可包含 `bjt06-cn-market.bars`（A 股
`1d/1h/5m` K 线）。具体列和可空性以 LLM/typed analytics catalog
返回的 schema 为准；`ts` 均为 epoch milliseconds，`timezone` 为对应 IANA timezone。

## LLM catalog

```http
GET /api/cell/v2/llm/catalog?datasets=us.bars
```

也可以使用 JSON body：

```http
POST /api/cell/v2/llm/catalog
Content-Type: application/json

{"datasets":["us.bars"]}
```

## LLM query

```http
POST /api/cell/v2/llm/query
Content-Type: application/json
X-Trace-ID: optional-client-trace-id

{
  "datasets": ["us.bars"],
  "data_version": "current",
  "sql": "SELECT * FROM \"us.bars\" LIMIT 10",
  "params": [],
  "max_rows": 1000,
  "timeout_ms": 30000
}
```

响应包含实际固定的数据版本、列、行、截断状态和 `trace_id`。

## News history

```http
POST /api/cell/v2/news/query
Content-Type: application/json
X-Trace-ID: optional-client-trace-id

{
  "start_ts": 1788134400000,
  "end_ts": 1788220800000,
  "instrument_id": "infoway:stock_us:AAPL.US",
  "limit": 100,
  "cursor": 0,
  "allow_partial": false
}
```

`start_ts` 包含、`end_ts` 不包含，均为 epoch milliseconds。`instrument_id` 可省略，
表示读取时间窗内全部新闻；`limit` 范围为 1–10000，默认 1000；`cursor` 是从 0 开始
的稳定结果偏移。响应包含按 `(published_at_ms, article_id)` 升序排列的 `articles`、
`count`、可空的 `next_cursor`、`partial`、`gaps`、`time_unit:"ms"` 和 `trace_id`。
若 `next_cursor` 非空，将它原样用于下一页。未覆盖完整时间窗且
`allow_partial:false` 时返回 409。

每条 article 至少包含稳定 `article_id`、`provider`、`title`、
`published_at_ms`、RFC 3339 UTC `published_at`、IANA `timezone`、
`instrument_ids` 与 `tags`；`body`、`summary`、`url`、`language` 和 `payload`
按源可空。`news.articles` 也可通过 LLM catalog/query 固定 Published version 后批量读取。
交易所披露与监管公告使用同一 article 契约，并通过 `provider`、`instrument_ids` 与
`tags` 区分来源和证券。

## Typed analytics

```http
POST /api/cell/v2/analytics/catalog
Content-Type: application/json

{"datasets":[{"dataset":"us.bars"}]}
```

```http
POST /api/cell/v2/analytics/query
Content-Type: application/json

{
  "datasets": [
    {
      "alias":"bars",
      "dataset":"us.bars",
      "version":"current",
      "scan":{"filters":[
        {"column":"symbol","op":"eq","parameter":"symbol"}
      ]}
    }
  ],
  "sql": "SELECT symbol, avg(close) AS avg_close FROM bars GROUP BY symbol",
  "parameters": {
    "symbol":{"type":"string","value":"NVDA_NASDAQ"}
  },
  "mode": "sync",
  "timeout_ms": 30000,
  "result": {"preferred_format":"json","max_rows":1000}
}
```

`scan.filters` 是可选的 Dataset binding 过滤条件。当前仅支持字符串参数的 `eq`；过滤
在 SQL 看到 `bars` relation 之前生效，适合 symbol 等稳定分区键。调用方仍可在外层 SQL
使用同一个 `$symbol` 参数，但不是必需。没有 `scan` 的既有请求保持原有全量 relation
语义。

响应 `stats` 加法式包含 `prepare_ms` 和 `index`。`index.used:true` 表示 Cell 用不可变
Published artifact 的安全索引缩小了文件集合；`files_total` / `files_selected` 展示缩减量。
索引不可用或不能证明某文件的精确值时自动完整扫描，不改变结果，也不修改 Published
数据。

## WebSocket

BATX canonical 行情使用独立的 audience-bound v1 路径：

```text
wss://feed.batx.ai/api/cell/v2/market/ws?ticket=<batx-market-ticket>
```

该路径只接受 typed ticket；订阅发送 canonical symbol，服务端 ACK 返回 provider 映射，
再以 `stream_id + symbol + channel + sequence` 提供 snapshot、event、quiet status、gap 与
replay。完整契约见 [DataGate WebSocket API](feed-websocket.md#batx-market-protocol-v1)。
下述 `/api/cell/v2/ws` 是既有 Kline/Edge market/news/Dataset 协议，两者不能混用。

连接：

```text
wss://feed.batx.ai/api/cell/v2/ws?ticket=<ticket>
```

连接成功：

```json
{"type":"connected","protocol":2,"plane":"v2"}
```

Kline snapshot + incremental：

```json
{"action":"subscribe","channel":"kline","symbols":["AAPL_NASDAQ"],"tf":"1d","snapshot":20}
```

自 2026-09-21 起 feed 为纯转发部署：`snapshot` 参数仍被接受，但返回的 snapshot 帧
固定为空 `rows`（帧结构不变），K 线自订阅起由实时成交增量生成；历史读取请使用
NAS/SQL 产品。

Market：

```json
{"action":"subscribe","channel":"market","instrument_ids":["infoway:stock_us:AAPL.US"]}
```

News：

```json
{"action":"subscribe","channel":"news","instrument_ids":["infoway:stock_us:AAPL.US"]}
```

Dataset 版本通知：

```json
{"action":"subscribe","channel":"dataset","datasets":["market.prices","macro.series"]}
```

每当 owning Cell 发布一个新的 immutable `data_version`，Feed 发送版本级通知，
不在 WebSocket 内推送整批行：

```json
{"type":"event","protocol":2,"data":{"channel":"dataset","dataset_id":"market.prices","data_version":"v20260831","contract_version":"market-prices-v1","cell_id":"market_batch","timezone":"Etc/UTC","time_unit":"ms","manifest_hash":"sha256:...","columns":[],"coverage_intervals":[],"gaps":[],"metadata":{},"updated_at_ms":1788134400000,"registered_at":"2026-08-31T00:00:00Z"}}
```

`datasets` 为空表示订阅全部 Dataset 版本通知。收到通知后，通过 discovery 获取
目录，再使用 REST/LLM query 固定 `data_version` 批量读取。

不传 news `instrument_ids` 表示订阅全部新闻。退订使用相同 frame 并将 `action`
改为 `unsubscribe`。心跳 frame 为 `{"action":"ping"}`。

### BATX authenticated market stream

BATX 浏览器使用独立的加法式 `/api/cell/v2/market/ws`，不改变上面的 Cell V2
`/api/cell/v2/ws`。它只接受 `/api/ws-ticket` 以
`batx-market-ticket-request/v1` + `audience:"batx-market-feed/v1"` 签发的单次票据；
不接受 API key query/header、legacy ticket 或跨 audience 重放。

该路径使用 `batx-market-feed/v1` 的 canonical subscribe/ACK、snapshot/event/status、
sequence、gap/replay 和 snapshot-resubscribe 合同。每个 instant 同时提供 epoch-ms、
RFC 3339 与市场 IANA timezone；完整帧见 [WebSocket API](feed-websocket.md)。

## HTTP errors

| HTTP | 含义 |
|---:|---|
| 401 | API key 无效或缺失 |
| 409 | Dataset、版本或能力不可用 |
| 422 | 请求格式或参数无效 |
| 503 | 当前数据服务断开或超时 |
