API

Save a log from a launcher, mod or server panel with one request. You get a link back.

Draft. The API is built and being tested, but it isn't live at api.minelog.org yet. You can start building against this page. Fields may still change before launch.

Quick start

Send the log text to https://api.minelog.org and you get a link back. Sending it as plain text means there's nothing to escape, which suits shell scripts and Java mods.

curl --data-binary @latest.log \
  -H "Content-Type: text/plain" \
  "https://api.minelog.org/v2/logs?source=MyLauncher"

A successful upload returns 201 and the details of the saved log:

201 Created
{
  "id": "k7M2xq9D4",
  "url": "https://minelog.org/k7M2xq9D4",
  "raw": "https://api.minelog.org/v2/logs/k7M2xq9D4/raw",
  "source": "MyLauncher",
  "kind": "server",
  "lines": 1204,
  "size": 88012,
  "errors": 3,
  "warnings": 11,
  "privacyApplied": true,
  "createdAt": "2026-09-24T10:40:55Z",
  "expiresAt": "2026-11-03T10:40:55Z"
}

Open url to see the log. That's the link you give to a player or paste into a support thread.

Basics

Everything is on https://api.minelog.org, separate from the website, so a launcher only needs one short host. Logs are still read on https://minelog.org, and every url in a response points there.

There are two protocols on the same host. /v2 is ours, and it's the one to use for new work. /1 copies the mclo.gs API, so tools written for it work once you change the host. Both use the same storage: a log saved through one opens through the other.

  • Requests and responses are UTF-8. /v2 dates are ISO 8601 in UTC.
  • You can gzip large uploads with Content-Encoding: gzip. The size limits apply to the unpacked text.
  • Every endpoint allows cross-origin requests, so a web page can upload straight from the browser.
  • There are no accounts and no API keys. Limits are per connection.

Limits

These are the same limits the website uses, so anything that uploads there uploads here.

Largest log
15 MBMeasured on the text that gets saved, after private details are hidden. With gzip, the unpacked size is what counts.
Most lines
30,000A log with one very long line counts as one line. The size limit still applies to it.
Kept for
40 daysEvery log deletes itself after this long. You can't extend it or delete a log early.
Uploads
30 per hourPer connection. Reading logs doesn't count.

Instead of hard coding these numbers, read them from /v2/limits.

Identify your app

Send your app's name as source with each upload. The log page shows it as a small badge, so a player who opens a link from your app can see it came from there. Any name works, there is nothing to sign up for and no list to be on. Every name is shown exactly as you send it, and none is treated differently from another.

  • Up to 32 characters.
  • Letters and digits from any language, with spaces or . _ + - between words. Something like MyLauncher or My Launcher 2.1.
  • A name that doesn't fit is ignored, and the log is saved without a badge. It is never an error.

You can also send it in an X-Minelog-Client header, which helps when you can't change the body.

The source isn't verified. Anyone can send any name, so treat the badge as a hint about where a link came from, not a guarantee.

Private details

By default, minelog hides IP addresses, user folder names, tokens and email addresses before saving. Player names stay, because you need them for debugging. The server does this on its own copy, so a client that skips it still can't store private details.

To turn it off, send hidePrivate: false. The log is then saved as sent, privacyApplied is false in the response, and the log page shows a warning to everyone who opens it. Leave it on unless you have a reason not to.

Save a log

POST/v2/logs

Saves one log and returns its details. You can send it two ways:

  • Plain text. Send Content-Type: text/plain with the log as the body. Options go in the query string.
  • JSON. Send Content-Type: application/json with an object.
NameTypeNeededWhat it does
contentstringrequiredThe log text. In plain text mode you leave this out and the whole request body is the log. Goes in: JSON body.
hidePrivatebooleandefault trueHides IP addresses, MAC addresses, user folder names, tokens and emails before saving. The server does it again on its own copy, so a client that skips it can't leak anything. Goes in: JSON body or query.
sourcestringoptionalThe name of the app that is uploading, like MyLauncher. It shows as a badge on the log page. Names that don't fit the rules above are ignored, not treated as errors. Goes in: JSON body, query or X-Minelog-Client header.

The response is 201 with the fields below. Keep the id if you want to read the log again later.

NameTypeWhat it does
idstringNine characters, case sensitive. Ids can't be guessed.
urlstringThe page where people read the log.
rawstringThe whole log as plain text.
sourcestring | nullThe source name as saved, or null if none was sent or it was ignored.
kindstringWhat minelog detected: server, client, crash, jvm, yaml, toml, props, json, readme or unknown.
linesnumberNumber of lines saved.
sizenumberSize of the saved text in bytes.
errorsnumberNumber of error entries. A whole stack trace counts once.
warningsnumberNumber of warning entries.
privacyAppliedbooleanFalse if hidePrivate was turned off for this log.
createdAtstringWhen it was saved, in ISO 8601 UTC.
expiresAtstringWhen it will be deleted, in ISO 8601 UTC.

This can fail with 400, 413, 422 or 429. See Errors.

Read a log

GET/v2/logs/{id}

Returns the same fields as an upload. Add ?content=true to include the log text in a content field. Without it, the response stays small no matter how big the log is.

If the id doesn't exist or the log has expired, you get a 404:

404 Not Found
{
  "error": {
    "code": "not_found",
    "message": "This log doesn't exist or has expired."
  }
}

Get the raw text

GET/v2/logs/{id}/raw

Returns the log exactly as saved, as text/plain; charset=utf-8. Useful for piping into other tools. Errors here are still JSON, so check the status code before parsing.

Read the limits

GET/v2/limits

Returns the current limits, so an app can check a file before trying to upload it:

200 OK
{
  "retentionDays": 40,
  "maxBytes": 15728640,
  "maxLines": 30000,
  "uploadsPerHour": 30
}

Errors

Every /v2 error has the same shape: an error object with a code you can branch on and a message written for people. Show the message to users as is, but don't match on its wording, because it may change.

413 Payload Too Large
{
  "error": {
    "code": "too_many_lines",
    "message": "This log has too many lines. The limit is 30,000."
  }
}
CodeStatusMeaning
invalid_body400The body isn't valid JSON or gzip, or isn't text.
empty_content400There's no log text to save.
not_plain_text400The content looks like a binary file. Only plain text logs and configs can be shared.
invalid_option400An option has the wrong type, like a hidePrivate that isn't true or false.
not_found404The log doesn't exist or has expired.
log_too_large413The log is over the size limit.
too_many_lines413The log is over the line limit.
blocked_content422The log contains content we can't host. The message gives the line number and the text around the match, so you know what to remove.
rate_limited429Too many uploads from this connection. Retry-After tells you how many seconds to wait.
server_error500Something broke on our side. Nothing was saved, so it's safe to try again in a moment.

Rate limits

Each connection can save 30 logs an hour. After that you get a 429 with a Retry-After header, in seconds. We count using a keyed hash of the IP address and never store the address itself. Reads aren't counted.

An app running on a player's own machine is fine, since each player has their own connection. If your service will upload for many people from one server, contact us before you launch so we can plan for it.

mclo.gs compatibility

If your tool already uploads to mclo.gs, you don't need to rewrite it. Change api.mclo.gs to api.minelog.org and keep the /1 paths:

curl -X POST https://api.mclo.gs/1/log \
  --data-urlencode "content@latest.log"

The response has the shape those tools expect, with a success flag and times in unix seconds:

200 OK
{
  "success": true,
  "id": "k7M2xq9D4",
  "source": "MyLauncher",
  "created": 1790246455,
  "expires": 1793702455,
  "size": 88012,
  "lines": 1204,
  "errors": 3,
  "url": "https://minelog.org/k7M2xq9D4",
  "raw": "https://api.minelog.org/1/raw/k7M2xq9D4",
  "metadata": []
}

Errors use the same flat shape rather than the /v2 one:

413 Payload Too Large
{
  "success": false,
  "error": "This log is too large."
}

A few things can't match, mostly because minelog never deletes logs early. This is every mclo.gs endpoint and where it stands:

EndpointStatusNotes
POST /1/logSupportedAccepts JSON or form encoding, with content and source. metadata is accepted and ignored, and the response has an empty list. There's no token, because logs can't be deleted early. Times are unix seconds.
GET /1/log/{id}SupportedSame fields as the upload response. Add ?raw=1 to get the text in content. parsed and insights are accepted and ignored.
GET /1/raw/{id}SupportedThe whole log as plain text.
GET /1/limitsSupportedstorageTime in seconds, maxLength in bytes and maxLines, with our numbers.
GET /1/filtersDifferentAlways an empty list. minelog doesn't ask clients to trim or rewrite logs. It only hides private details on its own side.
DELETE /1/log/{id}Not supportedReturns 501. Logs are never deleted early, they expire on their own.
POST /1/bulk/log/deleteNot supportedReturns 501, for the same reason.
GET /1/insights/{id}LaterReturns 501 until minelog can read crash causes and mod lists.
POST /1/analyseLaterReturns 501, on the same schedule as insights.

/1/limits returns our numbers in the units mclo.gs uses:

200 OK
{
  "storageTime": 3456000,
  "maxLength": 15728640,
  "maxLines": 30000
}

Our limits differ from theirs. A tool that reads /1/limits before uploading picks ours up by itself. One that hard codes the mclo.gs numbers stays inside them, which always fits.

Stability

/v2 is a draft until the API launches. After that:

  • We can add new fields to a response at any time. If your client ignores fields it doesn't know, nothing breaks.
  • Changes that would break a working client go into a new version, /v3. /v2 keeps working alongside it.
  • The /1 paths follow mclo.gs. If they add something we'll look at it, but we can't promise the same day.
  • Logs are deleted after 40 days, whichever protocol saved them.