metaculus
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMetaculus API
Metaculus API
Metaculus is a forecasting platform where users predict outcomes of real-world events. Questions range across science, technology, politics, economics, and more. The platform aggregates individual forecasts into community predictions and scores forecasters on accuracy.
Check this skill and the official API documentation FREQUENTLY for updates.
Feedback: Contact the Metaculus team at api-requests@metaculus.com with questions, ideas, or feedback.
Source code & issues: github.com/Metaculus/metaculus
Metaculus 是一个供用户预测现实世界事件结果的预测平台。问题涵盖科学、技术、政治、经济等多个领域。该平台会将个人预测汇总为社区预测,并根据准确性对预测者进行评分。
请经常查看此技能文档和官方API文档以获取更新信息。
反馈: 如有问题、想法或反馈,请通过 api-requests@metaculus.com 联系Metaculus团队。
源代码与问题反馈: github.com/Metaculus/metaculus
Key Concepts (Glossary)
核心概念(术语表)
| Term | Definition |
|---|---|
| Post | The primary feed entity. A post wraps a question, group of questions, conditional pair, or notebook. Posts have statuses, authors, projects, and comments. |
| Question | A single forecastable item within a post. Types: |
| Group of Questions | A post containing multiple related sub-questions displayed together (e.g., "What will GDP be in 2025, 2026, 2027?"). |
| Conditional | A post with paired questions: "If [condition], what is P(child)?" — produces question_yes and question_no variants. |
| Forecast | A user's prediction on a question. Format depends on question type: probability (binary), CDF (continuous), or distribution (multiple choice). |
| Community Prediction (CP) | The aggregated forecast from all users, computed via various aggregation methods. |
| Aggregation Method | How individual forecasts are combined: |
| Project | A container for posts — can be a tournament, category, tag, question series, or site section. |
| Tournament | A special project type with prize pools, start/close dates, and leaderboards. |
| Category | A topic classification (e.g., "Nuclear Technology & Risks", "Health & Pandemics"). |
| Resolution | The actual outcome of a question once known. Values vary by type: |
| Curation Status | Editorial status of a post: |
| Scaling | Defines how a continuous question's range maps to the CDF. Includes |
| CDF | Cumulative Distribution Function — the format for continuous forecasts. A list of 201 floats (or |
| Inbound Outcome Count | Number of possible outcomes within a question's range (excluding out-of-bounds). Default is 200 for continuous; smaller for discrete. |
| 术语 | 定义 |
|---|---|
| Post(帖子) | 平台信息流中的核心实体。一个帖子可包含单个问题、一组相关问题、条件式问题对或笔记内容。帖子包含状态、作者、项目和评论等信息。 |
| Question(问题) | 帖子中的单个可预测项。类型包括: |
| Group of Questions(问题组) | 包含多个相关子问题的帖子(例如:“2025、2026、2027年的GDP将是多少?”)。 |
| Conditional(条件式问题) | 包含配对问题的帖子:“如果[条件成立],则P(子问题)的概率是多少?”——会生成question_yes和question_no两种变体。 |
| Forecast(预测) | 用户针对某个问题提交的预测内容。格式取决于问题类型:二元问题为概率值,连续型问题为CDF,多选问题为概率分布。 |
| Community Prediction (CP)(社区预测) | 所有用户预测的汇总结果,通过多种聚合方法计算得出。 |
| Aggregation Method(聚合方法) | 汇总个人预测的方式: |
| Project(项目) | 帖子的容器——可以是锦标赛、分类、标签、问题系列或网站板块。 |
| Tournament(锦标赛) | 特殊类型的项目,包含奖金池、开始/结束日期和排行榜。 |
| Category(分类) | 主题分类(例如:“核技术与风险”、“健康与流行病”)。 |
| Resolution(结果判定) | 问题的实际结果(当结果确定后)。值的类型取决于问题:二元问题为 |
| Curation Status(审核状态) | 帖子的编辑状态: |
| Scaling(缩放规则) | 定义连续型问题的范围如何映射到CDF。包含 |
| CDF | 累积分布函数——连续型预测的格式。包含201个浮点数(离散型问题为 |
| Inbound Outcome Count(有效结果数量) | 问题范围内的可能结果数量(不包含超出边界的结果)。连续型问题默认值为200,离散型问题数值更小。 |
Base URL
基础URL
All endpoints are served from:
https://www.metaculus.comAll paths below are relative to this base (e.g., means ).
GET /api/posts/GET https://www.metaculus.com/api/posts/所有接口的基础地址为:
https://www.metaculus.com以下所有路径均基于此基础地址(例如: 对应 )。
GET /api/posts/GET https://www.metaculus.com/api/posts/Authentication
身份验证
All API requests require authentication. Unauthenticated requests are rejected.
所有API请求均需要身份验证。 未验证的请求会被拒绝。
Getting Your Token
获取你的Token
- Log in to Metaculus.
- Go to your Account Settings → API Access.
- Copy (or generate) your API token.
- 登录 Metaculus。
- 进入你的 账户设置 → API访问 页面。
- 复制(或生成)你的API Token。
Using the Token
使用Token
Add the header to every request. The token must be prefixed with the literal string followed by a space:
AuthorizationTokenAuthorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b在每个请求中添加请求头。Token必须以字符串开头,后跟一个空格:
AuthorizationTokenAuthorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4bExample: curl
示例:curl
bash
curl -s "https://www.metaculus.com/api/posts/?limit=5&order_by=-published_at" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .bash
curl -s "https://www.metaculus.com/api/posts/?limit=5&order_by=-published_at" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .Example: Python
示例:Python
python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(f"{BASE}/api/posts/", headers=HEADERS, params={"limit": 5})
resp.raise_for_status()
data = resp.json()python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(f"{BASE}/api/posts/", headers=HEADERS, params={"limit": 5})
resp.raise_for_status()
data = resp.json()Environment Variables
环境变量
Never hardcode tokens. Store them as environment variables or in a file:
.envMETACULUS_API_TOKEN=your-token-here请勿硬编码Token。请将其存储为环境变量或保存在文件中:
.envMETACULUS_API_TOKEN=your-token-hereRate Limits
请求频率限制
Metaculus throttles requests to prevent abuse. If you receive a response, implement exponential backoff before retrying.
429 Too Many RequestsMetaculus会对请求进行限流以防止滥用。如果收到响应,请实现指数退避机制后再重试。
429 Too Many RequestsREST API Endpoints Overview
REST API接口概述
Feed (Posts)
信息流(帖子)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| | Retrieve paginated posts feed with filters | Required |
| | Retrieve a single post with full details | Required |
| 方法 | 接口 | 描述 | 身份验证 |
|---|---|---|---|
| | 获取带筛选条件的分页帖子信息流 | 必填 |
| | 获取单个帖子的完整详情 | 必填 |
Questions & Forecasts
问题与预测
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| | Submit forecasts for one or more questions | Required |
| | Withdraw active forecasts | Required |
| 方法 | 接口 | 描述 | 身份验证 |
|---|---|---|---|
| | 提交一个或多个问题的预测 | 必填 |
| | 撤回已提交的活跃预测 | 必填 |
Comments
评论
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| | Retrieve comments (filter by post or author) | Required |
| | Create a new comment on a post | Required |
| 方法 | 接口 | 描述 | 身份验证 |
|---|---|---|---|
| | 获取评论(可按帖子或作者筛选) | 必填 |
| | 在帖子下创建新评论 | 必填 |
Utilities & Data
工具与数据
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| | Download question data as a ZIP of CSVs | Required |
| | Download full project data as a ZIP of CSVs | Required (admin/whitelisted) |
| 方法 | 接口 | 描述 | 身份验证 |
|---|---|---|---|
| | 以ZIP压缩包(内含CSV文件)的形式下载问题数据 | 必填 |
| | 下载整个项目的完整数据(ZIP压缩包) | 必填(仅管理员或白名单用户可用) |
Data Model
数据模型
Post → Question Hierarchy
帖子 → 问题层级关系
A Post is the top-level entity in the feed. Each post contains exactly one of:
- — a single question (binary, numeric, date, multiple choice, or discrete)
question - — multiple related sub-questions
group_of_questions - — a conditional pair (condition + child, producing question_yes and question_no)
conditional - A notebook (no question content)
The other fields will be . For example, a post with a single binary question will have populated and / set to .
nullquestionconditionalgroup_of_questionsnull帖子是信息流中的顶层实体。每个帖子仅包含以下内容之一:
- — 单个问题(适用于单问题帖子)
question - — 多个相关子问题
group_of_questions - — 条件式问题对(条件+子问题,生成question_yes和question_no两种变体)
conditional - 笔记内容(无问题相关内容)
其他字段将为。例如,包含单个二元问题的帖子会填充字段,而/字段将设为。
nullquestionconditionalgroup_of_questionsnullPost
帖子
| Field | Type | Description |
|---|---|---|
| integer | Unique post ID |
| string | Full title |
| string | URL-friendly short title |
| string | URL slug |
| integer | Author's user ID |
| string | Author's username |
| object | Associated projects (see below) |
| datetime | When the post was created |
| datetime? | When the post was published |
| datetime? | When the question opened for forecasting |
| datetime | Last edit timestamp |
| string | |
| integer | Number of comments |
| string | |
| integer | Number of unique forecasters |
| Question? | Single question (if applicable) |
| Conditional? | Conditional pair (if applicable) |
| GroupOfQuestions? | Question group (if applicable) |
| string | |
| object | |
| integer | Total number of forecasts |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 唯一帖子ID |
| 字符串 | 完整标题 |
| 字符串 | 适合URL的短标题 |
| 字符串 | URL别名 |
| 整数 | 作者的用户ID |
| 字符串 | 作者的用户名 |
| 对象 | 关联的项目(详见下文) |
| 日期时间 | 帖子创建时间 |
| 日期时间(可选) | 帖子发布时间 |
| 日期时间(可选) | 问题开放预测的时间 |
| 日期时间 | 最后编辑时间戳 |
| 字符串 | |
| 整数 | 评论数量 |
| 字符串 | |
| 整数 | 唯一预测者数量 |
| Question(可选) | 单个问题(如适用) |
| Conditional(可选) | 条件式问题对(如适用) |
| GroupOfQuestions(可选) | 问题组(如适用) |
| 字符串 | |
| 对象 | |
| 整数 | 预测总数量 |
Post Projects Object
帖子关联项目对象
| Field | Type | Description |
|---|---|---|
| Project[] | Site-level project associations |
| Project[] | Tournaments this post belongs to |
| Category[] | Categories |
| Tag[] | Tags |
| Project[] | Question series |
| Project | The post's primary/default project |
| 字段 | 类型 | 描述 |
|---|---|---|
| Project[] | 网站级别的项目关联 |
| Project[] | 帖子所属的锦标赛 |
| Category[] | 分类 |
| Tag[] | 标签 |
| Project[] | 问题系列 |
| Project | 帖子的主要/默认项目 |
Project
项目
| Field | Type | Description |
|---|---|---|
| integer | Project ID |
| string | |
| string | Display name |
| string? | URL slug |
| string | Prize pool amount (e.g., "0.00") |
| datetime? | Start date |
| datetime? | Close date |
| boolean? | Whether the project is ongoing |
| string | Default user permission (e.g., "forecaster") |
| string | |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 项目ID |
| 字符串 | |
| 字符串 | 显示名称 |
| 字符串(可选) | URL别名 |
| 字符串 | 奖金池金额(例如:"0.00") |
| 日期时间(可选) | 开始日期 |
| 日期时间(可选) | 结束日期 |
| 布尔值(可选) | 项目是否正在进行中 |
| 字符串 | 默认用户权限(例如:"forecaster") |
| 字符串 | |
Question
问题
| Field | Type | Description |
|---|---|---|
| integer | Unique question ID (used in forecast submissions, not the post ID) |
| string | Question title |
| string | Full description (may be omitted unless |
| string | |
| string | |
| string? | Resolution value (null if unresolved) |
| string | How the question will be resolved |
| string | Additional resolution details |
| datetime | Creation timestamp |
| datetime | When forecasting opens |
| datetime | When forecasting is scheduled to close |
| datetime | When forecasting actually closed |
| datetime | When resolution is scheduled |
| datetime? | When it was actually resolved |
| string[] | Current options (multiple choice only) |
| string[] | All options that have ever existed (multiple choice only) |
| boolean | Whether the upper bound is open |
| boolean | Whether the lower bound is open |
| integer? | Number of discrete outcomes in range (default 200 for continuous) |
| string | Display unit (e.g., "$", "°C") |
| string? | Label for sub-questions |
| QuestionScaling | Range and scaling parameters |
| object | Community prediction aggregations (see below) |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 唯一问题ID(提交预测时使用此ID,而非帖子ID) |
| 字符串 | 问题标题 |
| 字符串 | 完整描述(仅当 |
| 字符串 | |
| 字符串 | |
| 字符串(可选) | 结果值(未判定时为null) |
| 字符串 | 问题的结果判定规则 |
| 字符串 | 额外的结果判定细节 |
| 日期时间 | 创建时间戳 |
| 日期时间 | 开放预测的时间 |
| 日期时间 | 计划停止预测的时间 |
| 日期时间 | 实际停止预测的时间 |
| 日期时间 | 计划判定结果的时间 |
| 日期时间(可选) | 实际判定结果的时间 |
| string[] | 当前选项(仅多选问题) |
| string[] | 所有曾出现过的选项(仅多选问题) |
| 布尔值 | 上边界是否为开放区间 |
| 布尔值 | 下边界是否为开放区间 |
| 整数(可选) | 范围内的离散结果数量(连续型问题默认200) |
| 字符串 | 显示单位(例如:"$", "°C") |
| 字符串(可选) | 子问题的标签 |
| QuestionScaling | 范围和缩放参数 |
| 对象 | 社区预测的聚合结果(详见下文) |
QuestionScaling
问题缩放参数
| Field | Type | Description |
|---|---|---|
| float? | Lower boundary of the input range |
| float? | Upper boundary of the input range |
| float? | Log-scale zero point (null = linear scaling) |
| boolean? | Whether upper bound is open |
| boolean? | Whether lower bound is open |
| integer? | Number of outcomes within range |
| string[]? | Real-value locations where the CDF is evaluated |
| 字段 | 类型 | 描述 |
|---|---|---|
| 浮点数(可选) | 输入范围的下边界 |
| 浮点数(可选) | 输入范围的上边界 |
| 浮点数(可选) | 对数缩放的零点(null表示线性缩放) |
| 布尔值(可选) | 上边界是否为开放区间 |
| 布尔值(可选) | 下边界是否为开放区间 |
| 整数(可选) | 范围内的结果数量 |
| string[](可选) | 计算CDF时使用的实际值位置 |
Aggregations
聚合结果
Each question includes an object with up to four methods:
aggregations- — Default; weights recent forecasts more heavily
recency_weighted - — Equal weight for all forecasts
unweighted - — Metaculus's proprietary prediction
metaculus_prediction - — Beta; admin-only
single_aggregation
Each method contains:
| Field | Type | Description |
|---|---|---|
| object[] | Time series of aggregated forecasts |
| object? | Most recent aggregated forecast |
| object? | Scoring information |
Each history/latest entry contains:
| Field | Type | Description |
|---|---|---|
| datetime | When this aggregation period started |
| datetime? | When this aggregation period ended |
| float[] | The aggregated forecast (probability for binary, CDF for continuous) |
| integer | Number of contributing forecasters |
| float[] | Median/center values |
| float[] | Lower confidence bounds |
| float[] | Upper confidence bounds |
| float? | Mean values |
每个问题的对象包含最多四种聚合方法的结果:
aggregations- — 默认方法,近期预测权重更高
recency_weighted - — 所有预测权重相等
unweighted - — Metaculus专有算法的预测结果
metaculus_prediction - — 测试版,仅管理员可用
single_aggregation
每种方法包含以下内容:
| 字段 | 类型 | 描述 |
|---|---|---|
| object[] | 聚合预测的时间序列数据 |
| 对象(可选) | 最新的聚合预测结果 |
| 对象(可选) | 评分信息 |
historylatest| 字段 | 类型 | 描述 |
|---|---|---|
| 日期时间 | 此聚合周期的开始时间 |
| 日期时间(可选) | 此聚合周期的结束时间 |
| float[] | 聚合后的预测结果(二元问题为概率,连续型问题为CDF) |
| 整数 | 参与此聚合的预测者数量 |
| float[] | 中位数/中心值 |
| float[] | 置信区间下边界 |
| float[] | 置信区间上边界 |
| 浮点数(可选) | 平均值 |
Conditional
条件式问题
| Field | Type | Description |
|---|---|---|
| integer | Conditional ID |
| Question | The condition question |
| Question | The child question |
| Question | "If condition = Yes" variant |
| Question | "If condition = No" variant |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 条件式问题ID |
| Question | 条件问题 |
| Question | 子问题 |
| Question | "如果条件为Yes"的变体问题 |
| Question | "如果条件为No"的变体问题 |
GroupOfQuestions
问题组
| Field | Type | Description |
|---|---|---|
| integer | Group ID |
| string | Group description |
| string | Resolution criteria |
| string | Additional details |
| string | The variable that differs across sub-questions |
| string | |
| Question[] | The sub-questions |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 问题组ID |
| 字符串 | 问题组描述 |
| 字符串 | 结果判定规则 |
| 字符串 | 额外细节 |
| 字符串 | 子问题之间的差异变量 |
| 字符串 | |
| Question[] | 子问题列表 |
Comment
评论
| Field | Type | Description |
|---|---|---|
| integer | Comment ID |
| object | |
| integer? | Parent comment ID (for replies) |
| integer? | Root comment ID in thread |
| datetime | Creation timestamp |
| string | Comment content |
| integer | Post ID the comment belongs to |
| boolean | Whether the user's last forecast is included |
| boolean | Whether the comment is private |
| integer | Total vote score |
| integer | Current user's vote (-1, 0, 1) |
| 字段 | 类型 | 描述 |
|---|---|---|
| 整数 | 评论ID |
| 对象 | |
| 整数(可选) | 父评论ID(用于回复) |
| 整数(可选) | 评论线程的根评论ID |
| 日期时间 | 创建时间戳 |
| 字符串 | 评论内容 |
| 整数 | 评论所属的帖子ID |
| 布尔值 | 是否包含用户的最新预测 |
| 布尔值 | 评论是否为私有 |
| 整数 | 总投票分数 |
| 整数 | 当前用户的投票(-1, 0, 1) |
Endpoints: Detailed Reference
接口:详细参考
GET /api/posts/ — Retrieve Posts Feed
GET /api/posts/ — 获取帖子信息流
Returns a paginated list of posts with extensive filtering and sorting.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
| integer | Page size (default varies) |
| integer | Pagination offset |
| string[] | Filter by tournament slugs (e.g., |
| string[] | Filter by status: |
| string[] | Filter by type: |
| string[] | Filter by category slugs (e.g., |
| integer | Posts where this user has forecasted |
| integer | Posts where this user has NOT forecasted |
| boolean | Filter for main feed suitability |
| boolean | Include community predictions (default: |
| boolean | Include full CP history per aggregation method (default: |
| boolean | Include |
| string | Sort field. Prefix with |
| datetime | Open time greater than (also supports |
| datetime | Published time greater than (also supports |
| datetime | Scheduled resolve time greater than (also supports |
order_by| Value | Description |
|---|---|
| Publication time |
| When forecasting opened |
| Community vote score |
| Number of comments |
| Number of forecasts |
| Scheduled close time |
| Scheduled resolution time |
| When the user last forecasted |
| Unread comments |
| Weekly probability movement |
| Divergence metric |
| Composite trending score (decays after 3.5 days) |
| User forecasting performance (requires |
Response:
json
{
"next": "https://www.metaculus.com/api/posts/?limit=20&offset=20",
"previous": null,
"results": [ /* array of Post objects */ ]
}Example: Fetch open binary questions, newest first:
bash
curl -s "https://www.metaculus.com/api/posts/?statuses=open&forecast_type=binary&order_by=-published_at&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'Example: Fetch questions from a tournament:
bash
curl -s "https://www.metaculus.com/api/posts/?tournaments=metaculus-cup&with_cp=true&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .Example: Fetch questions you haven't forecasted on yet:
bash
undefined返回带筛选和排序功能的分页帖子列表。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| 整数 | 每页数量(默认值可变) |
| 整数 | 分页偏移量 |
| string[] | 按锦标赛别名筛选(例如: |
| string[] | 按状态筛选: |
| string[] | 按问题类型筛选: |
| string[] | 按分类别名筛选(例如: |
| 整数 | 筛选该用户已预测过的帖子 |
| 整数 | 筛选该用户未预测过的帖子 |
| 布尔值 | 筛选适合显示在主信息流的帖子 |
| 布尔值 | 是否包含社区预测(默认: |
| 布尔值 | 是否包含完整的社区预测历史数据(默认: |
| 布尔值 | 是否包含问题描述、判定规则等字段 |
| 字符串 | 排序字段。前缀 |
| 日期时间 | 开放时间晚于指定时间(也支持 |
| 日期时间 | 发布时间晚于指定时间(也支持 |
| 日期时间 | 计划判定时间晚于指定时间(也支持 |
order_by| 值 | 描述 |
|---|---|
| 发布时间 |
| 开放预测时间 |
| 社区投票分数 |
| 评论数量 |
| 预测数量 |
| 计划停止预测时间 |
| 计划判定结果时间 |
| 用户最后一次预测的时间 |
| 未读评论数量 |
| 每周概率变化幅度 |
| 分歧度指标 |
| 综合热度评分(3.5天后衰减) |
| 用户预测表现分数(必须指定 |
响应:
json
{
"next": "https://www.metaculus.com/api/posts/?limit=20&offset=20",
"previous": null,
"results": [ /* 帖子对象数组 */ ]
}示例:获取最新的开放二元问题:
bash
curl -s "https://www.metaculus.com/api/posts/?statuses=open&forecast_type=binary&order_by=-published_at&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'示例:获取锦标赛中的问题:
bash
curl -s "https://www.metaculus.com/api/posts/?tournaments=metaculus-cup&with_cp=true&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .示例:获取你尚未预测的问题:
bash
undefinedReplace YOUR_USER_ID with your actual Metaculus user ID
将YOUR_USER_ID替换为你的Metaculus用户ID
curl -s "https://www.metaculus.com/api/posts/?not_forecaster_id=YOUR_USER_ID&statuses=open&limit=10"
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
---curl -s "https://www.metaculus.com/api/posts/?not_forecaster_id=YOUR_USER_ID&statuses=open&limit=10"
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
---GET /api/posts/{postId}/ — Retrieve Post Details
GET /api/posts/{postId}/ — 获取帖子详情
Returns full details for a single post, including all sub-questions and aggregations.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
| integer | The post ID |
Example:
bash
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .返回单个帖子的完整详情,包括所有子问题和聚合结果。
路径参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| 整数 | 帖子ID |
示例:
bash
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .POST /api/questions/forecast/ — Submit Forecasts
POST /api/questions/forecast/ — 提交预测
Submit one or more forecasts. The request body is a JSON array of forecast objects.
Important: Thefield takes the question ID, not the post ID. Get the question ID fromquestion,post.question.id, orpost.group_of_questions.questions[].id/post.conditional.question_yes.id.post.conditional.question_no.id
提交一个或多个预测。请求体为JSON数组,包含多个预测对象。
重要提示:字段使用问题ID,而非帖子ID。可从question、post.question.id或post.group_of_questions.questions[].id/post.conditional.question_yes.id中获取问题ID。post.conditional.question_no.id
Binary Forecast
二元问题预测
json
[
{
"question": 12345,
"probability_yes": 0.63
}
]| Field | Type | Required | Description |
|---|---|---|---|
| integer | Yes | Question ID |
| float | Yes | Probability between 0 and 1 |
| datetime | No | Auto-withdraw timestamp |
json
[
{
"question": 12345,
"probability_yes": 0.63
}
]| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 是 | 问题ID |
| 浮点数 | 是 | 0到1之间的概率值 |
| 日期时间 | 否 | 自动撤回预测的时间戳 |
Multiple Choice Forecast
多选问题预测
json
[
{
"question": 12345,
"probability_yes_per_category": {
"Futurama": 0.5,
"Paperclipalypse": 0.3,
"Singularia": 0.2
}
}
]| Field | Type | Required | Description |
|---|---|---|---|
| integer | Yes | Question ID |
| object | Yes | Map of option name → probability. Must sum to 1.0. |
| datetime | No | Auto-withdraw timestamp |
json
[
{
"question": 12345,
"probability_yes_per_category": {
"Futurama": 0.5,
"Paperclipalypse": 0.3,
"Singularia": 0.2
}
}
]| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 是 | 问题ID |
| 对象 | 是 | 选项名称→概率的映射。所有概率之和必须为1.0。 |
| 日期时间 | 否 | 自动撤回预测的时间戳 |
Continuous Forecast (Numeric / Date / Discrete)
连续型问题预测(数值/日期/离散)
json
[
{
"question": 12345,
"continuous_cdf": [0.0, 0.00005, 0.00010, "... 201 values total ...", 1.0]
}
]| Field | Type | Required | Description |
|---|---|---|---|
| integer | Yes | Question ID |
| float[] | Yes | CDF array (see CDF generation section below) |
| object | No | Slider values for frontend display |
| datetime | No | Auto-withdraw timestamp |
json
[
{
"question": 12345,
"continuous_cdf": [0.0, 0.00005, 0.00010, "... 共201个值 ...", 1.0]
}
]| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 是 | 问题ID |
| float[] | 是 | CDF数组(详见下文CDF生成部分) |
| 对象 | 否 | 用于前端显示的滑块值 |
| 日期时间 | 否 | 自动撤回预测的时间戳 |
Conditional Forecast
条件式问题预测
Submit forecasts for both the "if Yes" and "if No" questions:
json
[
{ "question": 111, "probability_yes": 0.499 },
{ "question": 222, "probability_yes": 0.501 }
]Where is and is .
111conditional.question_yes.id222conditional.question_no.id同时提交"if Yes"和"if No"两种变体问题的预测:
json
[
{ "question": 111, "probability_yes": 0.499 },
{ "question": 222, "probability_yes": 0.501 }
]其中是,是。
111conditional.question_yes.id222conditional.question_no.idGroup Forecast
问题组预测
Submit forecasts for multiple sub-questions in a single request:
json
[
{ "question": 1, "probability_yes": 0.11 },
{ "question": 2, "probability_yes": 0.22 },
{ "question": 3, "probability_yes": 0.33 }
]Example: Submit a binary forecast with curl:
bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'Responses:
| Status | Description |
|---|---|
| Forecasts submitted successfully |
| Invalid request format |
在单个请求中提交多个子问题的预测:
json
[
{ "question": 1, "probability_yes": 0.11 },
{ "question": 2, "probability_yes": 0.22 },
{ "question": 3, "probability_yes": 0.33 }
]示例:使用curl提交二元问题预测:
bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'响应:
| 状态码 | 描述 |
|---|---|
| 预测提交成功 |
| 请求格式无效 |
POST /api/questions/withdraw/ — Withdraw Forecasts
POST /api/questions/withdraw/ — 撤回预测
Withdraw active forecasts. The request body is a JSON array of withdrawal objects.
json
[
{ "question": 12345 },
{ "question": 12346 }
]Example:
bash
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'撤回已提交的活跃预测。请求体为JSON数组,包含多个撤回对象。
json
[
{ "question": 12345 },
{ "question": 12346 }
]示例:
bash
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'GET /api/comments/ — Retrieve Comments
GET /api/comments/ — 获取评论
Fetch comments with filters. Either or is required.
postauthorQuery Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| integer | One of post/author | Post ID to filter by |
| integer | One of post/author | Author user ID to filter by |
| integer | No | Number of comments to retrieve |
| integer | No | Pagination offset |
| boolean | No | Filter private vs public (default: |
| boolean | No | If |
| string | No | |
| integer | No | Place this comment at the top of results |
Response:
json
{
"total_count": 42,
"count": 15,
"next": "https://www.metaculus.com/api/comments/?post=123&limit=10&offset=10",
"previous": null,
"results": [ /* array of Comment objects */ ]
}- — Total root + child comments
total_count - — Total root comments only
count
Example:
bash
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .获取带筛选条件的评论。必须指定或参数之一。
postauthor查询参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 二选一 | 按帖子ID筛选 |
| 整数 | 二选一 | 按作者用户ID筛选 |
| 整数 | 否 | 要获取的评论数量 |
| 整数 | 否 | 分页偏移量 |
| 布尔值 | 否 | 筛选私有/公开评论(默认: |
| 布尔值 | 否 | 如果为 |
| 字符串 | 否 | |
| 整数 | 否 | 将指定评论放在结果顶部 |
响应:
json
{
"total_count": 42,
"count": 15,
"next": "https://www.metaculus.com/api/comments/?post=123&limit=10&offset=10",
"previous": null,
"results": [ /* 评论对象数组 */ ]
}- — 根评论+子评论的总数
total_count - — 仅根评论的数量
count
示例:
bash
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .POST /api/comments/create/ — Create a Comment
POST /api/comments/create/ — 创建评论
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
| integer | Yes | Post ID to comment on |
| string | Yes | Comment content |
| boolean | Yes | Include the user's last forecast |
| boolean | Yes | Whether the comment is private |
| integer | No | Parent comment ID (for replies) |
Example:
bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "I updated my forecast based on the latest data.",
"included_forecast": false,
"is_private": false
}'Responses:
| Status | Description |
|---|---|
| Comment created successfully (returns Comment object) |
| Invalid request format |
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 是 | 评论所属的帖子ID |
| 字符串 | 是 | 评论内容 |
| 布尔值 | 是 | 是否包含用户的最新预测 |
| 布尔值 | 是 | 评论是否为私有 |
| 整数 | 否 | 父评论ID(用于回复) |
示例:
bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "我根据最新数据更新了我的预测。",
"included_forecast": false,
"is_private": false
}'响应:
| 状态码 | 描述 |
|---|---|
| 评论创建成功(返回评论对象) |
| 请求格式无效 |
GET /api/posts/{postId}/download-data/ — Download Question Data
GET /api/posts/{postId}/download-data/ — 下载问题数据
Downloads forecast data as a ZIP file containing CSVs.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
| integer | Post ID (the number after |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| integer | No | Sub-question ID for group/conditional questions |
| string[] | No | Methods to include: |
| boolean | No | Include bot forecasts in aggregation recalculation |
| string[] | No | Specific user IDs (whitelisted users only). Requires |
| boolean | No | Default: |
| boolean | No | Default: |
| boolean | No | Default: |
ZIP Contents:
| File | Description |
|---|---|
| Question metadata, scaling, resolution |
| Individual and aggregated forecasts |
| Comments (if |
| Scores (if |
Example:
bash
curl -s "https://www.metaculus.com/api/posts/12345/download-data/?include_comments=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-o question_data.zip以ZIP压缩包(内含CSV文件)的形式下载预测数据。
路径参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| 整数 | 帖子ID(URL中 |
查询参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 整数 | 否 | 问题组/条件式问题的子问题ID |
| string[] | 否 | 要包含的聚合方法: |
| 布尔值 | 否 | 在重新计算聚合结果时是否包含机器人预测 |
| string[] | 否 | 指定用户ID(仅白名单用户可用)。需要同时指定 |
| 布尔值 | 否 | 默认: |
| 布尔值 | 否 | 默认: |
| 布尔值 | 否 | 默认: |
ZIP压缩包内容:
| 文件 | 描述 |
|---|---|
| 问题元数据、缩放规则、结果判定信息 |
| 个人预测和聚合预测数据 |
| 评论数据(当 |
| 评分数据(当 |
示例:
bash
curl -s "https://www.metaculus.com/api/posts/12345/download-data/?include_comments=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-o question_data.zipGET /api/projects/{projectId}/download-data/ — Download Project Data
GET /api/projects/{projectId}/download-data/ — 下载项目数据
Downloads data for an entire project as a ZIP. Only available to site admins and whitelisted users.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
| integer | Project ID |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| boolean | No | Include comments CSV |
| boolean | No | Include scores CSV |
下载整个项目的完整数据(ZIP压缩包)。仅网站管理员和白名单用户可用。
路径参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| 整数 | 项目ID |
查询参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| 布尔值 | 否 | 是否包含评论CSV文件 |
| 布尔值 | 否 | 是否包含评分CSV文件 |
Generating Continuous CDFs
生成连续型CDF
Continuous, numeric, date, and discrete questions require forecasts as a CDF (Cumulative Distribution Function). This section explains how to generate valid CDFs.
连续型、数值型、日期型和离散型问题的预测需要以CDF(累积分布函数)格式提交。本节说明如何生成有效的CDF。
CDF Rules
CDF规则
-
Length: The CDF must have exactlyvalues. For most continuous questions, this is 201 values. For discrete questions, it depends on the question.
inbound_outcome_count + 1 -
Bounds:
- If (closed): first value must be 0.0
open_lower_bound == false - If (open): first value must be ≥ 0.001 (at least 0.1% mass below lower bound)
open_lower_bound == true - If (closed): last value must be 1.0
open_upper_bound == false - If (open): last value must be ≤ 0.999 (at least 0.1% mass above upper bound)
open_upper_bound == true
- If
-
Monotonicity: The CDF must be strictly increasing by at leastper step (i.e.,
0.01 / inbound_outcome_countper step for the standard 200).0.00005 -
Maximum step: No two adjacent values may differ by more than.
0.2 * (200 / inbound_outcome_count)
-
长度: CDF必须包含恰好个值。对于大多数连续型问题,这个数量是201。对于离散型问题,数量取决于问题设置。
inbound_outcome_count + 1 -
边界规则:
- 如果(闭区间):第一个值必须为0.0
open_lower_bound == false - 如果(开区间):第一个值必须≥0.001(至少0.1%的概率质量低于下边界)
open_lower_bound == true - 如果(闭区间):最后一个值必须为1.0
open_upper_bound == false - 如果(开区间):最后一个值必须≤0.999(至少0.1%的概率质量高于上边界)
open_upper_bound == true
- 如果
-
单调性: CDF必须严格递增,每一步的增量至少为(对于标准200的情况,每步至少0.00005)。
0.01 / inbound_outcome_count -
最大步长: 相邻两个值的差值不得超过。
0.2 * (200 / inbound_outcome_count)
Understanding Scaling
理解缩放规则
The CDF values correspond to evenly spaced points across the internal [0, 1] range. To map real-world values to CDF positions:
- Linear scaling (is
zero_point):nullinternal = (value - range_min) / (range_max - range_min) - Logarithmic scaling (is set): requires log transformation (see function below)
zero_point - Date questions: convert ISO dates to unix timestamps first, then apply scaling
CDF值对应内部[0,1]范围内的均匀间隔点。要将现实世界的值映射到CDF位置:
- 线性缩放(为
zero_point):nullinternal = (value - range_min) / (range_max - range_min) - 对数缩放(已设置):需要进行对数转换(见下文函数)
zero_point - 日期问题:先将ISO日期转换为unix时间戳,再应用缩放规则
Python: Nominal Value to CDF Location
Python:实际值转CDF位置
python
import datetime
import numpy as np
def nominal_location_to_cdf_location(
nominal_location: str | float,
question_data: dict,
) -> float:
"""Takes a location in nominal format (e.g. 123, "123",
or datetime in iso format) and scales it to metaculus's
"internal representation" range [0,1] incorporating question scaling"""
if question_data["type"] == "date":
scaled_location = datetime.datetime.fromisoformat(
nominal_location
).timestamp()
else:
scaled_location = float(nominal_location)
scaling = question_data["scaling"]
range_min = scaling.get("range_min")
range_max = scaling.get("range_max")
zero_point = scaling.get("zero_point")
if zero_point is not None:
# logarithmically scaled question
deriv_ratio = (range_max - zero_point) / (range_min - zero_point)
unscaled_location = (
np.log(
(scaled_location - range_min) * (deriv_ratio - 1)
+ (range_max - range_min)
)
- np.log(range_max - range_min)
) / np.log(deriv_ratio)
else:
# linearly scaled question
unscaled_location = (scaled_location - range_min) / (
range_max - range_min
)
return unscaled_locationpython
import datetime
import numpy as np
def nominal_location_to_cdf_location(
nominal_location: str | float,
question_data: dict,
) -> float:
"""将实际格式的位置(例如123、"123"或ISO格式的日期)缩放为Metaculus的内部表示范围[0,1],并应用问题的缩放规则"""
if question_data["type"] == "date":
scaled_location = datetime.datetime.fromisoformat(
nominal_location
).timestamp()
else:
scaled_location = float(nominal_location)
scaling = question_data["scaling"]
range_min = scaling.get("range_min")
range_max = scaling.get("range_max")
zero_point = scaling.get("zero_point")
if zero_point is not None:
# 对数缩放的问题
deriv_ratio = (range_max - zero_point) / (range_min - zero_point)
unscaled_location = (
np.log(
(scaled_location - range_min) * (deriv_ratio - 1)
+ (range_max - range_min)
)
- np.log(range_max - range_min)
) / np.log(deriv_ratio)
else:
# 线性缩放的问题
unscaled_location = (scaled_location - range_min) / (
range_max - range_min
)
return unscaled_locationPython: Generate CDF from Percentiles
Python:从分位数生成CDF
python
def generate_continuous_cdf(
percentiles: dict,
question_data: dict,
below_lower_bound: float = None,
above_upper_bound: float = None,
) -> list[float]:
"""
Takes a set of percentiles and returns a corresponding CDF with
inbound_outcome_count + 1 values (typically 201).
percentiles: dict mapping percentile keys to nominal values
Keys must end in a number interpretable as a float in (0, 100).
Values are in the question's real-world scale.
Example:
{
"percentile_05": 25,
"percentile_25": 500,
"percentile_50": 650,
"percentile_75": 700,
"percentile_95": 990,
}
below_lower_bound: probability mass below range_min (for open lower bound)
above_upper_bound: probability mass above range_max (for open upper bound)
"""
percentile_locations = []
if below_lower_bound is not None:
percentile_locations.append((0.0, below_lower_bound))
if above_upper_bound is not None:
percentile_locations.append((1.0, 1 - above_upper_bound))
for percentile, nominal_location in percentiles.items():
height = float(str(percentile).split("_")[-1]) / 100
location = nominal_location_to_cdf_location(
nominal_location, question_data
)
percentile_locations.append((location, height))
percentile_locations.sort()
first_point, last_point = percentile_locations[0], percentile_locations[-1]
if (first_point[0] > 0.0) or (last_point[0] < 1.0):
raise ValueError(
"Percentiles must encompass bounds of the question"
)
def get_cdf_at(location):
previous = percentile_locations[0]
for i in range(1, len(percentile_locations)):
current = percentile_locations[i]
if previous[0] <= location <= current[0]:
return previous[1] + (current[1] - previous[1]) * (
location - previous[0]
) / (current[0] - previous[0])
previous = current
n_points = (question_data.get("inbound_outcome_count") or 200) + 1
continuous_cdf = [get_cdf_at(i / (n_points - 1)) for i in range(n_points)]
return continuous_cdfpython
def generate_continuous_cdf(
percentiles: dict,
question_data: dict,
below_lower_bound: float = None,
above_upper_bound: float = None,
) -> list[float]:
"""
根据分位数生成对应的CDF,包含inbound_outcome_count + 1个值(通常为201)。
percentiles: 分位数键到实际值的映射
键必须以可解析为(0,100)范围内浮点数的数字结尾。
值为问题的现实世界单位。
示例:
{
"percentile_05": 25,
"percentile_25": 500,
"percentile_50": 650,
"percentile_75": 700,
"percentile_95": 990,
}
below_lower_bound: 下边界以下的概率质量(用于开区间下边界)
above_upper_bound: 上边界以上的概率质量(用于开区间上边界)
"""
percentile_locations = []
if below_lower_bound is not None:
percentile_locations.append((0.0, below_lower_bound))
if above_upper_bound is not None:
percentile_locations.append((1.0, 1 - above_upper_bound))
for percentile, nominal_location in percentiles.items():
height = float(str(percentile).split("_")[-1]) / 100
location = nominal_location_to_cdf_location(
nominal_location, question_data
)
percentile_locations.append((location, height))
percentile_locations.sort()
first_point, last_point = percentile_locations[0], percentile_locations[-1]
if (first_point[0] > 0.0) or (last_point[0] < 1.0):
raise ValueError(
"分位数必须覆盖问题的整个范围"
)
def get_cdf_at(location):
previous = percentile_locations[0]
for i in range(1, len(percentile_locations)):
current = percentile_locations[i]
if previous[0] <= location <= current[0]:
return previous[1] + (current[1] - previous[1]) * (
location - previous[0]
) / (current[0] - previous[0])
previous = current
n_points = (question_data.get("inbound_outcome_count") or 200) + 1
continuous_cdf = [get_cdf_at(i / (n_points - 1)) for i in range(n_points)]
return continuous_cdfPython: Standardize CDF for Submission
Python:标准化CDF以提交
This function ensures your CDF satisfies all validation rules. It adds a small linear component to guarantee monotonicity and respects bound constraints.
python
def standardize_cdf(cdf, question_data: dict):
"""
Standardize a CDF for submission:
- No mass outside closed bounds
- Minimum mass outside open bounds
- Strictly increasing by at least the minimum step
- Caps maximum step size
"""
lower_open = question_data["open_lower_bound"]
upper_open = question_data["open_upper_bound"]
inbound_outcome_count = question_data.get("inbound_outcome_count") or 200
default_inbound_outcome_count = 200
cdf = np.asarray(cdf, dtype=float)
if not cdf.size:
return []
scale_lower_to = 0 if lower_open else cdf[0]
scale_upper_to = 1.0 if upper_open else cdf[-1]
rescaled_inbound_mass = scale_upper_to - scale_lower_to
def standardize(F: float, location: float) -> float:
rescaled_F = (F - scale_lower_to) / rescaled_inbound_mass
if lower_open and upper_open:
return 0.988 * rescaled_F + 0.01 * location + 0.001
elif lower_open:
return 0.989 * rescaled_F + 0.01 * location + 0.001
elif upper_open:
return 0.989 * rescaled_F + 0.01 * location
return 0.99 * rescaled_F + 0.01 * location
for i, value in enumerate(cdf):
cdf[i] = standardize(value, i / (len(cdf) - 1))
pmf = np.diff(cdf, prepend=0, append=1)
cap = 0.2 * (default_inbound_outcome_count / inbound_outcome_count)
def cap_pmf(scale: float) -> np.ndarray:
return np.concatenate(
[pmf[:1], np.minimum(cap, scale * pmf[1:-1]), pmf[-1:]]
)
def capped_sum(scale: float) -> float:
return float(cap_pmf(scale).sum())
lo = hi = scale = 1.0
while capped_sum(hi) < 1.0:
hi *= 1.2
for _ in range(100):
scale = 0.5 * (lo + hi)
s = capped_sum(scale)
if s < 1.0:
lo = scale
else:
hi = scale
if s == 1.0 or (hi - lo) < 2e-5:
break
pmf = cap_pmf(scale)
pmf[1:-1] *= (cdf[-1] - cdf[0]) / pmf[1:-1].sum()
cdf = np.cumsum(pmf)[:-1]
cdf = np.round(cdf, 10)
return cdf.tolist()此函数确保你的CDF满足所有验证规则。它会添加一个小的线性分量以保证单调性,并遵守边界约束。
python
def standardize_cdf(cdf, question_data: dict):
"""
标准化CDF以便提交:
- 闭区间边界外无概率质量
- 开区间边界外有最小概率质量
- 严格递增且每步增量不小于最小值
- 限制最大步长
"""
lower_open = question_data["open_lower_bound"]
upper_open = question_data["open_upper_bound"]
inbound_outcome_count = question_data.get("inbound_outcome_count") or 200
default_inbound_outcome_count = 200
cdf = np.asarray(cdf, dtype=float)
if not cdf.size:
return []
scale_lower_to = 0 if lower_open else cdf[0]
scale_upper_to = 1.0 if upper_open else cdf[-1]
rescaled_inbound_mass = scale_upper_to - scale_lower_to
def standardize(F: float, location: float) -> float:
rescaled_F = (F - scale_lower_to) / rescaled_inbound_mass
if lower_open and upper_open:
return 0.988 * rescaled_F + 0.01 * location + 0.001
elif lower_open:
return 0.989 * rescaled_F + 0.01 * location + 0.001
elif upper_open:
return 0.989 * rescaled_F + 0.01 * location
return 0.99 * rescaled_F + 0.01 * location
for i, value in enumerate(cdf):
cdf[i] = standardize(value, i / (len(cdf) - 1))
pmf = np.diff(cdf, prepend=0, append=1)
cap = 0.2 * (default_inbound_outcome_count / inbound_outcome_count)
def cap_pmf(scale: float) -> np.ndarray:
return np.concatenate(
[pmf[:1], np.minimum(cap, scale * pmf[1:-1]), pmf[-1:]]
)
def capped_sum(scale: float) -> float:
return float(cap_pmf(scale).sum())
lo = hi = scale = 1.0
while capped_sum(hi) < 1.0:
hi *= 1.2
for _ in range(100):
scale = 0.5 * (lo + hi)
s = capped_sum(scale)
if s < 1.0:
lo = scale
else:
hi = scale
if s == 1.0 or (hi - lo) < 2e-5:
break
pmf = cap_pmf(scale)
pmf[1:-1] *= (cdf[-1] - cdf[0]) / pmf[1:-1].sum()
cdf = np.cumsum(pmf)[:-1]
cdf = np.round(cdf, 10)
return cdf.tolist()End-to-End Example: Submit a Continuous Forecast
端到端示例:提交连续型预测
python
import os, requests, numpy as np
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"python
import os, requests, numpy as np
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"1. Fetch the question
1. 获取问题详情
post_id = 12345
resp = requests.get(f"{BASE}/api/posts/{post_id}/", headers=HEADERS)
resp.raise_for_status()
post = resp.json()
question = post["question"]
post_id = 12345
resp = requests.get(f"{BASE}/api/posts/{post_id}/", headers=HEADERS)
resp.raise_for_status()
post = resp.json()
question = post["question"]
2. Define your percentile beliefs (in real-world units)
2. 定义你的分位数信念(现实世界单位)
percentiles = {
"percentile_05": 25,
"percentile_25": 40,
"percentile_50": 55,
"percentile_75": 70,
"percentile_95": 90,
}
percentiles = {
"percentile_05": 25,
"percentile_25": 40,
"percentile_50": 55,
"percentile_75": 70,
"percentile_95": 90,
}
3. Generate and standardize the CDF
3. 生成并标准化CDF
raw_cdf = generate_continuous_cdf(
percentiles,
question,
below_lower_bound=0.01,
above_upper_bound=0.01,
)
final_cdf = standardize_cdf(raw_cdf, question)
raw_cdf = generate_continuous_cdf(
percentiles,
question,
below_lower_bound=0.01,
above_upper_bound=0.01,
)
final_cdf = standardize_cdf(raw_cdf, question)
4. Submit
4. 提交预测
resp = requests.post(
f"{BASE}/api/questions/forecast/",
headers={**HEADERS, "Content-Type": "application/json"},
json=[{"question": question["id"], "continuous_cdf": final_cdf}],
)
resp.raise_for_status()
print("Forecast submitted!")
---resp = requests.post(
f"{BASE}/api/questions/forecast/",
headers={**HEADERS, "Content-Type": "application/json"},
json=[{"question": question["id"], "continuous_cdf": final_cdf}],
)
resp.raise_for_status()
print("预测提交成功!")
---Common Patterns
常见使用模式
Browse the Feed
浏览信息流
bash
undefinedbash
undefinedNewest open questions
最新的开放问题
curl -s "https://www.metaculus.com/api/posts/?statuses=open&order_by=-published_at&limit=10&with_cp=true"
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
undefinedcurl -s "https://www.metaculus.com/api/posts/?statuses=open&order_by=-published_at&limit=10&with_cp=true"
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
undefinedGet Community Prediction for a Post
获取帖子的社区预测
bash
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
| jq '.question.aggregations.recency_weighted.latest'bash
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
| jq '.question.aggregations.recency_weighted.latest'Find Trending Questions
查找热门问题
bash
curl -s "https://www.metaculus.com/api/posts/?order_by=-hotness&statuses=open&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'bash
curl -s "https://www.metaculus.com/api/posts/?order_by=-hotness&statuses=open&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'Find Questions by Category
按分类查找问题
bash
curl -s "https://www.metaculus.com/api/posts/?categories=nuclear&statuses=open&with_cp=true&limit=10" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'bash
curl -s "https://www.metaculus.com/api/posts/?categories=nuclear&statuses=open&with_cp=true&limit=10" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'Find Questions in a Tournament
查找锦标赛中的问题
bash
curl -s "https://www.metaculus.com/api/posts/?tournaments=aibq3&with_cp=true&limit=50" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'bash
curl -s "https://www.metaculus.com/api/posts/?tournaments=aibq3&with_cp=true&limit=50" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'Submit a Binary Forecast
提交二元问题预测
bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'Submit a Multiple Choice Forecast
提交多选问题预测
bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{
"question": 12345,
"probability_yes_per_category": {
"Option A": 0.4,
"Option B": 0.35,
"Option C": 0.25
}
}]'bash
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{
"question": 12345,
"probability_yes_per_category": {
"选项A": 0.4,
"选项B": 0.35,
"选项C": 0.25
}
}]'Withdraw a Forecast
撤回预测
bash
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'bash
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'Read Comments on a Post
查看帖子的评论
bash
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {author: .author.username, text: .text}'bash
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {author: .author.username, text: .text}'Post a Comment
发布评论
bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "My analysis suggests a higher probability because...",
"included_forecast": true,
"is_private": false
}'bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "我的分析表明概率更高,因为...",
"included_forecast": true,
"is_private": false
}'Reply to a Comment
回复评论
bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"parent": 67890,
"text": "Good point — I updated my forecast accordingly.",
"included_forecast": false,
"is_private": false
}'bash
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"parent": 67890,
"text": "好观点——我已相应更新了我的预测。",
"included_forecast": false,
"is_private": false
}'Python: Paginate Through All Open Questions
Python:分页获取所有开放问题
python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
all_posts = []
offset = 0
limit = 100
while True:
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"limit": limit,
"offset": offset,
"with_cp": True,
},
)
resp.raise_for_status()
data = resp.json()
results = data["results"]
all_posts.extend(results)
if data["next"] is None:
break
offset += limit
print(f"Fetched {len(all_posts)} open posts")python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
all_posts = []
offset = 0
limit = 100
while True:
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"limit": limit,
"offset": offset,
"with_cp": True,
},
)
resp.raise_for_status()
data = resp.json()
results = data["results"]
all_posts.extend(results)
if data["next"] is None:
break
offset += limit
print(f"已获取{len(all_posts)}个开放帖子")Python: Find Questions with Large Movement
Python:查找变化较大的问题
python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"order_by": "-weekly_movement",
"forecast_type": "binary",
"with_cp": True,
"limit": 10,
},
)
resp.raise_for_status()
for post in resp.json()["results"]:
q = post.get("question")
if q and q.get("aggregations"):
latest = q["aggregations"].get("recency_weighted", {}).get("latest")
if latest:
prob = latest.get("centers", [None])[0]
print(f"[{post['id']}] {post['title']} — CP: {prob}")python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"order_by": "-weekly_movement",
"forecast_type": "binary",
"with_cp": True,
"limit": 10,
},
)
resp.raise_for_status()
for post in resp.json()["results"]:
q = post.get("question")
if q and q.get("aggregations"):
latest = q["aggregations"].get("recency_weighted", {}).get("latest")
if latest:
prob = latest.get("centers", [None])[0]
print(f"[{post['id']}] {post['title']} — 社区预测概率: {prob}")Question Type Quick Reference
问题类型速查
| Type | Forecast Format | Key Fields |
|---|---|---|
| | — |
| | |
| | |
| | |
| | |
| 类型 | 预测格式 | 关键字段 |
|---|---|---|
| | — |
| | |
| | |
| | |
| | |
Post Status Lifecycle
帖子状态生命周期
draft → pending → approved → open → closed → resolved
→ rejected| Status | Description |
|---|---|
| Author is still editing |
| Submitted for curation review |
| Rejected by curators |
| Approved and accepting forecasts |
| Approved but not yet open for forecasting |
| No longer accepting forecasts; awaiting resolution |
| Final outcome determined |
draft → pending → approved → open → closed → resolved
→ rejected| 状态 | 描述 |
|---|---|
| 作者仍在编辑中 |
| 已提交等待审核 |
| 被审核人员拒绝 |
| 已通过审核并开放预测 |
| 已通过审核但尚未开放预测 |
| 停止接受预测,等待结果判定 |
| 已确定最终结果 |
Error Handling
错误处理
| Status Code | Meaning | Action |
|---|---|---|
| Success | — |
| Created | — |
| Bad request | Check request format, CDF validity, required fields |
| Unauthorized | Check your |
| Forbidden | You don't have permission (e.g., project data download) |
| Not found | Check the post/question/project ID |
| Rate limited | Implement exponential backoff and retry |
| Server error | Retry after a delay; if persistent, contact Metaculus |
| 状态码 | 含义 | 处理方式 |
|---|---|---|
| 请求成功 | — |
| 创建成功 | — |
| 请求格式错误 | 检查请求格式、CDF有效性、必填字段 |
| 未授权 | 检查 |
| 禁止访问 | 你无此权限(例如:下载项目数据) |
| 资源不存在 | 检查帖子/问题/项目ID |
| 请求频率超限 | 实现指数退避机制后重试 |
| 服务器错误 | 延迟后重试;如果持续出现,联系Metaculus团队 |
Usage Tips
使用技巧
- Always pass when listing posts if you need community predictions — aggregations are empty by default.
with_cp=true - Use only when you need historical CP data — it increases response size significantly.
include_cp_history=true - Use only when needed — descriptions can be large.
include_descriptions=true - Question ID ≠ Post ID — Forecasts use the , not the
question.id. Always fetch the post first to get the question ID.post.id - For group posts, on the list endpoint only returns CP for the top 3 sub-questions. Use the detail endpoint (
with_cp=true) for all sub-questions./api/posts/{postId}/ - CDF generation is the trickiest part — use the provided Python functions or adapt them. Always run before submitting.
standardize_cdf() - Combine filters for targeted queries — e.g., narrows results efficiently.
statuses=open&forecast_type=binary&categories=nuclear - Paginate responsibly — use and
limit. Don't fetch all posts at once.offset - The sort requires
order_by=score— it ranks questions by a specific user's performance.forecaster_id - Date questions use unix timestamps in and
scaling.range_min— convert ISO dates to timestamps before mapping to CDF locations.scaling.range_max - Respect rate limits — implement backoff when you receive 429 responses.
- 始终添加:如果你需要社区预测,在列出帖子时必须添加此参数——聚合结果默认是空的。
with_cp=true - 仅在需要时使用:它会显著增加响应大小。
include_cp_history=true - 仅在需要时使用:描述内容可能很大。
include_descriptions=true - 问题ID ≠ 帖子ID:提交预测时使用,而非
question.id。请始终先获取帖子详情以获取问题ID。post.id - 对于问题组帖子:列表接口的仅返回前3个子问题的社区预测。如需所有子问题的社区预测,请使用详情接口(
with_cp=true)。/api/posts/{postId}/ - CDF生成是最复杂的部分:使用提供的Python函数或进行适配。提交前请务必运行。
standardize_cdf() - 组合筛选条件以进行精准查询——例如:可高效缩小结果范围。
statuses=open&forecast_type=binary&categories=nuclear - 合理分页:使用和
limit参数。请勿一次性获取所有帖子。offset - 排序需要
order_by=score:它会按特定用户的预测表现对问题排序。forecaster_id - 日期问题的和
scaling.range_min使用unix时间戳:在映射到CDF位置前,请将ISO日期转换为时间戳。scaling.range_max - 遵守请求频率限制:当收到429响应时,请实现退避机制。
Resources
资源
| Resource | URL |
|---|---|
| Metaculus Platform | metaculus.com |
| API Documentation | metaculus.com/api |
| Account Settings (API Token) | metaculus.com/accounts/settings |
| GitHub Issues | github.com/Metaculus/metaculus |
| Contact | api-requests@metaculus.com |
| 资源 | URL |
|---|---|
| Metaculus平台 | metaculus.com |
| API文档 | metaculus.com/api |
| 账户设置(API Token) | metaculus.com/accounts/settings |
| GitHub问题反馈 | github.com/Metaculus/metaculus |
| 联系方式 | api-requests@metaculus.com |