Self-hosted API health monitoring with AI-explained failures — runs in your own cron/CI, never on our servers
Project description
oneport-apiwatch
Self-hosted API health monitoring with AI-explained failures. It runs in your cron or CI, on your keys — never on our servers.
UptimeRobot and Checkly watch your APIs from someone else's cloud. oneport-apiwatch is a single stateless command you schedule in your own GitHub Actions / cron. It probes your endpoints, and when something breaks it explains why in plain English and alerts your Slack — with zero infrastructure on our side and your data never leaving your machine.
✗ payments POST https://api.acme.com/pay [500 · 1840ms]
→ status 500 (expected 200)
→ latency 1840ms over budget 1000ms
AI diagnosis: 500s with "connection pool exhausted" and latency climbing
over the last 3 runs point to a database connection leak.
1. DB connection pool exhausted
2. A recent deploy stopped releasing connections
first action: Check the last deploy and your DB pool metrics.
Monitoring without a server (the headline)
The whole product is one idea: let GitHub's scheduler be the uptime robot.
- Drop a checks file (
oneport-apiwatch.yaml) and a scheduled workflow into your repo. - GitHub Actions wakes every 5 minutes, runs
oneport-apiwatch check, and the job goes red if any endpoint is down. - On failure it explains the cause (your Gemini key) and alerts your Slack / opens an issue in your repo.
No server of ours sits in the path. You own the schedule, the keys, the alerts, and the history file.
# .github/workflows/apiwatch.yml
name: API Watch
on:
schedule:
- cron: "*/5 * * * *" # GitHub's scheduler is your uptime robot
workflow_dispatch: {}
permissions:
contents: read
issues: write
jobs:
watch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install oneport-apiwatch
- uses: actions/cache@v4 # keep status history across runs for trend detection
with:
path: .oneport-apiwatch-history.json
key: apiwatch-history-${{ github.run_id }}
restore-keys: apiwatch-history-
- env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
oneport-apiwatch check --explain --alert slack --alert github-issue
The complete, commented version is in examples/workflows/apiwatch.yml. Prefer plain cron? It's the same one line:
*/5 * * * * cd /srv/checks && oneport-apiwatch check --explain --alert slack
Install
pip install oneport-apiwatch
Python 3.10+. The deterministic check needs no API key at all — a Gemini key is only used for the optional --explain diagnosis (free at aistudio.google.com/apikey).
The checks file
Secrets never live in this file — auth headers are pulled from environment variables by name, so it's safe to commit.
model: gemini-2.5-flash # optional; only used by --explain
history_file: .oneport-apiwatch-history.json
defaults: # applied to every check unless overridden
method: GET
expect_status: 200
latency_budget_ms: 2000
timeout: 10
checks:
- name: homepage
url: https://example.com/
- name: api-health
url: https://api.example.com/health
expect_status: [200, 204] # int, or a list of acceptable codes
latency_budget_ms: 800
headers:
Accept: application/json
body: # JSON-body assertions
- path: status # dot-path into the response (lists: data.items.0.id)
equals: ok
- path: db.connected
equals: true
- path: data.count
gt: 0
- name: billing
url: https://api.example.com/v1/account
auth_header_env: API_TOKEN # value read from $API_TOKEN at runtime
auth_header_name: Authorization
Body assertion operators: equals, not_equals, contains, exists, gt, lt (exactly one per assertion).
A check fails when any of these is true: transport error (DNS / refused / timeout), status not in expect_status, latency over latency_budget_ms, response isn't valid JSON while assertions are set, or any body assertion fails.
CLI
oneport-apiwatch check [OPTIONS]
--config FILE Path to the checks file (default: ./oneport-apiwatch.yaml).
--alert [slack|github-issue] Alert this channel on failure. Repeatable.
--explain Add an AI diagnosis to each failure (needs GEMINI_API_KEY).
--format [inline|json] Output format (default: inline).
--no-history Don't read/write the local status-history file.
oneport-apiwatch auth Show whether a Gemini key is configured.
Exit codes: 0 all healthy · 1 one or more checks failed · 2 config error · 3 a check failed and an alert failed to deliver.
Alerting — your channels, not ours
| Channel | Env it uses | What it does |
|---|---|---|
slack |
SLACK_WEBHOOK_URL |
POSTs a failure summary (with the AI diagnosis) to your own Slack incoming webhook. |
github-issue |
GITHUB_REPOSITORY, GITHUB_TOKEN |
Opens — or updates, deduped by a hidden marker — one issue in your repo describing the failures. |
Both use credentials you provide. Nothing routes through Oneport.
Trend detection
Each run appends {ok, status_code, latency_ms, timestamp} per check to a local JSON file you own (history_file). The --explain layer feeds the recent runs to the model, so it can distinguish "just went down" from "latency has been climbing for three runs." In CI, persist that file with actions/cache (see the workflow) so trends survive across scheduled runs.
Privacy / the moat
Serverless by design. Your endpoints are probed from your machine; the only outbound call besides the endpoints themselves is the optional Gemini request under --explain, made with your key. See PRIVACY.md.
Development
pip install -e ".[dev]"
pytest # 83 tests, respx-mocked endpoints + LLM + Slack, 80% coverage gate
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oneport_apiwatch-0.1.0.tar.gz.
File metadata
- Download URL: oneport_apiwatch-0.1.0.tar.gz
- Upload date:
- Size: 30.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95740f5d8ea0f158face1711b2a16acb37f3b7d04f9a2b2f088fe2f6c4f7bbb4
|
|
| MD5 |
278a32335609cec5611ec6c9ab8adc29
|
|
| BLAKE2b-256 |
ff534cfe90dcbc6e0f3c435af0c56ee079c09fd1c46d6607b550054d7b65cd95
|
File details
Details for the file oneport_apiwatch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: oneport_apiwatch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff700988be9c8dd28a0c1170bc19aa9f39aa52c962ed2c016746e051fe234df5
|
|
| MD5 |
a936b4d790d596461a39db946544ab8b
|
|
| BLAKE2b-256 |
fbfbfa4c8ccee05cbe361bd13a2032778dd9fd93ff2f6e2fa348215fd5f2ce0e
|