Overview
No authentication. No account. POST your log content, receive a permanent shareable URL. The viewer renders your log with automatic or specified syntax highlighting, line numbers, and a raw content endpoint.
Base URL: https://report.openutils.net
Create a report
Creates a new log report. Returns an ID and a shareable viewer URL.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | required* | The log content to store. Maximum 20 MB. In multipart/form-data mode, use -F "content=<file.txt" to read from a file without any escaping. |
| file | file | required* | multipart/form-data only. Upload a file directly with -F "file=@path/to/file.log". The filename is used as the default title and the extension is auto-extracted. Mutually exclusive with content. |
| language | string | optional | Language identifier for syntax highlighting — e.g. python, javascript, bash. Falls back to auto-detection if omitted. |
| extension | string | optional | File extension without dot — e.g. log, txt. Used for the download filename. Overrides the auto-extracted extension when using file upload. |
| title | string | optional | Human-readable label displayed in the viewer header. Overrides the filename when using file upload. |
Responses
{
"id": "7e3MhXGsYT9nxsbd6u8Xb6",
"url": "https://report.openutils.net/log/7e3MhXGsYT9nxsbd6u8Xb6"
}
content field.{ "error": "content is required" }
Code examples
# Upload a file directly — no escaping, extension and title auto-detected
curl -sF "[email protected]" \
-F "title=Payment worker crash" \
https://report.openutils.net/api/report
# Form field — inline content without JSON escaping
curl -sF 'content=Error: connection refused at 127.0.0.1:5432' \
-F "language=javascript" \
-F "title=DB failure" \
https://report.openutils.net/api/report
# JSON body
curl -sX POST https://report.openutils.net/api/report \
-H "Content-Type: application/json" \
-d '{"content":"Error: connection refused","language":"javascript","title":"DB failure"}'
# Plain text — pipe stdin directly
curl -sX POST "https://report.openutils.net/api/report?language=bash&title=Build+failure" \
-H "Content-Type: text/plain" \
--data-binary @build.log
import requests
# File upload — no escaping, extension auto-detected from filename
with open("worker.log", "rb") as f:
r = requests.post(
"https://report.openutils.net/api/report",
files={"file": f},
data={"title": "Payment worker crash"}
)
print(r.json()["url"])
# JSON body
r = requests.post(
"https://report.openutils.net/api/report",
json={
"content": open("app.log").read(),
"language": "python",
"title": "Unhandled exception"
}
)
print(r.json()["url"])
// File upload (browser File object or Node.js Blob)
const form = new FormData();
form.append("file", fileObject); // File / Blob
form.append("title", "Payment worker crash");
const res = await fetch("https://report.openutils.net/api/report", {
method: "POST", body: form
});
const { url } = await res.json();
// JSON body
const res2 = await fetch("https://report.openutils.net/api/report", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content: error.stack, language: "javascript", title: "Unhandled rejection" })
});
const { url: url2 } = await res2.json();
// File upload — no escaping needed
$ch = curl_init("https://report.openutils.net/api/report");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
"file" => new CURLFile("/var/log/app/worker.log"),
"title" => "Payment worker crash",
],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data["url"];
// JSON body
$ch = curl_init("https://report.openutils.net/api/report");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"content" => $logContent,
"language" => "php",
"title" => "Exception report",
]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data["url"];
Get a report
Returns the full report as JSON. Calling this endpoint also resets the 6-month inactivity timer for reports under 2 MB.
Response fields
| Field | Type | Description |
|---|---|---|
| id | string | 22-character unique identifier. |
| content | string | The full log content. |
| language | string | null | Language hint provided at creation. |
| extension | string | null | File extension provided at creation. |
| title | string | null | Human-readable title. |
| size | integer | Content size in bytes. |
| created_at | string | Creation timestamp (UTC). |
| last_accessed_at | string | Timestamp of the last view or fetch. |
Example response
{
"id": "7e3MhXGsYT9nxsbd6u8Xb6",
"content": "Error: Connection refused at 127.0.0.1:5432\n at db.js:42",
"language": "javascript",
"extension": "log",
"title": "DB connection failure",
"size": 58,
"created_at": "2026-05-03 16:50:30",
"last_accessed_at": "2026-05-03 18:22:11"
}
Get raw content
Returns the log content as text/plain with Content-Disposition: inline. The suggested filename in the header is derived from the title and extension set at creation.
Use this endpoint to pipe output into other commands or to view unrendered content directly in a browser tab.
curl -s https://report.openutils.net/api/report/7e3MhXGsYT9nxsbd6u8Xb6/raw \
| grep "ERROR"
Rate Limiting
Upload requests (POST) are limited to 6 per minute per IP address. The counter resets every 60 seconds. Read requests (GET) are not rate-limited.
When the limit is exceeded the API responds with 429 Too Many Requests and a Retry-After: 60 header.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
{ "error": "Rate limit exceeded: 6 uploads per minute allowed" }
Storage Rules
Reports are stored subject to the following expiry rules. Cleanup runs daily at 03:00 UTC. Once deleted, a URL returns 404 permanently.
| Condition | Behaviour |
|---|---|
| ≤ 2 MB | Deleted after 6 consecutive months without any view. Every visit to the viewer or API resets the timer. |
| 2 MB – 20 MB | Hard expiry 30 days after upload, regardless of access frequency. |