Mindframe API
Mindframe · 开放接口

量表开放接口 v1

用一条 API Key 接入 Mindframe 的标准化心理量表: 拉取题目与选项、提交作答、拿到总分、分量表分、分级, 以及危机预警标记。

标准化量表

PHQ-9 抑郁(公共领域)与 ESSA 学业压力(已获授权),题目与分级均来自公开发表的原始量表。

一行请求计分

POST 一组作答,直接返回总分、分级、分量表得分。评分规则由服务端统一维护,不会出现版本分叉。

危机预警内置

PHQ-9 第 9 题(自杀意念)答非零时,响应会带上 crisis.flagged 与求助资源,便于你及时干预。

基本地址 https://api.mindframe.top
所有接口均为 HTTPS,返回 application/json; charset=utf-8。
重要声明 本接口仅供研究与初筛用途,不构成医学诊断,不能替代执业医师的临床评估。 任何基于分数的判断都应由具备资质的人员复核。

快速开始

1

获取 API Key

联系 Mindframe 管理员,提供用途名称与使用方,管理员会在后台生成一条 Key。 Key 形如 mfk_ 开头 + 64 位十六进制。

明文 Key 只在生成时显示一次。请安全保管,不要提交到公开仓库或前端代码里。
2

调一次试试

curl https://api.mindframe.top/v1/scales \
  -H "X-API-Key: mfk_你的密钥"

返回可用量表列表。看到 "ok": true 就说明鉴权成功了。

3

取题目 → 收集作答 → 提交计分

# 取 PHQ-9 的题目与选项
curl https://api.mindframe.top/v1/scales/phq9 \
  -H "X-API-Key: mfk_你的密钥"

# 提交 9 题的作答(0=完全没有 … 3=几乎每天)
curl -X POST https://api.mindframe.top/v1/scales/phq9/score \
  -H "X-API-Key: mfk_你的密钥" \
  -H "Content-Type: application/json" \
  -d '{"answers":[1,1,2,1,1,2,1,0,0]}'

响应里的 score.total 是总分,band 是分级,crisis.flagged 是危机标记。

鉴权

每个请求都要带上 API Key。支持两种等价的携带方式,任选其一:

方式请求头说明
推荐X-API-Key: mfk_…语义清晰,不与 HTTP 标准认证混用
标准Authorization: Bearer mfk_…适合已有 Bearer Token 体系的系统

权限范围(scopes)

Key 可以只授予部分权限。管理员生成时会指定:

Scope允许的操作
read读取量表列表与量表详情(题目、选项、分级标准)
score提交作答并获取分数(含危机标记)

缺少所需 scope 时返回 403 insufficient_scope。

安全建议

  • Key 只放在服务端环境变量里,绝不要写进浏览器 JS 或移动端安装包。
  • 不同系统用不同 Key,便于单独停用与统计用量。
  • 怀疑泄露时立即联系管理员停用并重新签发。

接口参考

GET /v1/scales scope: read

列出全部可计分的量表(不含题目正文,响应体较小)。

请求示例

curl https://api.mindframe.top/v1/scales \
  -H "X-API-Key: mfk_你的密钥"

响应示例 200

{
  "ok": true,
  "count": 2,
  "scales": [
    {
      "id": "phq9",
      "name": "PHQ-9",
      "item_count": 9,
      "score_range": [0, 27],
      "window": "过去两周",
      "stage": 1,
      "time_minutes": 2,
      "bands": [
        { "key": "minimal",  "label": "无或极轻", "min": 0,  "max": 4  },
        { "key": "mild",     "label": "轻度",     "min": 5,  "max": 9  },
        { "key": "moderate", "label": "中度",     "min": 10, "max": 14 },
        { "key": "moderately_severe", "label": "中重度", "min": 15, "max": 19 },
        { "key": "severe",   "label": "重度",     "min": 20, "max": 27 }
      ]
    },
    {
      "id": "essa",
      "name": "ESSA",
      "item_count": 6,
      "score_range": [6, 30],
      "window": "通常情况",
      "bands": [
        { "key": "low",       "label": "低",   "min": 6,  "max": 12 },
        { "key": "moderate",  "label": "中",   "min": 13, "max": 19 },
        { "key": "high",      "label": "高",   "min": 20, "max": 25 },
        { "key": "very_high", "label": "很高", "min": 26, "max": 30 }
      ]
    }
  ],
  "scale_set": "core15",
  "disclaimer": "仅供研究与初筛用途,不构成医学诊断,不能替代执业医师的临床评估。",
  "docs": "https://docs.mindframe.top/",
  "time": 1792305145
}
GET /v1/scales/{id} scope: read

取单个量表的完整定义:题目、选项、分量表、分级区间、文献引用。

路径参数

参数类型说明
idstring量表标识,如 phq9、essa。可从列表接口获取。

查询参数

参数默认说明
langzh题目语言:zh 中文 / en 英文

响应示例 200

{
  "ok": true,
  "scale": {
    "id": "phq9",
    "name": "PHQ-9",
    "name_zh": "PHQ-9",
    "name_en": "PHQ-9",
    "window": "过去两周",
    "item_count": 9,
    "score_min": 0,
    "score_max": 27,
    "options": [
      { "value": 0, "label": "完全没有" },
      { "value": 1, "label": "有几天" },
      { "value": 2, "label": "一半以上天数" },
      { "value": 3, "label": "几乎每天" }
    ],
    "items": [
      { "index": 0, "text": "做事时提不起劲或没有兴趣", "reverse": false, "crisis": false },
      { "index": 1, "text": "感到心情低落、沮丧或绝望",   "reverse": false, "crisis": false }
    ],
    "subscales": {
      "emotional": { "label": "情绪/认知", "items": [0,1,5,6,7], "min": 0, "max": 15 },
      "somatic":   { "label": "躯体",     "items": [2,3,4],     "min": 0, "max": 9  }
    },
    "bands": [ ],
    "citation": "Kroenke, K., Spitzer, R. L., & Williams, J. B. W. (2001)…",
    "licence": "public_domain",
    "stage": 1,
    "time_minutes": 2
  },
  "disclaimer": "仅供研究与初筛用途…",
  "time": 1792305038
}
提示 接口返回的 items 顺序就是 answers 数组的下标顺序。 按顺序收集作答即可,不需要自己维护题号映射。
POST /v1/scales/{id}/score scope: score

提交一份完整作答,返回总分、分级、分量表分、危机标记。

请求体

字段类型必填说明
answersarray / object是 逐题作答。数组按 items[].index 顺序;也可用 {"0":1,"1":2} 形式显式指定下标。
langstring否 响应中标签语言,zh / en,默认 zh
必须答满 所有题目都要有作答,缺题返回 422 incomplete_answers 并列出缺失的题号。 取值必须是该量表 options[].value 中的值,否则返回 422 invalid_answers。

请求示例

curl -X POST https://api.mindframe.top/v1/scales/phq9/score \
  -H "X-API-Key: mfk_你的密钥" \
  -H "Content-Type: application/json" \
  -d '{"answers":[1,1,2,1,1,2,1,0,0]}'

响应示例 200

{
  "ok": true,
  "scale": { "id": "phq9", "name": "PHQ-9" },
  "score": {
    "total": 9,
    "min": 0,
    "max": 27,
    "percent": 33.3
  },
  "band": { "key": "mild", "label": "轻度", "range": [5, 9] },
  "subscales": {
    "emotional": { "label": "情绪/认知", "score": 5, "min": 0, "max": 15 },
    "somatic":   { "label": "躯体",     "score": 4, "min": 0, "max": 9  }
  },
  "items": [
    { "index": 0, "value": 1, "scored": 1 },
    { "index": 1, "value": 1, "scored": 1 }
  ],
  "crisis": { "flagged": false, "items": [] },
  "key": { "id": 4, "name": "验证用 Key" },
  "disclaimer": "仅供研究与初筛用途…",
  "time": 1792305145
}

字段说明

字段说明
score.total总分(已按量表规则处理反向计分)
score.percent在量表可能区间内的相对位置,便于跨量表比较,不是百分位排名
band分级结果;量表无官方分界值时该字段为 null
subscales分量表得分与取值范围;无分量表时为空对象
items[].scored该题在计分方向上的实际值(反向题已折算)
crisis.flagged是否触发危机预警,见 危机预警
GET /health 无需鉴权

探活接口。可用于监控与上线自检,不需要 API Key。

{
  "ok": true,
  "service": "mindframe-api",
  "version": "2.1.0",
  "generation": "v2",
  "env": "production",
  "php": "8.4.6",
  "sapi": "fpm-fcgi",
  "routes": { "total": 6, "live": 6, "planned": 0 },
  "retention_lock_hours": 12,
  "time": 1792305145
}

可用量表

PHQ-9 抑郁症状

9 题 · 0–27 分 · 约 2 分钟

患者健康问卷抑郁量表,Kroenke 等(2001)。评估过去两周的抑郁症状频率。 本题库使用完整 9 题,含第 9 题自杀意念筛查。

计分9 题得分相加,0–27
分级0–4 无/极轻 · 5–9 轻度 · 10–14 中度 · 15–19 中重度 · 20–27 重度
分量表情绪/认知(5 题)、躯体(3 题)—— 探索性,不作独立构念报告
许可公共领域(Pfizer 已豁免版权)
Kroenke, K., Spitzer, R. L., & Williams, J. B. W. (2001). The PHQ-9: Validity of a brief depression severity measure. Journal of General Internal Medicine, 16(9), 606–613.

ESSA 学业压力

6 题 · 6–30 分 · 约 2 分钟

教育压力量表( adolescents),Sun 等(2011)。本题库使用其中区分度最高的 6 题子集, 覆盖同辈竞争、家庭期望、自我期望三个压力来源。

计分6 题得分相加,6–30(每题 1–5)
分级6–12 低 · 13–19 中 · 20–25 高 · 26–30 很高 (研究者自定,非原文分界值)
分量表同辈竞争、家庭期望、自我期望(各 2 题)
许可已取得书面授权;须保留完整文献引用
Sun, J., Dunne, M. P., Hou, X.-Y., & Xu, A.-Q. (2011). Educational stress scale for adolescents: Development, validity, and reliability with Chinese students. Journal of Psychoeducational Assessment, 29(6), 534–546.
关于量表集合 当前部署的量表集合为 core15(PHQ-9 与 ESSA 合计 15 题)。 后续若调整集合,列表接口的 scale_set 字段会同步变化,请以接口返回为准,不要硬编码。

计分与分级

所有计分都在服务端完成,调用方只需提交原始作答值。 这样做的原因很实际:量表规则(反向计分、分量表归属、分界值)一旦在客户端重写一份, 迟早会出现「网页算 9 分、接口算 7 分」这类无法追查的问题。

反向计分

个别题目需要反向折算(items[].reverse 为 true)。 折算公式为 min + max − value,由服务端处理, 你提交的永远是该选项的原始值。

分级的解读边界

  • PHQ-9 的 0–27 分界值是公开发表的临床分级,可直接引用。
  • ESSA 6 题子集的分级是研究者自定,不对应原量表分界值;论文中应报告连续分数与三维度分。
  • percent 只表示「在量表可能区间中的位置」,不是人群百分位。
  • 分量表分数为探索性描述,不建议单独作为构念报告。

危机预警

PHQ-9 第 9 题询问自杀/自伤意念(items[8].crisis = true)。 该题不计入总分,但只要答非「完全没有」, 响应中的 crisis.flagged 就会变成 true,并附带求助资源。

{
  "score": { "total": 12, "max": 27 },
  "band": { "key": "moderate", "label": "中度" },
  "crisis": {
    "flagged": true,
    "items": [8],
    "message": "有作答指向潜在风险,请尽快向作答者展示求助资源。",
    "resources": [
      { "name": "急救", "contact": "120" },
      { "name": "北京心理危机研究与干预中心", "contact": "800-810-1117 / 010-8295-1332" },
      { "name": "全国统一心理援助热线", "contact": "12356" }
    ]
  }
}
接入方必须做的事 不要只把分数存进数据库就结束。crisis.flagged 为 true 时, 必须在用户可见的界面上即时展示求助资源,并(如适用)触发人工回访流程。 这是使用本接口的前提条件。

代码示例

Python

import os, requests

API = "https://api.mindframe.top"
HEADERS = {
    "X-API-Key": os.environ["MINDFRAME_API_KEY"],
    "Content-Type": "application/json",
}

def get_scale(scale_id: str) -> dict:
    r = requests.get(f"{API}/v1/scales/{scale_id}", headers=HEADERS, timeout=15)
    r.raise_for_status()
    return r.json()["scale"]

def score(scale_id: str, answers: list[int]) -> dict:
    r = requests.post(f"{API}/v1/scales/{scale_id}/score",
                      headers=HEADERS, json={"answers": answers}, timeout=15)
    r.raise_for_status()
    return r.json()

# 用法
scale = get_scale("phq9")
print("题目数:", scale["item_count"])

result = score("phq9", [1, 1, 2, 1, 1, 2, 1, 0, 0])
print("总分:", result["score"]["total"], "分级:", result["band"]["label"])

if result["crisis"]["flagged"]:
    for res in result["crisis"]["resources"]:
        print("求助:", res["name"], res["contact"])

JavaScript / Node

const API = "https://api.mindframe.top";
const KEY = process.env.MINDFRAME_API_KEY;

async function scoreScale(scaleId, answers) {
  const res = await fetch(`${API}/v1/scales/${scaleId}/score`, {
    method: "POST",
    headers: {
      "X-API-Key": KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ answers }),
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(`${res.status} ${err.error ?? "unknown"}: ${err.message ?? ""}`);
  }
  return res.json();
}

const r = await scoreScale("phq9", [1, 1, 2, 1, 1, 2, 1, 0, 0]);
console.log(r.score.total, r.band?.label);
if (r.crisis.flagged) {
  // 必须展示求助资源
  console.table(r.crisis.resources);
}

PHP

<?php
$key = getenv('MINDFRAME_API_KEY');

$ch = curl_init('https://api.mindframe.top/v1/scales/phq9/score');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'X-API-Key: ' . $key,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode(['answers' => [1,1,2,1,1,2,1,0,0]]),
    CURLOPT_TIMEOUT        => 15,
]);

$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($code !== 200) {
    throw new RuntimeException($data['message'] ?? 'API error');
}

echo "总分: {$data['score']['total']}\n";
if (!empty($data['crisis']['flagged'])) {
    foreach ($data['crisis']['resources'] as $r) {
        echo "求助: {$r['name']} {$r['contact']}\n";
    }
}

限流

限流按 API Key 计算,默认每分钟 60 次,可在签发时调整。 超限返回:

{
  "ok": false,
  "error": "rate_limited",
  "message": "该 API Key 每分钟最多 60 次调用,请稍后重试。",
  "limit_per_minute": 60,
  "retry_after": 60,
  "time": 1792305145
}

响应头带 Retry-After,请按该秒数退避重试,不要立即重发。

容量规划 计分接口是纯计算,成本很低;限制主要是防止脚本误循环。 如果你需要更高的配额,联系管理员调整你的 Key 即可,无需改代码。

错误码

所有错误响应结构一致:ok: false + 机器可读的 error + 人类可读的 message。

HTTPerror含义怎么处理
400bad_json请求体不是合法 JSON检查 Content-Type 与 JSON 语法
400empty_body请求体为空确认发出了 body
400missing_answers缺少 answers 字段按文档传 {"answers":[…]}
401missing_api_key没带 Key加 X-API-Key 请求头
401invalid_api_keyKey 不存在或已删除核对 Key,或联系管理员重发
401key_disabledKey 已停用联系管理员启用
401key_expiredKey 已过期联系管理员续期
403insufficient_scope缺所需权限申请带 score 或 read 的 Key
404scale_not_found量表 id 不存在用列表接口拿可用 id
405method_not_allowed用了错误的 HTTP 方法看响应里的 allow
422incomplete_answers缺题看 missing_items 补齐
422invalid_answers取值越界看 errors[],取值须在 options 内
429rate_limited超过限流按 Retry-After 退避
500internal_error服务端异常稍后重试;持续失败请联系管理员

错误响应示例

{
  "ok": false,
  "error": "incomplete_answers",
  "message": "作答不完整:本量表共 9 题,缺 6 题。",
  "missing_items": [3, 4, 5, 6, 7, 8],
  "item_count": 9,
  "time": 1792305145
}

伦理与合规

使用边界

  • 本接口仅供研究与初筛,输出结果不构成诊断、处方或任何医疗建议。
  • 不得将分数直接呈现给受测者作为「你患有抑郁症」之类的结论。
  • 涉及未成年人时,须遵循当地关于未成年人参与研究的伦理与知情同意规定。

数据责任

  • 本接口是无状态的:计分不会保存作答内容,服务端只记录调用次数用于限流统计。
  • 作答数据的存储、脱敏与销毁由接入方负责,须符合你所在机构的伦理审查要求。
  • 日志中不记录作答正文,但可能记录 Key 的标识与调用时间。

量表版权

  • PHQ-9:公共领域,Pfizer 已明确豁免版权,可免费使用。
  • ESSA:版权归 SAGE Publications,使用须保留完整文献引用; 其中改编自 AESI 的题目属第三方版权,如你有特殊用途请自行确认。

危机响应义务

见 危机预警。触发时向受测者展示求助资源是接入方的硬性义务, 不是可选项。

更新记录

2026-10-18
v1 量表接口上线

新增 /v1/scales、/v1/scales/{id}、/v1/scales/{id}/score, 引入 API Key 鉴权与按 Key 限流。服务版本 2.1.0。

2026-10-11
v2.0.1

问卷提交与记录撤回接口稳定;补全探活信息。