Overview
The Analytics API provides comprehensive metrics for your projects and affiliate codes, including views, downloads, playtime statistics, and revenue data. All analytics data is aggregated into time slices for efficient querying.
Analytics endpoints require authentication with appropriate scopes:
ANALYTICS scope for views, downloads, and playtime
PAYOUTS_READ scope for revenue data
Fetch Analytics Data
Fetch analytics data with customizable time ranges, metrics, and bucketing options.
Request Body
Defines the time period and resolution for analytics data. Start date/time in ISO 8601 format (RFC 3339) Example: 2024-01-01T00:00:00Z
End date/time in ISO 8601 format (RFC 3339) Example: 2024-01-31T23:59:59Z
How to divide the time range into slices. Options:
{ "slices": 30 } - Fixed number of time slices
{ "minutes": 60 } - Each slice is N minutes long
Constraints:
Minimum resolution: 60 minutes
Maximum slices: 1024
Specify which metrics to return and how to group them. Show Return Metrics Object
Page view metrics.
project_id - Group by project
domain - Group by referrer domain
site_path - Group by visited path
monetized - Group by monetization status
country - Group by country (anonymized for fewer than 50 views)
Download metrics.
project_id - Group by project
version_id - Group by version
domain - Group by referrer domain
site_path - Group by download path
country - Group by country (anonymized for fewer than 50 downloads)
Playtime metrics in seconds.
project_id - Group by project
version_id - Group by version
loader - Group by mod loader (Fabric, Forge, etc.)
game_version - Group by game version
Revenue metrics (requires PAYOUTS_READ scope).
project_id - Group by project
Affiliate code click metrics.
affiliate_code_id - Group by affiliate code
affiliate_code_conversions
Affiliate code conversion metrics.
affiliate_code_id - Group by affiliate code
Affiliate code revenue (requires PAYOUTS_READ scope).
affiliate_code_id - Group by affiliate code
Returns an array of time slices, where each slice contains analytics data for that time period.
Array of time slices (length determined by resolution). Array of analytics data points in this slice. Each data point is either project or affiliate code analytics: Project Analytics: Project ID this data is for
Type of metric: views, downloads, playtime, or revenue
Any requested bucket fields (domain, version_id, etc.)
The metric value:
views - View count
downloads - Download count
seconds - Playtime in seconds
revenue - Revenue amount (decimal)
Affiliate Code Analytics: Affiliate code ID this data is for
Type: clicks, conversions, or revenue
Examples
Basic Views and Downloads
curl -X POST "https://api.modrinth.com/v3/analytics" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"time_range": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z",
"resolution": { "slices": 31 }
},
"return_metrics": {
"project_views": {
"bucket_by": ["project_id"]
},
"project_downloads": {
"bucket_by": ["project_id"]
}
}
}'
Downloads by Version
curl -X POST "https://api.modrinth.com/v3/analytics" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"time_range": {
"start": "2024-03-01T00:00:00Z",
"end": "2024-03-08T00:00:00Z",
"resolution": { "slices": 7 }
},
"return_metrics": {
"project_downloads": {
"bucket_by": ["project_id", "version_id"]
}
}
}'
Views by Referrer
curl -X POST "https://api.modrinth.com/v3/analytics" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"time_range": {
"start": "2024-03-01T00:00:00Z",
"end": "2024-03-02T00:00:00Z",
"resolution": { "minutes": 60 }
},
"return_metrics": {
"project_views": {
"bucket_by": ["domain", "monetized"]
}
}
}'
Playtime Statistics
curl -X POST "https://api.modrinth.com/v3/analytics" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"time_range": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-02-01T00:00:00Z",
"resolution": { "slices": 31 }
},
"return_metrics": {
"project_playtime": {
"bucket_by": ["project_id", "loader", "game_version"]
}
}
}'
Revenue Data
curl -X POST "https://api.modrinth.com/v3/analytics" \
-H "Authorization: Bearer YOUR_TOKEN_WITH_PAYOUTS_READ" \
-H "Content-Type: application/json" \
-d '{
"time_range": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-12-31T23:59:59Z",
"resolution": { "slices": 12 }
},
"return_metrics": {
"project_revenue": {
"bucket_by": ["project_id"]
}
}
}'
Example Response
[
[
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "youtube.com" ,
"views" : 1523
},
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "downloads" ,
"version_id" : "XYZ12345" ,
"downloads" : 456
}
],
[
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "discord.com" ,
"views" : 892
}
]
]
Bucketing Behavior
Bucketing allows you to group analytics data by specific dimensions:
When bucket_by is empty, all data for that metric is aggregated: {
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"views" : 5000
}
Bucketing by one field creates separate entries: [
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "youtube.com" ,
"views" : 3000
},
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "discord.com" ,
"views" : 2000
}
]
Multiple bucket fields create combinations: [
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "youtube.com" ,
"monetized" : true ,
"views" : 2500
},
{
"source_project" : "ABCDEFGH" ,
"metric_kind" : "views" ,
"domain" : "youtube.com" ,
"monetized" : false ,
"views" : 500
}
]
Data Sources
Clickhouse Views, downloads, playtime, and affiliate click data is stored in Clickhouse for high-performance querying
PostgreSQL Revenue and conversion data comes from PostgreSQL transaction records
Privacy & Anonymization
Country data is automatically anonymized when counts are below 50 to protect user privacy. Low-traffic countries are reported as “XX”.
Best Practices
Choose resolution based on your time range:
Hours : Use minute-based resolution
Days/Weeks : Use 24-96 slices
Months : Use 30-31 slices
Years : Use 12 or 52 slices
Only bucket by fields you need:
Fewer buckets = faster queries
More buckets = more detailed data
Consider client-side aggregation for flexibility
Keep queries reasonable:
Maximum 1024 time slices
Minimum 60-minute resolution
Larger ranges need coarser resolution
Request multiple metrics in one call for efficiency: {
"return_metrics" : {
"project_views" : { "bucket_by" : [] },
"project_downloads" : { "bucket_by" : [] },
"project_playtime" : { "bucket_by" : [] }
}
}
Permissions
Analytics data is filtered by project permissions:
You can only view analytics for projects where you have VIEW_ANALYTICS permission
This includes:
Projects you own
Projects where you’re a team member with analytics access
Projects in organizations where you have the appropriate role
Revenue data requires the PAYOUTS_READ scope in addition to ANALYTICS. Requests without this scope will return an authentication error when accessing revenue metrics.