DEV Community

John Wick
John Wick

Posted on

Build a Public Status Page for Your Python Bot in One Function Call

How to create a python bot status page with uptime history, incident logs, and an admin view using StayPresent's built-in web.status().

Build a Public Status Page for Your Python Bot in One Function Call

A real status page — the kind with rolling uptime percentages, an incident timeline, and per-service health — has traditionally meant reaching for a separate hosted service, or building one yourself against a database you have to maintain. StayPresent's web.status() builds this directly from data it's already tracking internally, with zero external dependencies and no separate infrastructure.

Table of Contents

  1. What You Get by Default
  2. Customizing the Status Page
  3. Public vs Admin Views
  4. Setting Up the Admin API Key
  5. Controlling Which Services Appear
  6. Naming and Describing Services
  7. Full Example
  8. Best Practices
  9. Common Mistakes
  10. FAQs
  11. Conclusion

What You Get by Default

You don't need to call anything explicitly — a status page is served automatically at /status alongside / and /health, the moment you start using staypresent.run():

import staypresent

staypresent.run("bot.py")
Enter fullscreen mode Exit fullscreen mode

Visiting /status shows current service state, rolling uptime figures (24 hours, 7 days, 30 days, and lifetime), restart counts, and a friendly incident history — all derived from data StayPresent is already collecting for crash recovery, with nothing extra to configure.

Customizing the Status Page

Call web.status() explicitly when you want to change the title, add a footer, or brand the page:

staypresent.web.status(
    title="Groundflare Bot Status",
    copyright="Groundflare Inc.",
    footer_links=[
        {"label": "Support", "url": "https://support.groundflare/support"},
    ],
    mode="dark",
)
Enter fullscreen mode Exit fullscreen mode

mode follows the same theming convention as web.markdown()"light", "dark", or "auto" to follow the visitor's own OS/browser preference.

Public vs Admin Views

The page deliberately separates two audiences. The public view shows overall status, uptime figures, and friendly incident descriptions — safe to expose to end users or link from a support page. The admin view — exit codes and recent stdout/stderr log tails, now captured via a ring buffer as of v1.6.0 — sits behind a login gated by api_key.

Setting Up the Admin API Key

If you don't set an api_key yourself, StayPresent generates a random one per session and logs it, so admin access is still available without you needing to configure anything up front:

staypresent.web.status(
    api_key="a-long-random-secret-you-control",
)
Enter fullscreen mode Exit fullscreen mode

To disable the admin view entirely — for a deployment where you're confident you'll never need the log tail — pass an empty string:

staypresent.web.status(api_key="")
Enter fullscreen mode Exit fullscreen mode

Admin login attempts are rate-limited to 5 per 15 minutes specifically to protect against brute-force/timing attacks against the key. If you're running behind a trusted reverse proxy and need accurate client IPs for that rate limiting, trust_proxy_headers=True is available.

Controlling Which Services Appear

Every route-registering function accepts status=True/False to control whether it shows up as its own row on the status page:

staypresent.web.json({"status": "online"}, path="/telegram", status=True)
staypresent.web.json({"internal": "debug-only"}, path="/debug", status=False)
Enter fullscreen mode Exit fullscreen mode

Bots are included by default unless explicitly opted out — useful if you're running an internal debug endpoint you don't want showing up on a page you might eventually make public.

Naming and Describing Services

Rather than showing a raw path or filename, services_name/services_description let you label a row meaningfully:

staypresent.run(
    bots=[
        {"file": "telegram_bot.py", "services_name": "Telegram Bot", "services_description": "Handles inbound messages"},
        {"file": "discord_bot.py", "services_name": "Discord Bot", "services_description": "Community server integration"},
    ],
)
Enter fullscreen mode Exit fullscreen mode

Full Example

import os
import staypresent

staypresent.web.status(
    title="My Bot Fleet",
    copyright="My Company",
    footer_links=[{"label": "Docs", "url": "https://example.com/docs"}],
    mode="auto",
    api_key=os.getenv("STATUS_ADMIN_KEY"),
)

staypresent.run(
    bots=[
        {"file": "telegram_bot.py", "services_name": "Telegram Bot"},
        {"file": "discord_bot.py", "services_name": "Discord Bot"},
    ],
    port=int(os.getenv("PORT", 8080)),
    heartbeat_timeout=30,
)
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Set your own api_key via an environment variable for anything genuinely public-facing, rather than relying on the auto-generated per-session key.
  • Use services_name/services_description for any bot whose filename wouldn't be meaningful to someone outside your project.
  • If the status page is meant to be genuinely public, review what's showing before sharing the URL — use status=False on any internal-only endpoint you don't want visible.

Common Mistakes

  • Assuming the admin log tail is off by default. It's on, gated by an auto-generated key that's logged — check your logs for that key, or set your own explicitly, rather than assuming there's no admin access at all.
  • Forgetting trust_proxy_headers=True behind a reverse proxy, which can cause the rate limiter to treat all traffic as coming from one IP (the proxy's) rather than distinguishing real clients.
  • Not customizing services_name for a fleet of similarly-named bot files, leaving a status page that shows several confusingly similar filenames instead of clear service names.

FAQs

Does the status page require a separate database?
No — everything it shows is derived from data StayPresent already tracks in-process for crash recovery and restart counting.

Can I move the status page to a different path?
Yes — web.status() accepts a path argument like other route functions, the same way web.json()/web.html() do.

Is the admin view secure enough for a genuinely sensitive deployment?
The rate-limited API-key gate is a reasonable default, but for anything highly sensitive, consider also restricting network access to /status itself via your hosting platform's own access controls, rather than relying solely on the API key.

Conclusion

A python bot status page with real uptime history and incident tracking no longer requires a separate monitoring service — staypresent.web.status() builds one directly from data StayPresent already has, live at /status by default, with a customizable public view and a properly gated admin view for deeper diagnostics.

pip install "staypresent[prod]"
Enter fullscreen mode Exit fullscreen mode

Top comments (0)