Skip to content
Sign in

We rebuilt SOTF Mods. Sign in again — your password is the same.

Developers

SOTF Mods API for developers

Last updated

Overview

The SOTF Mods API is public and read-only for anonymous clients: the whole catalogue of Sons of the Forest mods, libraries, builds and Kits, with versions, dependencies and compatibility per game build.

  • Base URL: https://api.sotf-mods.com/api/v2 for third-party clients (also served on https://sotf-mods.com/api/v2).
  • Reference: every endpoint, parameter and schema is in the interactive reference, generated from the same contracts the server runs on. The raw spec is at /api/v2/openapi.json (OpenAPI 3.1).
  • No key needed for public reads. Please send a descriptive User-Agent with a contact URL, so we can reach you before blocking a misbehaving client.

Format

  • JSON in UTF-8 with camelCase fields.
  • Dates are ISO 8601 in UTC with a Z (2026-09-30T12:00:00.000Z); calendar dates are YYYY-MM-DD.
  • Ids of mods, versions and users are integers and stable. The id from a mod's manifest.json is exposed as manifestId.
  • Changes inside v2 are additive only: new fields and endpoints may appear, existing ones don't change meaning. Ignore fields you don't know. A breaking change would ship as /api/v3.

Errors

Errors follow RFC 9457 (application/problem+json):

{"type":"https://sotf-mods.com/developers/errors#not-found","title":"Not found","status":404,"detail":"No mod with id 99999","code":"NOT_FOUND","requestId":"01J…"}

Branch on code (stable), not on title or detail (human text). Common codes: VALIDATION_FAILED (422, with an errors list), NOT_FOUND (404), GONE (410), RATE_LIMITED (429, with Retry-After) and UNAVAILABLE (503). Quote the requestId when you report a problem.

Pagination

  • Catalogue lists use pages: ?page=1&pageSize=24 (up to 100) and answer { items, page, pageSize, total, totalPages }.
  • Feeds (comments, reviews) use cursors: ?limit=20&cursor=… and answer { items, nextCursor }. Treat the cursor as opaque and stop when nextCursor is null.

Caching

Public responses carry Cache-Control, an ETag and are cached at our edge. Send If-None-Match with the last ETag and you get an empty 304 Not Modified when nothing changed — it doesn't count against your limits in any meaningful way and makes your client fast. Don't poll more often than every few minutes; catalogue data rarely changes faster than that.

CORS is open (Access-Control-Allow-Origin: *, without credentials) on public reads, so browser apps can call the API directly.

Integrate a mod manager

The typical flow for a launcher or mod manager:

  1. List and search with GET /mods (q, type, category, sort, page). Each item is a card with latestVersion, compatStatus and canonicalPath.
  2. Show the details with GET /mods/{id} or GET /mods/by-slug/{user}/{slug}; to match a mod already installed on disk, use GET /mods/by-manifest/{manifestId} with the id from its manifest.json.
  3. Resolve dependencies with GET /mods/{id}/dependencies. Install required ones first, recursively, and warn about conflicts. Libraries are regular items with kind: "library".
  4. Pick a version with GET /mods/{id}/versions. Prefer the latest stable version; offer betas only if the user opts in.
  5. Check compatibility with GET /mods/{id}/compat and GET /game-builds or GET /ecosystem to tell users whether a mod works on their game build before they install it.
  6. Download through the mod's download URL (see below), extract into the game folder and record the version you installed.
  7. Check for updates by comparing the installed version with latestVersion (semver), a few times a day at most.

Link users back to the mod page (https://sotf-mods.com + canonicalPath) so they can read the description, report problems and support the creator.

Downloads

Download a version with GET https://sotf-mods.com/mods/{user}/{slug}/download/{version}. It answers 302 to the file on our storage; follow redirects. Downloads are never rate-limited with a 429: above the fair-use threshold they still work but are not counted in the statistics. Please download once per install, not on every launch.

Staying up to date

Changes to the API are announced in News and in its RSS feed. Deprecated routes answer with Deprecation and Sunset headers and a Link to this page months before they are retired.

Support

Found a bug or need an endpoint? Ask in the developers channel of our Discord or open an issue on GitHub. Report security issues privately through GitHub Security Advisories.

Public read endpoints

Anonymous, cacheable and CORS-enabled. The reference documents parameters, responses and errors for each one.

buildViewer

Public endpoints: buildViewer
Method Path What it does
GET /api/v2/builds/:id/preview Top-down SVG preview of a build (latest version)
GET /api/v2/builds/:id/geometry Packed geometry of a build for the 3D viewer

bundles

Public endpoints: bundles
Method Path What it does
GET /api/v2/mods/:id/bundles Official bundles of a mod that are ready to download
GET /api/v2/bundles/:id/download Download the bundle zip (302 to R2)

catalog

Public endpoints: catalog
Method Path What it does
GET /api/v2/mods Explore mods, libraries and builds
GET /api/v2/mods/:id Mod detail by id
GET /api/v2/mods/by-slug/:user/:slug Mod detail by user handle and slug (exact)
GET /api/v2/mods/by-manifest/:manifestId Mod detail by manifest id (case-sensitive)
GET /api/v2/mods/:id/dependencies Resolved dependencies (required, optional, conflicts)
GET /api/v2/mods/:id/dependents Published mods that require this one ("Required by")
GET /api/v2/mods/:id/related Related mods (same category or tags + text similarity)
GET /api/v2/categories Categories with i18n names and counts
GET /api/v2/tags Curated tags with i18n names and counts
GET /api/v2/creators Creators directory with stats and tier
GET /api/v2/users/:handle Public profile
GET /api/v2/users/:handle/mods Published mods and libraries of a user
GET /api/v2/users/:handle/builds Published builds of a user
GET /api/v2/users/:handle/reviews Visible reviews written by a user
GET /api/v2/users/:handle/activity 12-month contribution heatmap (respects privacy)

comments

Public endpoints: comments
Method Path What it does
GET /api/v2/mods/:id/comments Comments of a mod (Top = Wilson on positive reactions, or New)
GET /api/v2/comments/:id Permalink with the whole thread

compat

Public endpoints: compat
Method Path What it does
GET /api/v2/mods/:id/compat Compatibility of a mod per version and game build
GET /api/v2/ecosystem RedLoader / RedManager status per game build
GET /api/v2/game-builds Registered game builds
GET /api/v2/patch-radar Patch Radar for a game build (top 50 by downloads in 30 days)
GET /api/v2/compat/uptime Uptime series of the platform components (Patch Radar)

discovery

Public endpoints: discovery
Method Path What it does
GET /api/v2/scout/status Whether Scout (AI mod finder) is available
GET /api/v2/mods/:id/recommendations Players also downloaded, and similar mods

downloads

Public endpoints: downloads
Method Path What it does
GET /api/v2/versions/:id/download Download a version (302 to R2)

events

Public endpoints: events
Method Path What it does
GET /api/v2/mods/:id/live/stream Live counters of a mod over server-sent events

gamification

Public endpoints: gamification
Method Path What it does
GET /api/v2/badges Badge catalog with unlock share, ranks and tiers
GET /api/v2/awards/current Mod of the Week and current staff picks
GET /api/v2/users/:handle/badges Badges of a user (field notebook)

kits

Public endpoints: kits
Method Path What it does
GET /api/v2/kits Public kits
GET /api/v2/kits/:id Kit detail (unlisted only by link, private only for the owner)
GET /api/v2/kits/by-slug/:user/:slug Kit detail by owner handle and slug
GET /api/v2/kits/by-code/:code Kit by share code (`KIT-XXXX-XX` or the 6-character short code)
GET /api/v2/users/:handle/kits Public kits of a user (respects privacy)
GET /api/v2/mods/:id/kits Public kits that contain a mod (most followed first)

kitSocial

Public endpoints: kitSocial
Method Path What it does
GET /api/v2/kits/:id/live/stream Live follower and comment counts of a kit over server-sent events
GET /api/v2/kits/:id/comments Comments of a kit (newest first, replies embedded)

modKnowledge

Public endpoints: modKnowledge
Method Path What it does
GET /api/v2/mods/:id/knowledge Known issues, author FAQ and co-authors of a mod
GET /api/v2/mods/:id/diff Difference between two versions of a mod (files, manifest, changelog range)
GET /api/v2/users/:handle/coauthored Published mods a user co-authors

oauth

Public endpoints: oauth
Method Path What it does
GET /api/v2/auth/providers OAuth providers available on this server
GET /api/v2/auth/oauth/:provider/start Start an OAuth sign-in or link
GET /api/v2/auth/oauth/:provider/callback OAuth callback

requests

Public endpoints: requests
Method Path What it does
GET /api/v2/requests Mod requests (top voted or newest)
GET /api/v2/requests/:id A mod request
GET /api/v2/requests/:id/comments Comments of a request, oldest first

reviews

Public endpoints: reviews
Method Path What it does
GET /api/v2/mods/:id/reviews Reviews of a mod
GET /api/v2/mods/:id/reviews/summary Histogram, mean and Bayesian mean of a mod

search

Public endpoints: search
Method Path What it does
GET /api/v2/search Full-text + fuzzy search across mods, builds, kits, users and pages
GET /api/v2/search/index Compact Cmd+K index for a locale

seo

Public endpoints: seo
Method Path What it does
GET /api/v2/resolve Resolve a public path (history, case, owner changes, tombstones)

stats

Public endpoints: stats
Method Path What it does
GET /api/v2/site/stats Site-wide figures
GET /api/v2/live/pulse Live readout of the landing
GET /api/v2/mods/:id/live Live counters of a mod
GET /api/v2/mods/:id/badge/:kind SVG badge of a mod (downloads, version, rating, followers, compat)
GET /api/v2/mods/:id/stats/public Public download series of a mod

translations

Public endpoints: translations
Method Path What it does
GET /api/v2/mods/:id/translation Translated short description of a mod

versions

Public endpoints: versions
Method Path What it does
GET /api/v2/mods/:id/versions Versions of a mod (semver order)
GET /api/v2/mods/:id/versions/:version One version by id, version string or `latest`

Error codes

The code field of an error is one of these. Each problem’s type URI points to its row here.

Error codes
Code Status Meaning
VALIDATION_FAILED 422 The request is not valid
UNAUTHENTICATED 401 Sign in required
INVALID_CREDENTIALS 401 Wrong email, handle or password
FORBIDDEN 403 You cannot do this
EMAIL_NOT_VERIFIED 403 Verify your email first
TURNSTILE_REQUIRED 403 Complete the human check
REAUTH_REQUIRED 403 Sign in again to continue
SUSPENDED 403 Your account is suspended
NOT_FOUND 404 Not found
CONFLICT 409 This conflicts with the current state
GONE 410 This is gone
PAYLOAD_TOO_LARGE 413 The upload is too large
UNSUPPORTED_MEDIA_TYPE 415 Unsupported content type
RATE_LIMITED 429 Too many requests
INTERNAL 500 Something went wrong
UNAVAILABLE 503 Temporarily unavailable

Rate limits

Above a limit the API answers 429 with a Retry-After header. Cache responses and honour ETags; you will rarely come close.

Rate limits
Scope Limit Notes
API v2 reads 300 / 1 minute · per IP —
Legacy API reads 600 / 1 minute · per IP —
Downloads 60 / 1 minute · per IP never 429: above the limit the download redirects but does not count
KelvinSeek 20 / 1 minute · per player 300/day per chat_id + IP and a global daily budget

Legacy API and deprecations

The API of the first version of the site still answers on api.sotf-mods.com/api/* and sotf-mods.com/api/*, so RedManager, UpdatesChecker and in-game mods keep working. New integrations should use v2.

Supported (byte-compatible)

Supported (byte-compatible)
Method Path What it does
GET /api/mods List mods (legacy, byte-compatible; RedManager and UpdatesChecker)
GET /api/mods/:mod_id Mod by manifest id (legacy, byte-compatible)
GET /api/mods/:mod_id/check Update check with node-semver `gt` (legacy, byte-compatible)
GET /api/kelvinseek/prompt KelvinSeek prompt: `text/plain` "{command}|{answer}", always 200
GET /api/kelvinseek/clear KelvinSeek clear: `text/plain` "Chat cleared"

Frozen and deprecated

These routes keep answering until December 31, 2027, then they will be retired. Their responses carry these headers:

Deprecation: true
Sunset: Fri, 31 Dec 2027 00:00:00 GMT
Link: <https://sotf-mods.com/developers>; rel="deprecation"
Frozen and deprecated
Method Path What it does
GET /api/mods/slug/:userSlug/:mod_slug Mod by user and slug (legacy, frozen)
GET /api/mods/find Manifest id by user and slug (legacy, frozen)
GET /api/mods/featured 12 featured mods (legacy, frozen)
GET /api/builds/featured 4 featured builds (legacy, frozen)
GET /api/stats Site stats (legacy, frozen; downloads include orphan rows)
GET /api/stats/builds Build stats (legacy, frozen)
GET /api/categories Categories by type (legacy, frozen)
GET /api/users/:userSlug User (legacy, frozen)
GET /api/users/:userSlug/stats User stats (legacy, frozen)
GET /api/comments Comments of a mod by numeric id (legacy, frozen; hidden ones excluded)
GET /api/mods/:mod_id/download-stats Daily downloads (legacy, frozen; unknown mod → 200 `{status:false}`)
GET /api/mods/:mod_id/download/:version Download alias (302 to R2; `?ip=&agent=` ignored)
GET /api/mods/slug/:userSlug/:mod_slug/download/:version Download alias by slug (302 to R2)

Retired

Every method on these routes answers 410 Gone with this body:

Write routes that were retired (uploads, edits, comments, votes) also answer 410 with the same envelope: status false, error GONE. Use the v2 API with a token instead.

{"status":false,"error":"GONE","message":"This endpoint was retired in sotf-mods v2. See https://sotf-mods.com/developers"}
  • ANY /api/auth/*
  • ANY /api/favorites
  • ANY /api/favorites/*
  • GET /api/mods/:mod_id/favorite
  • GET /api/mods/:mod_id/approve
  • GET /api/mods/:mod_id/unapprove
  • POST /api/mods/:mod_id/release
  • PATCH /api/mods/:mod_id/details
  • POST /api/files/presigned-url
  • POST /api/mods/upload
  • POST /api/mods/publish
  • POST /api/builds/upload
  • POST /api/builds/publish
  • POST /api/users/avatar
  • POST /api/comments
  • ANY /api/kelvin-gpt/*

Intentional differences from the old behaviour

  1. approved=false only returns pending mods whose checks passed
  2. approved=true returns published only (archived, unlisted and removed are excluded)
  3. Without approved: published, pending with checks, archived and unlisted
  4. The detail by mod_id returns everything except rejected and removed (404)
  5. The 19 mods with type = null come out as "Mod" or "Library" (backfill B4)
  6. Validation errors answer 422 with the envelope instead of 500
  7. GET /api/comments excludes hidden comments
  8. Every sort has id as tie-breaker (stable pagination)
  9. Responses add Cache-Control, Deprecation (Tier 2) and X-Request-Id
  10. Downloads answer 302 to R2 instead of streaming the file

KelvinSeek fallback

When the language model is unavailable (no key, timeout or daily budget spent) /api/kelvinseek/prompt does not fail: it answers with the legacy deterministic fallback "COMMAND|I will … right away (Chat GPT API Error …)", so the in-game bot keeps working.