You do not need a token for most requests. Generally speaking, only the following types of requests require a token:
- those which create data (such as version creation)
- those which modify data (such as editing a project)
- those which access private data (such as draft projects, notifications, emails, and payout data)
Each request requiring authentication has a certain scope. For example, to view the email of the user being requested, the token must have the `USER_READ_EMAIL` scope.
You can find the list of available scopes [on GitHub](https://github.com/modrinth/labrinth/blob/master/src/models/pats.rs#L15). Making a request with an invalid scope will return a 401 error.
Please note that certain scopes and requests cannot be completed with a personal access token or using OAuth.
For example, deleting a user account can only be done through Modrinth's frontend.
### OAuth2
Applications interacting with the authenticated API should create an OAuth2 application.
You can do this in [the developer settings](https://modrinth.com/settings/applications).
Once you have created a client, use the following URL to have a user authorize your client:
This route will be changed in the future to move the `_internal` part to `v3`.
### Personal access tokens
Personal access tokens (PATs) can be generated in from [the user settings](https://modrinth.com/settings/account).
### GitHub tokens
For backwards compatibility purposes, some types of GitHub tokens also work for authenticating a user with Modrinth's API, granting all scopes.
**Weurge any application still using GitHub tokens to start using personal access tokens for security and reliability purposes.**
GitHub tokens will cease to function to authenticate with Modrinth's API as soon as version 3 of the API is made generally available.
## Cross-Origin Resource Sharing
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with the [W3C spec](https://www.w3.org/TR/cors/).
This allows for cross-domain communication from the browser.
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
## Identifiers
The majority of items you can interact with in the API have a unique eight-digit base62 ID.
Projects, versions, users, threads, teams, and reports all use this same way of identifying themselves.
Version files use the sha1 or sha512 file hashes as identifiers.
Each project and user has a friendlier way of identifying them; slugs and usernames, respectively.
While unique IDs are constant, slugs and usernames can change at any moment.
If you want to store something in the long term, it is recommended to use the unique ID.
## Ratelimits
The API has a ratelimit defined per IP. Limits and remaining amounts are given in the response headers.
- `X-Ratelimit-Limit`:the maximum number of requests that can be made in a minute
- `X-Ratelimit-Remaining`:the number of requests remaining in the current ratelimit window
- `X-Ratelimit-Reset`:the time in seconds until the ratelimit window resets
Ratelimits are the same no matter whether you use a token or not.
The ratelimit is currently 300 requests per minute. If you have a use case requiring a higher limit, please [contact us](mailto:admin@modrinth.com).
## User Agents
To access the Modrinth API, you **must** use provide a uniquely-identifying `User-Agent` header.
Providing a user agent that only identifies your HTTP client library (such as "okhttp/4.9.3") increases the likelihood that we will block your traffic.
It is recommended, but not required, to include contact information in your user agent.
This allows us to contact you if we would like a change in your application's behavior without having to block your traffic.
- Best: `User-Agent: github_username/project_name/1.56.0 (launcher.com)` or `User-Agent:github_username/project_name/1.56.0 (contact@launcher.com)`
## Versioning
Modrinth follows a simple pattern for its API versioning.
In the event of a breaking API change, the API version in the URL path is bumped, and migration steps will be published below.
When an API is no longer the current one, it will immediately be considered deprecated.
Nomore support will be provided for API versions older than the current one.
It will be kept for some time, but this amount of time is not certain.
We will exercise various tactics to get people to update their implementation of our API.
One example is by adding something like `STOP USING THIS API` to various data returned by the API.
Once an API version is completely deprecated, it will permanently return a 410 error.
Please ensure your application handles these 410 errors.
### Migrations
Inside the following spoiler, you will be able to find all changes between versions of the Modrinth API, accompanied by tips and a guide to migrate applications to newer versions.
Here, you can also find changes for [Minotaur](https://github.com/modrinth/minotaur), Modrinth's official Gradle plugin. Major versions of Minotaur directly correspond to major versions of the Modrinth API.
<details><summary>API v1 to API v2</summary>
These bullet points cover most changes in the v2 API, but please note that fields containing `mod` in most contexts have been shifted to `project`. For example, in the search route, the field `mod_id` was renamed to `project_id`.
- The search route has been moved from `/api/v1/mod` to `/v2/search`
- New project fields:`project_type` (may be `mod` or `modpack`), `moderation_message` (which has a `message` and `body`), `gallery`
- New search facet:`project_type`
- Alphabetical sort removed (it didn't work and is not possible due to limits in MeiliSearch)
- New search fields:`project_type`, `gallery`
- The gallery field is an array of URLs to images that are part of the project's gallery
- The gallery is a new feature which allows the user to upload images showcasing their mod to the CDN which will be displayed on their mod page
- Internal change:Any project file uploaded to Modrinth is now validated to make sure it's a valid Minecraft mod, Modpack, etc.
- For example, a Forge 1.17 mod with a JAR not containing a mods.toml will not be allowed to be uploaded to Modrinth
- In project creation, projects may not upload a mod with no versions to review, however they can be saved as a draft
- Similarly, for version creation, a version may not be uploaded without any files
- Donation URLs have been enabled
- New project status:`archived`. Projects with this status do not appear in search
- Tags (such as categories, loaders) now have icons (SVGs) and specific project types attached
- Dependencies have been wiped and replaced with a new system
- Notifications now have a `type` field, such as `project_update`
Along with this, project subroutes (such as `/v2/project/{id}/version`) now allow the slug to be used as the ID. This is also the case with user routes.
</details><details><summary>Minotaur v1 to Minotaur v2</summary>
Minotaur 2.x introduced a few breaking changes to how your buildscript is formatted.
First, instead of registering your own `publishModrinth` task, Minotaur now automatically creates a `modrinth` task. As such, you can replace the `task publishModrinth(type:TaskModrinthUpload) {` line with just `modrinth {`.
To declare supported Minecraft versions and mod loaders, the `gameVersions` and `loaders` arrays must now be used. The syntax for these are pretty self-explanatory.
Instead of using `releaseType`, you must now use `versionType`. This was actually changed in v1.2.0, but very few buildscripts have moved on from v1.1.0.
Dependencies have been changed to a special DSL. Create a `dependencies` block within the `modrinth` block, and then use `scope.type("project/version")`. For example, `required.project("fabric-api")` adds a required project dependency on Fabric API.
You may now use the slug anywhere that a project ID was previously required.
</details>
# The above snippet about User Agents was adapted from https://crates.io/policies, copyright (c) 2014 The Rust Project Developers under MIT license
servers:
- url:https://api.modrinth.com/v2
description:Production server
- url:https://staging-api.modrinth.com/v2
description:Staging server
components:
parameters:
ProjectIdentifier:
name:id|slug
in:path
required:true
description:The ID or slug of the project
schema:
type:string
example:[AABBCCDD, my_project]
MultipleProjectIdentifier:
in:query
name:ids
description:The IDs and/or slugs of the projects
schema:
type:string
example:"[\"AABBCCDD\", \"EEFFGGHH\"]"
required:true
UserIdentifier:
name:id|username
in:path
required:true
description:The ID or username of the user
schema:
type:string
example:[EEFFGGHH, my_user]
VersionIdentifier:
name:id
in:path
required:true
description:The ID of the version
schema:
type:string
example:[IIJJKKLL]
TeamIdentifier:
name:id
in:path
required:true
description:The ID of the team
schema:
type:string
example:[MMNNOOPP]
ReportIdentifier:
name:id
in:path
required:true
description:The ID of the report
schema:
type:string
example:[RRSSTTUU]
ThreadIdentifier:
name:id
in:path
required:true
description:The ID of the thread
schema:
type:string
example:[QQRRSSTT]
NotificationIdentifier:
name:id
in:path
required:true
description:The ID of the notification
schema:
type:string
example:[NNOOPPQQ]
AlgorithmIdentifier:
name:algorithm
in:query
required:true
description:The algorithm of the hash
schema:
type:string
enum:[sha1, sha512]
example:sha512
default:sha1
MultipleHashQueryIdentifier:
name:multiple
in:query
required:false
description:Whether to return multiple results when looking for this hash
schema:
type:boolean
default:false
FileHashIdentifier:
name:hash
in:path
required:true
description:The hash of the file, considering its byte content, and encoded in hexadecimal
schema:
type:string
example:619e250c133106bacc3e3b560839bd4b324dfda8
requestBodies:
Image:
content:
image/png:
schema:
type:string
format:binary
image/jpeg:
schema:
type:string
format:binary
image/bmp:
schema:
type:string
format:binary
image/gif:
schema:
type:string
format:binary
image/webp:
schema:
type:string
format:binary
image/svg:
schema:
type:string
format:binary
image/svgz:
schema:
type:string
format:binary
image/rgb:
schema:
type:string
format:binary
schemas:
# Version
BaseVersion:
type:object
properties:
name:
type:string
description:The name of this version
example:"Version 1.0.0"
version_number:
type:string
description:"The version number. Ideally will follow semantic versioning"
example:"1.0.0"
changelog:
type:string
description:"The changelog for this version"
example:"List of changes in this version: ..."
nullable:true
dependencies:
type:array
items:
$ref:"#/components/schemas/VersionDependency"
description:A list of specific versions of projects that this version depends on
game_versions:
type:array
items:
type:string
description:A list of versions of Minecraft that this version supports
description:Whether this file is the primary one for its version. Only a maximum of one file per version will have this set to true. If there are not any primary files, it can be inferred that the first file is the primary one.
description:An optional invite link to the project's discord
example:https://discord.gg/AaBbCcDd
nullable:true
donation_urls:
type:array
items:
$ref:'#/components/schemas/ProjectDonationURL'
description:A list of donation links for the project
ProjectDonationURL:
type:object
properties:
id:
type:string
description:The ID of the donation platform
example:patreon
platform:
type:string
description:The donation platform this link is to
example:Patreon
url:
type:string
description:The URL of the donation platform and user
example:https://www.patreon.com/my_user
# Fields available only when editing or creating a project
ModifiableProject:
allOf:
- $ref:'#/components/schemas/NonSearchProject'
- type:object
properties:
license_id:
type:string
description:The SPDX license ID of a project
example:LGPL-3.0-or-later
license_url:
type:string
description:The URL to this license
nullable:true
# Fields that can be edited through a PATCH request. https://github.com/modrinth/labrinth/blob/master/src/routes/projects.rs#L195-L269
EditableProject:
allOf:
- $ref:'#/components/schemas/ModifiableProject'
- type:object
properties:
moderation_message:
type:string
description:The title of the moderators' message for the project
nullable:true
moderation_message_body:
type:string
description:The body of the moderators' message for the project
nullable:true
# Fields only available for project creation. https://github.com/modrinth/labrinth/blob/master/src/routes/project_creation.rs#L129-L197
CreatableProject:
allOf:
- $ref:'#/components/schemas/ModifiableProject'
- type:object
properties:
project_type:
type:string
enum:[mod, modpack]
example:modpack
initial_versions:
type:array
items:
$ref:'#/components/schemas/EditableVersion'
description:A list of initial versions to upload with the created project. Deprecated - please upload version files after initial upload.
deprecated:true
is_draft:
type:boolean
description:Whether the project should be saved as a draft instead of being sent to moderation for review. Deprecated - please always mark this as true.
example:true
deprecated:true
gallery_items:
type:array
description:Gallery images to be uploaded with the created project. Deprecated - please upload gallery images after initial upload.
description:The new status of the project. **Only present for `status_change` message type**
example:approved
required:
- type
# Team
TeamMember:
type:object
properties:
team_id:
type:string
example:MMNNOOPP
description:The ID of the team this team member is a member of
user:
$ref:'#/components/schemas/User'
role:
type:string
example:Member
description:The user's role on the team
permissions:
type:integer
format:bitfield
example:127
description:|
The user's permissions in bitfield format (requires authorization to view)
In order from first to tenth bit, the bits are:
- UPLOAD_VERSION
- DELETE_VERSION
- EDIT_DETAILS
- EDIT_BODY
- MANAGE_INVITES
- REMOVE_MEMBER
- EDIT_MEMBER
- DELETE_PROJECT
- VIEW_ANALYTICS
- VIEW_PAYOUTS
accepted:
type:boolean
example:true
description:Whether or not the user has accepted to be on the team (requires authorization to view)
payouts_split:
type:integer
example:100
description:The split of payouts going to this user. The proportion of payouts they get is their split divided by the sum of the splits of all members.
ordering:
type:integer
example:0
description:The order of the team member.
required:
- team_id
- user
- role
- accepted
# Tags
CategoryTag:
type:object
properties:
icon:
type:string
description:The SVG icon of a category
example:<svg></svg>
name:
type:string
description:The name of the category
example:"adventure"
project_type:
type:string
description:The project type this category is applicable to
example:mod
header:
type:string
description:The header under which the category should go
example:"resolutions"
required:
- icon
- name
- project_type
- header
LoaderTag:
type:object
properties:
icon:
type:string
description:The SVG icon of a loader
example:<svg></svg>
name:
type:string
description:The name of the loader
example:fabric
supported_project_types:
type:array
items:
type:string
description:The project type
description:The project types that this loader is applicable to
example:[mod, modpack]
required:
- icon
- name
- supported_project_types
GameVersionTag:
type:object
properties:
version:
type:string
description:The name/number of the game version
example:1.18.1
version_type:
type:string
enum:[release, snapshot, alpha, beta]
description:The type of the game version
example:release
date:
type:string
format:ISO-8601
description:The date of the game version release
major:
type:boolean
description:Whether or not this is a major version, used for Featured Versions
example:true
required:
- version
- version_type
- date
- major
DonationPlatformTag:
type:object
properties:
short:
type:string
description:The short identifier of the donation platform
example:bmac
name:
type:string
description:The full name of the donation platform
example:Buy Me a Coffee
required:
- short
- name
ModifyTeamMemberBody:
properties:
role:
type:string
example:Contributor
permissions:
type:integer
format:bitfield
example:127
description:|
The user's permissions in bitfield format
In order from first to tenth bit, the bits are:
- UPLOAD_VERSION
- DELETE_VERSION
- EDIT_DETAILS
- EDIT_BODY
- MANAGE_INVITES
- REMOVE_MEMBER
- EDIT_MEMBER
- DELETE_PROJECT
- VIEW_ANALYTICS
- VIEW_PAYOUTS
payouts_split:
type:integer
example:100
description:The split of payouts going to this user. The proportion of payouts they get is their split divided by the sum of the splits of all members.
ordering:
type:integer
example:0
description:The order of the team member.
LicenseTag:
type:object
description:A short overview of a license
properties:
short:
type:string
description:The short identifier of the license
example:lgpl-3
name:
type:string
description:The full name of the license
example:GNU Lesser General Public License v3
required:
- short
- name
License:
type:object
description:A full license
properties:
title:
type:string
example:GNU Lesser General Public License v3.0 or later
body:
type:string
example:Insert the entire text of the LGPL-3.0 here...
description:A list of the recommended and latest versions for each Minecraft release
properties:
"{version}-recommended":
type:string
description:The mod version that is recommended for `{version}`. Excludes versions with the `alpha` and `beta` version types.
"{version}-latest":
type:string
description:The latest mod version for `{version}`. Shows versions with the `alpha` and `beta` version types.
securitySchemes:
TokenAuth:
type:apiKey
in:header
name:Authorization
tags:
- name:projects
x-displayName:Projects
description:Projects are what Modrinth is centered around, be it mods, modpacks, resource packs, etc.
- name:versions
x-displayName:Versions
description:Versions contain download links to files with additional metadata.
- name:version-files
x-displayName:Version Files
description:Versions can contain multiple files, and these routes help manage those files.
- name:users
x-displayName:Users
description:Users can create projects, join teams, access notifications, manage settings, and follow projects. Admins and moderators have more advanced permissions such as reviewing new projects.
- name:notifications
x-displayName:Notifications
description:Notifications are sent to users for various reasons, including for project updates, team invites, and moderation purposes.
- name:threads
x-displayName:Threads
description:Threads are a way of communicating between users and moderators, for the purposes of project reviews and reports.
- name:teams
x-displayName:Teams
description:Through teams, user permissions limit how team members can modify projects.
- name:tags
x-displayName:Tags
description:Tags are common and reusable lists of metadata types such as categories or versions. Some can be applied to projects and/or versions.
summary:Get a version given a version number or ID
description:Please note that, if the version number provided matches multiple versions, only the **oldest matching version** will be returned.
operationId:getVersionFromIdOrNumber
tags:
- versions
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
$ref:'#/components/schemas/Version'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
/version:
post:
summary:Create a version
description:|
This route creates a version on an existing project. There must be at least one file attached to each new version, unless the new version's status is `draft`. `.mrpack`, `.jar`, `.zip`, and `.litemod` files are accepted.
The request is a [multipart request](https://www.ietf.org/rfc/rfc2388.txt) with at least two form fields:one is `data`, which includes a JSON body with the version metadata as shown below, and at least one field containing an upload file.
You can name the file parts anything you would like, but you must list each of the parts' names in `file_parts`, and optionally, provide one to use as the primary file in `primary_file`.
operationId:createVersion
tags:
- versions
security:
- TokenAuth:['VERSION_CREATE']
requestBody:
description:"New version"
content:
multipart/form-data:
schema:
$ref:'#/components/schemas/CreateVersionBody'
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
$ref:'#/components/schemas/Version'
"400":
description:Request was invalid, see given error
content:
application/json:
schema:
$ref:'#/components/schemas/InvalidInputError'
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
description:The requested item(s) were not found or no authorization to access the requested item(s)
patch:
summary:Modify a user
operationId:modifyUser
tags:
- users
security:
- TokenAuth:['USER_WRITE']
requestBody:
description:"Modified user fields"
content:
application/json:
schema:
$ref:'#/components/schemas/EditableUser'
responses:
"204":
description:Expected response to a valid request
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
/user:
get:
summary:Get user from authorization header
operationId:getUserFromAuth
tags:
- users
security:
- TokenAuth:['USER_READ']
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
$ref:'#/components/schemas/User'
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
/users:
parameters:
- in:query
name:ids
description:The IDs of the users
schema:
type:string
example:"[\"AABBCCDD\", \"EEFFGGHH\"]"
required:true
get:
summary:Get multiple users
operationId:getUsers
tags:
- users
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
type:array
items:
$ref:'#/components/schemas/User'
/user/{id|username}/icon:
parameters:
- $ref:'#/components/parameters/UserIdentifier'
patch:
summary:Change user's avatar
description:The new avatar may be up to 2MiB in size.
operationId:changeUserIcon
tags:
- users
requestBody:
$ref:'#/components/requestBodies/Image'
security:
- TokenAuth:['USER_WRITE']
responses:
"204":
description:Expected response to a valid request
"400":
description:Request was invalid, see given error
content:
application/json:
schema:
$ref:'#/components/schemas/InvalidInputError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
/user/{id|username}/projects:
parameters:
- $ref:'#/components/parameters/UserIdentifier'
get:
summary:Get user's projects
operationId:getUserProjects
tags:
- users
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
type:array
items:
$ref:'#/components/schemas/Project'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
/user/{id|username}/follows:
parameters:
- $ref:'#/components/parameters/UserIdentifier'
get:
summary:Get user's followed projects
operationId:getFollowedProjects
tags:
- users
security:
- TokenAuth:['USER_READ']
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
type:array
items:
$ref:'#/components/schemas/Project'
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
/user/{id|username}/payouts:
parameters:
- $ref:'#/components/parameters/UserIdentifier'
get:
summary:Get user's payout history
operationId:getPayoutHistory
tags:
- users
security:
- TokenAuth:['PAYOUTS_READ']
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
$ref:'#/components/schemas/UserPayoutHistory'
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
post:
summary:Withdraw payout balance to PayPal or Venmo
operationId:withdrawPayout
description:"Warning: certain amounts get withheld for fees. Please do not call this API endpoint without first acknowledging the warnings on the corresponding frontend page."
tags:
- users
security:
- TokenAuth:['PAYOUTS_WRITE']
parameters:
- name:amount
in:query
description:Amount to withdraw
schema:
type:integer
required:true
responses:
"204":
description:Expected response to a valid request
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)
# Notifications
/user/{id|username}/notifications:
parameters:
- $ref:'#/components/parameters/UserIdentifier'
get:
summary:Get user's notifications
operationId:getUserNotifications
tags:
- notifications
security:
- TokenAuth:['NOTIFICATION_READ']
responses:
"200":
description:Expected response to a valid request
content:
application/json:
schema:
type:array
items:
$ref:'#/components/schemas/Notification'
"401":
description:Incorrect token scopes or no authorization to access the requested item(s)
content:
application/json:
schema:
$ref:'#/components/schemas/AuthError'
"404":
description:The requested item(s) were not found or no authorization to access the requested item(s)