Developers & API
A public REST API for status, incidents and maintenance — plus an embeddable widget, badges, feeds and calendars.
Getting started
All public endpoints are plain HTTPS GET requests that return JSON. No key is needed. Try the system status:
curl -s "https://status.yucts.com/api/v2/status?locale=en"
# Conditional request — 304 Not Modified when nothing changed
curl -s -i "https://status.yucts.com/api/v2/services?locale=ja" -H 'If-None-Match: "<etag from the previous response>"'API versions
- v1 (/api/*) is the stable 1.x API used by existing apps and widgets. It is frozen: fields are never removed or renamed.
- v2 (/api/v2/*) is the current API: consistent envelopes, error codes, request ids, ETags, cursor pagination and seven languages.
Deprecation policy
Breaking changes only ship under a new version path. A deprecated version stays available for at least 12 months and announces itself with Deprecation and Sunset headers before removal.
Authentication
Every endpoint on this page is public and read-only: no authentication is needed. Administrative endpoints are not part of this documentation — signed-in administrators find their reference in the admin console (API reference). Never put credentials in a URL.
Endpoints
The complete reference, generated from the same code that serves the API, is available as an OpenAPI 3.1 document. /openapi.json
Errors
v2 errors share one shape. Use the code (not the message) in your logic and include the request id when reporting a problem.
{
"error": {
"code": "not_found",
"message": "Incident not found.",
"status": 404,
"request_id": "c0ffee00-…"
}
}Rate limits
Requests that miss the edge cache are limited per client IP (600 per minute by default). When limited you receive HTTP 429 with a Retry-After header.
Caching
Public responses are cacheable for 30 seconds (v1: 60 seconds) and carry an ETag — send it back in If-None-Match to get 304 Not Modified. Personal responses such as locale detection are never cached.
Languages
Pass locale=zh-TW, zh-HK, zh-CN, en, ja, ko or vi. Missing translations fall back to Japanese; every text field reports the language actually served in its *_locale field.
Webhooks
Administrators can send incident and maintenance events to HTTPS endpoints (generic JSON, Discord or Slack). Every request is signed:
X-Yucts-Signature: t=1767225600,v1=5f2c…
X-Yucts-Event: incident.created
X-Yucts-Delivery: 42Compute HMAC-SHA256 over "<t>.<raw body>" with your webhook secret and compare it to v1 in constant time. Reject timestamps older than five minutes and de-duplicate on X-Yucts-Delivery.
import { createHmac, timingSafeEqual } from 'node:crypto';
// header = X-Yucts-Signature, rawBody = the exact request body string
export function verifyYuctsSignature(rawBody, header, secret) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
if (!m) return false;
const [, t, sig] = m;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
return timingSafeEqual(Buffer.from(sig, 'hex'), expected);
}Code examples
JavaScript
const res = await fetch('https://status.yucts.com/api/v2/status?locale=en');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data } = await res.json();
console.log(data.status); // "operational" | "degraded" | "partial_outage" | ...
console.log(data.active_incidents); // ongoing incidents with their latest updateTypeScript
type ServiceStatus = 'operational' | 'degraded' | 'down' | 'maintenance' | 'unknown';
interface ServiceSummary {
id: number;
name: string;
status: ServiceStatus;
uptime: { h24: number | null; d30: number | null; d90: number | null }; // null = no data
}
async function services(locale = 'ja'): Promise<ServiceSummary[]> {
const res = await fetch(`https://status.yucts.com/api/v2/services?locale=${locale}`);
if (!res.ok) {
const { error } = await res.json(); // { code, message, status, request_id }
throw new Error(`${error.code} (${error.request_id})`);
}
return (await res.json()).data;
}Swift / SwiftUI
struct Envelope<T: Decodable>: Decodable { let data: T }
struct SystemStatus: Decodable {
let status: String
let counts: Counts
struct Counts: Decodable { let total, operational, degraded, down, maintenance, unknown: Int }
}
func fetchStatus(locale: String = "zh-TW") async throws -> SystemStatus {
var url = URLComponents(string: "https://status.yucts.com/api/v2/status")!
url.queryItems = [URLQueryItem(name: "locale", value: locale)]
let (data, response) = try await URLSession.shared.data(from: url.url!)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
return try JSONDecoder().decode(Envelope<SystemStatus>.self, from: data).data
}PHP (WordPress)
<?php
// WordPress: cache the status for 60 seconds with a transient.
function yucts_status() {
$cached = get_transient('yucts_status');
if ($cached !== false) return $cached;
$res = wp_remote_get('https://status.yucts.com/api/v2/status?locale=en', ['timeout' => 5]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) !== 200) return null;
$body = json_decode(wp_remote_retrieve_body($res), true);
set_transient('yucts_status', $body['data'], 60);
return $body['data'];
}Step-by-step guides for Swift/SwiftUI, JavaScript/TypeScript, WordPress and XenForo are in the repository documentation.
Embed widget
Show live status on any page (XenForo, WordPress, plain HTML). The 1.x widget keeps working unchanged; the v2 widget adds the new design, isolation from page styles and seven languages.
<!-- 2.0 widget: same attributes, Shadow DOM isolation, 7 languages -->
<div class="yucts-status" data-view="overview" data-locale="zh-TW"></div>
<script src="https://status.yucts.com/embed/v2.js" defer></script><!-- 1.x widget (unchanged) -->
<div id="yucts-status"></div>
<script src="https://status.yucts.com/embed.js" defer></script>Changelog
- 2.0 — API v2, OpenAPI 3.1, Vietnamese for all content, embed v2, badges, feeds, iCalendar, exports, OpenMetrics (each behind a feature switch).
- 1.x — /api/overview, /api/status, /api/incidents, /api/slo, /api/maintenance, /api/geo, embed.js.