We have added a public endpoint where you can access the status of your status page. To access it, you only need the unique :slug you have chosen for your page.

curl https://api.openstatus.dev/public/status/:slug

The response is a JSON object with the following structure:

{ "status": "operational" }

in which the status can be one of the following values:

enum Status {
  Operational = "operational",
  DegradedPerformance = "degraded_performance",
  PartialOutage = "partial_outage",
  MajorOutage = "major_outage",
  UnderMaintenance = "under_maintenance", // currently not in use
  Unknown = "unknown",
  Incident = "incident",

How does it work?

The status is calculated by the ratio uptime / (uptime + downtime) of the last 5 cron jobs of each monitor which we accumulate together. It is a simple calculation that gives us a good overview of the status of your services.

function getStatus(ratio: number) {
  if (isNaN(ratio)) return Status.Unknown;
  if (ratio >= 0.98) return Status.Operational;
  if (ratio >= 0.6) return Status.DegradedPerformance;
  if (ratio >= 0.3) return Status.PartialOutage;
  if (ratio >= 0) return Status.MajorOutage;
  return Status.Unknown;

We are caching the result for 30 seconds to reduce the load on our database.

The Status.Incident will always be returned when then status of any incident on your page is not “monitoring” or “resolved”. You can attach an incident to a monitor (implicit) or a page (explicit).

If you have a doubt about the above calculation, feel free to contact us via ping@openstatus.dev or discord.

React Widget

We currently do not provide any SDK or package to integrate the status into your stack. But will in the future.

If you are using Nextjs and RSC with the /app directory, feel free to use that Component. Small reminder that we are using shadcn ui and tailwindcss. You might want to update the bg-muted and text-foreground classes to your needs.

Status Widget

We are using zod to validate the response. You can use any other library if you want or just remove it. But better be safe than sorry.

import * as z from "zod";

const statusEnum = z.enum([

const statusSchema = z.object({ status: statusEnum });

const dictionary = {
  operational: {
    label: "Operational",
    color: "bg-green-500",
  degraded_performance: {
    label: "Degraded Performance",
    color: "bg-yellow-500",
  partial_outage: {
    label: "Partial Outage",
    color: "bg-yellow-500",
  major_outage: {
    label: "Major Outage",
    color: "bg-red-500",
  unknown: {
    label: "Unknown",
    color: "bg-gray-500",
  incident: {
    label: "Incident",
    color: "bg-yellow-500",
  under_maintenance: {
    label: "Under Maintenance",
    color: "bg-gray-500",
} as const;

export async function StatusWidget({ slug }: { slug: string }) {
  const res = await fetch(`https://api.openstatus.dev/public/status/${slug}`, {
    next: { revalidate: 60 }, // cache request for 60 seconds
  const data = await res.json();
  const parsed = statusSchema.safeParse(data);

  if (!parsed.success) {
    return null;

  const key = parsed.data.status;
  const { label, color } = dictionary[key];

  return (
      className="border-border text-foreground/70 hover:bg-muted hover:text-foreground inline-flex max-w-fit items-center gap-2 rounded-md border px-3 py-1 text-sm"
      <span className="relative flex h-2 w-2">
        {parsed.data.status === "operational" ? (
            className={`absolute inline-flex h-full w-full animate-ping rounded-full ${color} opacity-75 duration-1000`}
        ) : null}
          className={`relative inline-flex h-2 w-2 rounded-full ${color}`}