Skip to main content
The Modrinth API uses conventional HTTP status codes and returns structured error messages to help you debug issues quickly.

Error Response Format

All API errors follow a consistent JSON structure:
{
  "error": "error_type",
  "description": "Human-readable error message explaining what went wrong"
}
Some errors may include additional details:
{
  "error": "invalid_input",
  "description": "Validation failed for project creation",
  "details": {
    "field": "slug",
    "message": "Slug is already taken"
  }
}

HTTP Status Codes

The API uses standard HTTP status codes to indicate the success or failure of requests.

Success Codes (2xx)

200 OK
success
Request succeeded. Response body contains the requested data.
{
  "id": "AABBCCDD",
  "name": "My Project",
  "slug": "my-project"
}
201 Created
success
Resource successfully created. Response body contains the new resource.
{
  "id": "IIJJKKLL",
  "name": "Version 1.0.0",
  "version_number": "1.0.0"
}
204 No Content
success
Request succeeded but there’s no response body (e.g., DELETE operations).
304 Not Modified
success
Resource hasn’t changed since the last request (when using If-None-Match with ETags).

Client Error Codes (4xx)

400 Bad Request
error
The request is malformed or contains invalid data.Common error types:
  • invalid_input: Invalid request parameters
  • json_error: Malformed JSON in request body
  • xml_error: Malformed XML data
  • validation_error: Data failed validation rules
{
  "error": "invalid_input",
  "description": "Project slug must be between 3 and 64 characters"
}
401 Unauthorized
error
Authentication is required or credentials are invalid.Common error types:
  • unauthorized: Missing or invalid authentication token
  • auth_error: Authentication failed
{
  "error": "unauthorized",
  "description": "Invalid authentication credentials"
}
Solutions:
  • Include a valid Authorization header
  • Check that your token hasn’t expired
  • Verify your token hasn’t been revoked
403 Forbidden
error
You’re authenticated but don’t have permission to perform this action.Common causes:
  • Token lacks required scope
  • Attempting to modify someone else’s resource
  • Attempting a restricted operation
{
  "error": "unauthorized",
  "description": "Token does not have the required scope: PROJECT_WRITE"
}
404 Not Found
error
The requested resource doesn’t exist.
{
  "error": "not_found",
  "description": "Resource not found"
}
Common causes:
  • Invalid project/version/user ID
  • Resource has been deleted
  • Typo in the endpoint URL
409 Conflict
error
The request conflicts with the current state of the resource.
{
  "error": "conflict",
  "description": "A project with this slug already exists"
}
Common scenarios:
  • Duplicate slug or username
  • Concurrent modification conflicts
  • Resource state prevents the operation
410 Gone
error
The API version or resource has been permanently removed.
{
  "error": "api_deprecated",
  "description": "You are using an outdated version of Modrinth's API. Please update your application."
}
Solution: Migrate to a supported API version (v2 or v3).
429 Too Many Requests
error
You’ve exceeded the rate limit.
{
  "error": "ratelimit_error",
  "description": "You are being rate-limited. Please wait 15000 milliseconds. 0/300 remaining."
}
Response headers:
X-Ratelimit-Limit: 300
X-Ratelimit-Remaining: 0
X-Ratelimit-Reset: 15
Retry-After: 15
See Rate Limits for detailed handling strategies.

Server Error Codes (5xx)

500 Internal Server Error
error
An unexpected error occurred on the server.Common error types:
  • internal_error: Unexpected server error
  • database_error: Database operation failed
  • search_error: Search engine error
{
  "error": "internal_error",
  "description": "An unexpected error occurred while processing your request"
}
What to do:
  • Retry the request after a short delay
  • If the error persists, report it to Modrinth support
502 Bad Gateway
error
The API server received an invalid response from an upstream server.Common causes:
  • Temporary connectivity issues
  • Upstream service degradation
Solution: Retry with exponential backoff.
503 Service Unavailable
error
The API is temporarily unavailable.Common causes:
  • Scheduled maintenance
  • Server overload
  • Deployment in progress
Solution: Retry after waiting. Check Modrinth’s status page.
504 Gateway Timeout
error
The request took too long to process.Common causes:
  • Complex search queries
  • Large file uploads
  • Database slowdowns
Solutions:
  • Simplify your query
  • Break large requests into smaller chunks
  • Retry the request

Common Error Types

Here’s a reference of error type strings returned in the error field:

Authentication Errors

Error TypeStatusDescription
unauthorized401Invalid or missing authentication credentials
auth_error401Authentication process failed
invalid_credentials401Username/password or token is invalid
invalid_auth_method401Authentication method not supported

Input Validation Errors

Error TypeStatusDescription
invalid_input400Request contains invalid data
validation_error400Data failed validation rules
json_error400Malformed JSON in request body
xml_error400Malformed XML data
decoding_error400Failed to decode base62 ID

Resource Errors

Error TypeStatusDescription
not_found404Requested resource doesn’t exist
conflict409Request conflicts with existing resource

Rate Limiting

Error TypeStatusDescription
ratelimit_error429Too many requests, rate limit exceeded

Server Errors

Error TypeStatusDescription
internal_error500Unexpected server error
database_error500Database operation failed
redis_database_error500Redis cache error
clickhouse_error500Analytics database error
search_error500Search engine error
file_hosting_error500File storage error
mail_error500Email sending failed

External Service Errors

Error TypeStatusDescription
payments_error424Payment processor error
stripe_error424Stripe payment processing failed
discord_error424Discord integration failed
turnstile_error400Cloudflare Turnstile captcha failed

Error Handling Best Practices

1. Implement Comprehensive Error Handling

async function makeApiRequest(url, options = {}) {
  try {
    const response = await fetch(url, {
      ...options,
      headers: {
        'User-Agent': 'my-app/1.0.0',
        ...options.headers
      }
    });
    
    // Handle rate limiting
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new RateLimitError(
        `Rate limited. Retry after ${retryAfter} seconds`,
        parseInt(retryAfter)
      );
    }
    
    // Handle client errors
    if (response.status >= 400 && response.status < 500) {
      const error = await response.json();
      throw new ClientError(error.description, response.status, error.error);
    }
    
    // Handle server errors
    if (response.status >= 500) {
      const error = await response.json();
      throw new ServerError(error.description, response.status, error.error);
    }
    
    // Success
    return response.json();
    
  } catch (error) {
    if (error instanceof RateLimitError) {
      // Wait and retry
      await sleep(error.retryAfter * 1000);
      return makeApiRequest(url, options);
    }
    
    if (error instanceof ClientError) {
      // Log and handle client error
      console.error('Client error:', error.message);
      throw error;
    }
    
    if (error instanceof ServerError) {
      // Retry with exponential backoff
      console.warn('Server error, retrying...');
      await sleep(1000);
      return makeApiRequest(url, options);
    }
    
    // Network or other error
    throw error;
  }
}

class RateLimitError extends Error {
  constructor(message, retryAfter) {
    super(message);
    this.name = 'RateLimitError';
    this.retryAfter = retryAfter;
  }
}

class ClientError extends Error {
  constructor(message, status, type) {
    super(message);
    this.name = 'ClientError';
    this.status = status;
    this.type = type;
  }
}

class ServerError extends Error {
  constructor(message, status, type) {
    super(message);
    this.name = 'ServerError';
    this.status = status;
    this.type = type;
  }
}

2. Use Exponential Backoff for Retries

import time
import requests
from requests.exceptions import RequestException

def make_request_with_retry(url, max_retries=5):
    """Make API request with exponential backoff retry logic."""
    
    for attempt in range(max_retries):
        try:
            response = requests.get(
                url,
                headers={'User-Agent': 'my-app/1.0.0'}
            )
            
            # Handle rate limiting
            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 60))
                print(f"Rate limited. Waiting {retry_after} seconds...")
                time.sleep(retry_after)
                continue
            
            # Handle server errors with exponential backoff
            if response.status_code >= 500:
                wait_time = min(2 ** attempt, 60)  # Max 60 seconds
                print(f"Server error. Retrying in {wait_time} seconds...")
                time.sleep(wait_time)
                continue
            
            # Raise for client errors (don't retry)
            if 400 <= response.status_code < 500:
                error_data = response.json()
                raise Exception(
                    f"{error_data.get('error')}: {error_data.get('description')}"
                )
            
            # Success
            response.raise_for_status()
            return response.json()
            
        except RequestException as e:
            if attempt == max_retries - 1:
                raise
            
            wait_time = min(2 ** attempt, 60)
            print(f"Request failed: {e}. Retrying in {wait_time} seconds...")
            time.sleep(wait_time)
    
    raise Exception(f"Max retries ({max_retries}) exceeded")

# Usage
try:
    data = make_request_with_retry('https://api.modrinth.com/v2/search')
    print(data)
except Exception as e:
    print(f"Request failed: {e}")

3. Log Errors for Debugging

class ApiClient {
  constructor(baseUrl, logger) {
    this.baseUrl = baseUrl;
    this.logger = logger;
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    
    this.logger.info('API Request', {
      method: options.method || 'GET',
      url,
      timestamp: new Date().toISOString()
    });
    
    try {
      const response = await fetch(url, {
        ...options,
        headers: {
          'User-Agent': 'my-app/1.0.0',
          ...options.headers
        }
      });
      
      // Log rate limit info
      const remaining = response.headers.get('X-Ratelimit-Remaining');
      if (remaining !== null) {
        this.logger.debug('Rate Limit', {
          remaining,
          limit: response.headers.get('X-Ratelimit-Limit'),
          reset: response.headers.get('X-Ratelimit-Reset')
        });
      }
      
      if (!response.ok) {
        const error = await response.json();
        
        this.logger.error('API Error', {
          status: response.status,
          error: error.error,
          description: error.description,
          url,
          timestamp: new Date().toISOString()
        });
        
        throw new Error(error.description);
      }
      
      this.logger.info('API Success', {
        status: response.status,
        url
      });
      
      return response.json();
      
    } catch (error) {
      this.logger.error('Request Failed', {
        error: error.message,
        url,
        timestamp: new Date().toISOString()
      });
      throw error;
    }
  }
}

4. Provide User-Friendly Error Messages

function getUserFriendlyError(error) {
  const errorMessages = {
    'not_found': 'The requested item could not be found.',
    'unauthorized': 'Please sign in to continue.',
    'ratelimit_error': 'Too many requests. Please wait a moment and try again.',
    'conflict': 'This name is already taken. Please choose another.',
    'invalid_input': 'Please check your input and try again.',
    'internal_error': 'Something went wrong on our end. Please try again later.'
  };
  
  return errorMessages[error.type] || error.description;
}

// Usage in UI
try {
  await createProject(data);
} catch (error) {
  const message = getUserFriendlyError(error);
  showNotification(message, 'error');
}

Debugging Tips

Check Response Headers

Always inspect response headers for debugging information:
curl -i "https://api.modrinth.com/v2/project/invalid-id"
HTTP/2 404
content-type: application/json
x-ratelimit-limit: 300
x-ratelimit-remaining: 299
x-ratelimit-reset: 60

{
  "error": "not_found",
  "description": "Resource not found"
}

Verify Your Request

  1. Check the HTTP method: Ensure you’re using the correct method (GET, POST, etc.)
  2. Validate the URL: Check for typos in the endpoint path
  3. Inspect headers: Ensure required headers are present
  4. Verify request body: Validate JSON syntax and required fields

Test with cURL

Test your requests using cURL to isolate issues:
# Test authentication
curl -H "Authorization: YOUR_TOKEN" \
  "https://api.modrinth.com/v2/user"

# Test with verbose output
curl -v "https://api.modrinth.com/v2/project/fabric-api"

# Test POST request
curl -X POST "https://api.modrinth.com/v2/project" \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Project", "slug": "my-project"}'

Common Mistakes

Error: Unable to obtain user IP address! or blocked requestsSolution: Always include a User-Agent header:
headers: { 'User-Agent': 'my-app/1.0.0' }
Error: invalid_input or decoding_errorSolution: Some endpoints only accept IDs, not slugs. Check the API documentation for the specific endpoint.
Error: unauthorizedSolution: Use the token directly without “Bearer”:
// Correct
headers: { 'Authorization': 'mrp_YOUR_TOKEN' }

// Incorrect
headers: { 'Authorization': 'Bearer mrp_YOUR_TOKEN' }
Error: unauthorized with message about missing scopeSolution: Create a new token with the required scopes from your account settings.

Getting Help

If you’re experiencing issues:
  1. Check the documentation: Verify you’re using the correct endpoint and parameters
  2. Search existing issues: Look for similar problems on GitHub
  3. Join the community: Ask questions on Modrinth’s Discord
  4. Contact support: For persistent issues, email [email protected]

What to Include in Bug Reports

When reporting API issues, include:
  • Request details: Method, URL, headers (excluding tokens)
  • Response: Status code, error message, response headers
  • Expected behavior: What you expected to happen
  • Actual behavior: What actually happened
  • Steps to reproduce: Minimal code example
  • Environment: Programming language, library versions

Summary

Consistent Format

All errors return error and description fields in JSON format

Standard Codes

Uses HTTP status codes: 4xx for client errors, 5xx for server errors

Retry Logic

Implement exponential backoff for server errors and rate limits

Helpful Messages

Error descriptions provide actionable guidance for resolution

Next Steps

API Overview

Learn about API basics and common patterns

Authentication

Fix authentication and authorization errors