Overview
Projects are the organizational structure of Metaculus. They group related questions together and control access, visibility, and governance. Every question belongs to exactly one primary project and can be tagged to multiple additional projects.
Project Types
Metaculus supports multiple project types for different use cases (from projects/models.py:213-223):
class ProjectTypes ( models . TextChoices ):
SITE_MAIN = "site_main" # Main Metaculus site
INDEX = "index" # Aggregate indices (e.g., democracy index)
TOURNAMENT = "tournament" # Time-bound competitions with prizes
QUESTION_SERIES = "question_series" # Ongoing question collections
PERSONAL_PROJECT = "personal_project" # User-created collections
NEWS_CATEGORY = "news_category" # News and current events
CATEGORY = "category" # General topic categories
LEADERBOARD_TAG = "leaderboard_tag" # Tags for leaderboard filtering
TOPIC = "topic" # Subject matter tags
COMMUNITY = "community" # Community-run projects
Tournaments Competitions with prizes and leaderboards
Communities Independent forecasting communities
Categories Organize by topic (AI, climate, politics)
Question Series Ongoing question collections on specific topics
Indices Aggregate metrics from multiple questions
Personal Projects Custom question collections
Project Structure
From projects/models.py:212-402, the core project model:
class Project ( TimeStampedModel , TranslatedModel ):
type = models.CharField( max_length = 32 , choices = ProjectTypes.choices)
# Identification
name = models.CharField( max_length = 200 )
slug = models.CharField( max_length = 200 , validators = [validate_alpha_slug])
# Description and branding
subtitle = models.CharField( max_length = 255 , blank = True , default = "" )
description = models.TextField( blank = True , default = "" )
header_image = models.ImageField( null = True , blank = True )
header_logo = models.ImageField( null = True , blank = True )
emoji = models.CharField( max_length = 10 , default = "" , blank = True )
# Tournament/Competition fields
prize_pool = models.DecimalField( decimal_places = 2 , max_digits = 15 , null = True )
start_date = models.DateTimeField( null = True , blank = True )
close_date = models.DateTimeField( null = True , blank = True )
forecasting_end_date = models.DateTimeField( null = True , blank = True )
# Leaderboards
primary_leaderboard = models.ForeignKey( "scoring.Leaderboard" , null = True )
# Permissions
default_permission = models.CharField( choices = ObjectPermission.choices)
override_permissions = models.ManyToManyField(User, through = "ProjectUserPermission" )
created_by = models.ForeignKey(User, on_delete = models. CASCADE )
# Visibility
visibility = models.CharField( choices = Visibility.choices)
show_on_homepage = models.BooleanField( default = False )
# Metrics
followers_count = models.PositiveIntegerField( default = 0 )
forecasts_count = models.PositiveIntegerField( default = 0 )
forecasters_count = models.PositiveIntegerField( default = 0 )
Question-Project Relationship
Questions are associated with projects through posts:
Primary Project
Every question belongs to exactly one primary project through its post (from questions/models.py:249-259):
post = models.ForeignKey(
"posts.Post" ,
on_delete = models. CASCADE ,
related_name = "questions" ,
)
# Post has:
default_project = models.ForeignKey(Project, on_delete = models. CASCADE )
Posts can be tagged to multiple additional projects:
projects = models.ManyToManyField(Project, related_name = "posts" )
The primary project (default_project) controls governance, while additional projects (projects) provide categorization and discovery.
Permissions
Projects use a hierarchical permission system to control access.
Permission Levels
From projects/permissions.py, the available permissions are:
null : Private project - no access
VIEWER : Can view questions and forecasts
FORECASTER : Can view and make predictions
CURATOR : Can view, forecast, and help curate content
ADMIN : Full control over project
Default Permission
From projects/models.py:332-339:
default_permission = models.CharField(
choices = ObjectPermission.choices,
null = True , # null -> private
blank = True ,
default = ObjectPermission. FORECASTER ,
db_index = True ,
)
Setting default_permission = null makes the project completely private - only users with explicit permission overrides can access it.
Permission Overrides
Individual users can have custom permissions (from projects/models.py:498-513):
class ProjectUserPermission ( TimeStampedModel ):
user = models.ForeignKey(User, on_delete = models. CASCADE )
project = models.ForeignKey(Project, on_delete = models. CASCADE )
permission = models.CharField( choices = ObjectPermission.choices)
class Meta :
constraints = [
models.UniqueConstraint(
name = "projectuserpermission_unique_user_id_project_id" ,
fields = [ "user_id" , "project_id" ],
),
]
Permission Resolution
From projects/models.py:162-191, permissions are resolved with priority:
Superuser : Automatically get ADMIN permission
Creator : Project creator gets ADMIN permission
Override : Explicit ProjectUserPermission if set
Default : Falls back to project’s default_permission
def annotate_user_permission ( self , user : User = None ):
if user and user.is_superuser:
return self .annotate( user_permission = models.Value(ObjectPermission. ADMIN ))
return self .annotate(
user_permission = models.Case(
models.When(
Q( created_by_id__isnull = False , created_by_id = user_id),
then = models.Value(ObjectPermission. ADMIN ),
),
default = Coalesce(
F( "_user_permission_override__permission" ),
F( "default_permission" ),
),
),
)
Visibility
Projects control how they appear across the platform.
Visibility Options
From projects/models.py:229-362:
class Visibility ( models . TextChoices ):
NORMAL = "normal"
NOT_IN_MAIN_FEED = "not_in_main_feed"
UNLISTED = "unlisted"
Normal
Not In Main Feed
Unlisted
Visible on the main feed
Questions contribute to global leaderboards and medals
Listed on tournaments/question series page
Use for : Public competitions and major question collections
Not visible in main feed but searchable
Does NOT contribute to global leaderboards/medals
Listed on tournaments/question series page
Use for : Specialized topics that shouldn’t clutter the main feedDefault for : Most project types
Not visible in main feed
Not searchable
Does NOT contribute to global leaderboards/medals
Not listed on tournaments page
Use for : Work-in-progress tournaments or private collectionsDefault for : New tournaments/question series
Homepage Display
From projects/models.py:364-368:
show_on_homepage = models.BooleanField( default = False , db_index = True )
show_on_services_page = models.BooleanField(
default = False , db_index = True , help_text = "Show project on the Services page."
)
Featured projects can be promoted to the homepage for maximum visibility.
Communities
Communities are projects run by external organizations or user groups.
Independent governance (custom admins)
Own leaderboards and competitions
Custom branding (header images, logos)
Separate permission management
Can host tournaments within the community
Creating Communities
Communities are created with type=COMMUNITY and automatically get a primary leaderboard (from projects/models.py:422-439).
Indices
Indices aggregate multiple questions into a single metric.
Index Structure
From projects/models.py:533-563:
class ProjectIndex ( TimeStampedModel ):
class IndexType ( models . TextChoices ):
DEFAULT = "default"
MULTI_YEAR = "multi_year"
type = models.CharField( max_length = 32 , choices = IndexType.choices)
min = models.SmallIntegerField( default =- 100 , help_text = "Y-axis min" )
min_label = models.CharField(
max_length = 200 ,
help_text = 'Label at minimum end. Example: "Less democratic"' ,
)
max = models.SmallIntegerField( default = 100 , help_text = "Y-axis max" )
max_label = models.CharField(
max_length = 200 ,
help_text = 'Label at maximum end. Example: "More democratic"' ,
)
increasing_is_good = models.BooleanField(
default = True ,
help_text = "Color polarity: if on, higher values are good (green → right)" ,
)
Index Post Weights
Questions contribute to indices with configurable weights (from projects/models.py:566-614):
class ProjectIndexPost ( TimeStampedModel ):
index = models.ForeignKey(ProjectIndex, on_delete = models. CASCADE )
post = models.ForeignKey( "posts.Post" , on_delete = models. CASCADE )
weight = models.FloatField(
help_text = (
"Weight of the post within the index. "
"If the post includes a group of questions, "
"the same weight will be applied to all subquestions."
),
default = 1.0 ,
)
order = models.IntegerField( default = 0 )
Example : A democracy index might weight “free elections” at 2.0 and “press freedom” at 1.5.
Project Metrics
Projects track engagement metrics (from projects/models.py:375-379):
followers_count = models.PositiveIntegerField( default = 0 , editable = False )
forecasts_count = models.PositiveIntegerField( default = 0 , editable = False )
forecasters_count = models.PositiveIntegerField( default = 0 , editable = False )
These are updated via (from projects/models.py:470-495):
def update_followers_count ( self ):
self .followers_count = self .subscriptions.count()
def update_forecasts_count ( self ):
result = Post.objects.filter_projects( self ).aggregate(
total_forecasts = Sum( "forecasts_count" )
)
self .forecasts_count = result[ "total_forecasts" ] or 0
def update_forecasters_count ( self ):
self .forecasters_count = (
PostUserSnapshot.objects.filter(
last_forecast_date__isnull = False ,
user__is_staff = False ,
)
.filter(Q( post__default_project = self ) | Q( post__projects = self ))
.values( "user_id" )
.distinct()
.count()
)
Subscriptions
Users can follow projects to receive updates (from projects/models.py:516-530):
class ProjectSubscription ( TimeStampedModel ):
user = models.ForeignKey(
User, on_delete = models. CASCADE , related_name = "project_subscriptions"
)
project = models.ForeignKey(
Project, on_delete = models. CASCADE , related_name = "subscriptions"
)
class Meta :
constraints = [
models.UniqueConstraint(
name = "projectsubscription_unique_user_project" ,
fields = [ "user_id" , "project_id" ],
)
]
Subscribe to projects you’re interested in to get notifications when new questions are added or important updates occur.
Querying Projects
Metaculus provides powerful querysets for filtering projects.
By Type
From projects/models.py:24-47:
Project.objects.filter_tournament() # Tournaments and question series
Project.objects.filter_communities() # Communities
Project.objects.filter_topic() # Topic tags
Project.objects.filter_category() # Category tags
Project.objects.filter_leaderboard_tags() # Leaderboard filtering tags
By Permission
From projects/models.py:193-209:
# Get all projects user can access
Project.objects.filter_permission( user = user)
# Get projects where user can forecast
Project.objects.filter_permission( user = user, permission = ObjectPermission. FORECASTER )
# Get projects where user is admin
Project.objects.filter_permission( user = user, permission = ObjectPermission. ADMIN )
With Annotations
From projects/models.py:49-159:
# Annotate with post count
Project.objects.annotate_posts_count()
# Annotate with question count
Project.objects.annotate_questions_count()
# Annotate with user's subscription status
Project.objects.annotate_is_subscribed( user = user)
# Annotate with user's permission level
Project.objects.annotate_user_permission( user = user)
Use Cases
Create a TOURNAMENT project with prize pool, set start/close dates, configure leaderboard, add curated questions.
Create a TOPIC or CATEGORY project, tag relevant questions via post.projects, users can filter by topic.
Create an INDEX project, configure scale and labels, add weighted questions via ProjectIndexPost.
Create a PERSONAL_PROJECT, set default_permission=null for privacy, organize questions you’re tracking.
Best Practices
Choose the Right Type
Select the project type that matches your use case. Don’t use TOURNAMENT for simple question collections.
Set Appropriate Permissions
Consider who should access your project. Most public projects use FORECASTER as default.
Configure Visibility
Use UNLISTED for work-in-progress, NOT_IN_MAIN_FEED for specialized content, NORMAL for featured content.
Add Clear Descriptions
Write comprehensive descriptions and resolution criteria so participants understand the project goals.
Enable Subscriptions
Make your project subscribable so interested users can follow updates and new questions.
API Reference
Projects API Explore the full Projects API documentation
Tournaments Learn about competitive forecasting
Leaderboards Understand project-specific rankings
Questions Learn about organizing questions
Permissions Deep dive into permission management