This guide shows you how to submit forecasts for different question types using the Metaculus API.
Prerequisites
All API requests require authentication. Make sure you have:
- Generated an API token from your account settings
- Include the token in the
Authorization header: Token YOUR_API_TOKEN
Binary Questions
Binary questions have yes/no outcomes. Submit a probability between 0 and 1.
Get the question details
First, retrieve the question to get its ID and confirm itβs a binary question:import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Get post details
post_id = 1
response = requests.get(
f"https://www.metaculus.com/api/posts/{post_id}/",
headers=headers
)
post_data = response.json()
question = post_data["question"]
print(f"Question ID: {question['id']}")
print(f"Type: {question['type']}")
print(f"Title: {question['title']}")
Submit your binary forecast
Submit a probability for the yes outcome:import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Submit binary forecast
forecast_data = [
{
"question": 1, # Question ID
"probability_yes": 0.63
}
]
response = requests.post(
"https://www.metaculus.com/api/questions/forecast/",
headers=headers,
json=forecast_data
)
if response.status_code == 201:
print("Forecast submitted successfully!")
else:
print(f"Error: {response.status_code}")
print(response.json())
Numeric Questions (Continuous)
Numeric questions require a 201-point CDF (Cumulative Distribution Function). For detailed guidance on generating CDFs, see the Continuous CDF Guide.
Get the question scaling
Retrieve the question details to understand its range and scaling:import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Get question details
post_id = 3530
response = requests.get(
f"https://www.metaculus.com/api/posts/{post_id}/",
headers=headers
)
question = response.json()["question"]
print(f"Type: {question['type']}")
print(f"Range: {question['scaling']['range_min']} to {question['scaling']['range_max']}")
print(f"Unit: {question['unit']}")
print(f"Open lower bound: {question['open_lower_bound']}")
print(f"Open upper bound: {question['open_upper_bound']}")
Generate a CDF
Create a 201-point CDF. This example shows a simple approach:import numpy as np
# For a question with closed bounds (range_min to range_max)
# Generate a simple normal-like distribution
def simple_cdf(mean_location=0.5, spread=0.3):
"""
Generate a 201-point CDF.
mean_location: center of distribution (0 to 1)
spread: how spread out the distribution is
"""
points = np.linspace(0, 1, 201)
# Simple sigmoid-based CDF
cdf = 1 / (1 + np.exp(-(points - mean_location) / spread))
# Normalize to [0, 1]
cdf = (cdf - cdf.min()) / (cdf.max() - cdf.min())
return cdf.tolist()
my_cdf = simple_cdf(mean_location=0.6, spread=0.15)
print(f"CDF has {len(my_cdf)} points")
print(f"First value: {my_cdf[0]:.5f}, Last value: {my_cdf[-1]:.5f}")
For production use, see the Continuous CDF Guide for proper CDF generation that handles:
- Linear and logarithmic scaling
- Open and closed bounds
- Percentile-based distributions
- CDF standardization and validation
Submit your numeric forecast
Submit the CDF to the API:import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Submit continuous forecast
forecast_data = [
{
"question": 3530,
"continuous_cdf": my_cdf # 201-point list
}
]
response = requests.post(
"https://www.metaculus.com/api/questions/forecast/",
headers=headers,
json=forecast_data
)
if response.status_code == 201:
print("Forecast submitted successfully!")
else:
print(f"Error: {response.status_code}")
print(response.json())
Multiple Choice Questions
For multiple choice questions, provide probabilities for each option that sum to 1.0.
import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Submit multiple choice forecast
forecast_data = [
{
"question": 20772,
"probability_yes_per_category": {
"Democratic": 0.45,
"Republican": 0.40,
"Libertarian": 0.08,
"Green": 0.05,
"Other": 0.02
}
}
]
response = requests.post(
"https://www.metaculus.com/api/questions/forecast/",
headers=headers,
json=forecast_data
)
if response.status_code == 201:
print("Forecast submitted successfully!")
The probabilities must sum to exactly 1.0, or the API will reject your forecast.
Group of Questions
For posts containing multiple sub-questions, submit forecasts for each one:
import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Submit forecasts for multiple questions in a group
forecast_data = [
{"question": 10880, "probability_yes": 0.11}, # 2030
{"question": 10923, "probability_yes": 0.22}, # 2035
{"question": 10924, "probability_yes": 0.33} # 2040
]
response = requests.post(
"https://www.metaculus.com/api/questions/forecast/",
headers=headers,
json=forecast_data
)
if response.status_code == 201:
print("All forecasts submitted successfully!")
Withdrawing Forecasts
You can withdraw your current forecast on a question:
import requests
API_TOKEN = "your_api_token_here"
headers = {"Authorization": f"Token {API_TOKEN}"}
# Withdraw forecast
withdrawal_data = [
{"question": 1}
]
response = requests.post(
"https://www.metaculus.com/api/questions/withdraw/",
headers=headers,
json=withdrawal_data
)
if response.status_code == 201:
print("Forecast withdrawn successfully!")
Next Steps