# 结果与句柄

一次调用没法在请求里答完时，不会让你一直等：它先返回 202 和一个结果句柄，之后用 GET /v1/results/{handle} 读这次调用的结果。读结果免费，可以反复读；只有发起调用的那把 Key 能读；结果在运行结束后保存 24 小时。通过 MCP 时，把同一个句柄填进工具的 handle 参数。运行结束前不扣积分，调用失败不扣。

- 页面：https://socialtoai.com/zh/docs/api/results/
- 最近更新：2026-10-17

- GET: `https://api.socialtoai.com/v1/results/{handle}`

## 什么时候会返回句柄

- **慢接口。** 少数接口本来就慢，它们一上来就返回句柄；接口自己的页面上写明了这一点。
- **等待到时了。** 其他调用最多等平台 40 秒。到时还没有结果，请求就返回句柄，调用在后面继续跑。
- **在后台运行的工具。** 报价里的 `quote.mode` 会写明。

这时返回的是 HTTP 202，带 `Retry-After` 响应头和下面这个结构。`status` 是 `running`，`billing.cost_credits` 是 0：运行结束前不扣积分。

**202 Accepted** (202, 0 积分)

```
{
  "request_id": "req_00000000-0000-4000-8000-000000000003",
  "schema_version": "v1",
  "status": "running",
  "handle": "res_00000000000040008000000000000003",
  "retry_after_seconds": 5,
  "billing": {
    "cost_credits": 0,
    "balance_credits": 100,
    "today_spend_credits": 0,
    "daily_limit_credits": null,
    "daily_remaining_credits": null,
    "display": "本次 0 积分，余额 100 积分"
  }
}
```

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `request_id` | string | 这次返回的编号，报告问题时带上它。 |
| `schema_version` | "v1" | 固定是 "v1"。 |
| `status` | "running" | 在这个返回里固定是 "running"。 |
| `handle` | string | 读结果用的句柄。 |
| `retry_after_seconds` | integer | 多少秒后值得来读。响应头 Retry-After 里是同一个数。 |
| `cells` | array | 请求里有多个平台或目标时：已经结束的那几份结果，和最终返回里的一样。（可能不出现） |
| `steps` | array | 工具运行时：已经走到的各个步骤。（可能不出现） |
| `billing.cost_credits` | integer | 这里固定为 0：运行结束前不扣积分。 |
| `billing.balance_credits` | integer | 当前余额。 |
| `billing.today_spend_credits` | integer | 今天（UTC）已经花掉的积分，这次运行暂时占用的部分也算在里面。 |
| `billing.daily_limit_credits` | integer \| null | 每日上限；没设时为 null。 |
| `billing.daily_remaining_credits` | integer \| null | 每日上限还剩多少；没设时为 null。 |
| `billing.display` | string | 一句可以直接复述给你的话。 |
| `waves.planned` | integer |  |
| `waves.done` | integer |  |
| `charge.quote_credits` | integer |  |
| `charge.charged_credits` | integer |  |

## 读结果

**cURL**

```
curl https://api.socialtoai.com/v1/results/res_00000000000040008000000000000003 \
  -H "Authorization: Bearer $SOCIALTOAI_KEY"
```

- **还在跑：** HTTP 202，结构和上面一样。等 `retry_after_seconds` 秒再读。
- **跑完了：** 就是那次调用本来会返回的内容，状态码也是它自己的：200 时是[外层结构](https://socialtoai.com/zh/docs/api/response.md)，失败时是[错误结构](https://socialtoai.com/zh/docs/api/errors.md) 和对应的状态码。`billing.cost_credits` 是这次运行扣的积分，余额是你读的这一刻的。
- **读结果免费。** 读多少次都行；运行只在结束时按平常的规则扣一次。
- **运行在我们这边出了问题：** HTTP 500 `internal_error`，没有扣费。

## 谁能读

只有发起这次调用的那把 Key。对其他 Key 来说（同一个账户下的另一把也一样），这个句柄不存在：HTTP 404 `not_found`。光有句柄打不开结果，读的时候总要带上 Key。你在 Playground 里发起的调用，在控制台里凭登录读取。

## 保存多久

结果在运行结束后保存 24 小时。过了这个时间，句柄返回 HTTP 410 `handle_expired`。想再要数据，就重新发起调用：那是一次新的调用，照常计费。

## 导出成文件

加上 `?format=csv` 或 `?format=ndjson`，把跑完的结果里的条目导出成文件：一张表，或每行一个 JSON 对象。运行还没结束时，不管选什么格式，返回的都是上面的 202 结构。`format` 填了别的值会被拒绝，错误代码是 `invalid_params`。

- CSV 的列是各行出现过的全部字段。嵌套字段写成 `a.b`，列表留在一个单元格里。
- 以 `=`、`+`、`-`、`@` 开头的单元格前面会加一个撇号，免得表格软件把它当公式执行。

**cURL**

```
curl "https://api.socialtoai.com/v1/results/res_00000000000040008000000000000003?format=csv" \
  -H "Authorization: Bearer $SOCIALTOAI_KEY" -o result.csv
```

## 通过 MCP

13 个计费工具都多一个参数 `handle`。带了 `handle` 的调用只读那个结果：别的什么都不跑，不扣积分，其他参数也不看。所以工具返回 `status: "running"` 时，你的 AI 带着句柄再调一次同一个工具就行。

### 很大的结果：more.handle

MCP 的一次返回最多放大约 60 KB 的 JSON（约 2 万 token），免得占满模型的上下文。更大的结果先给前面的若干条，外加一个 `more` 对象：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `more.handle` | string | 带着接续位置的句柄，写成“句柄.位置”。拿它再调同一个工具，取后面的条目。 |
| `more.rows_left` | integer | 还剩多少条没给。 |
| `more.note` | string | 给 AI 看的一句话：怎么接着读。 |

接着读是免费的，读到返回里没有 `more` 为止。走 HTTP 时用同一个句柄（不带位置），一次读到完整结果。

## 同一个请求再发一次

有了句柄，`Idempotency-Key` 的用法不变。运行还没结束时，同一个键加同样的参数会被拒绝（HTTP 409）；运行结束后，它返回第一次的结果，不再扣费。
