Skip to main content

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.

  1. Drop a checks file (oneport-apiwatch.yaml) and a scheduled workflow into your repo.
  2. GitHub Actions wakes every 5 minutes, runs oneport-apiwatch check, and the job goes red if any endpoint is down.
  3. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oneport_apiwatch-0.1.0.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oneport_apiwatch-0.1.0-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

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

Hashes for oneport_apiwatch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 95740f5d8ea0f158face1711b2a16acb37f3b7d04f9a2b2f088fe2f6c4f7bbb4
MD5 278a32335609cec5611ec6c9ab8adc29
BLAKE2b-256 ff534cfe90dcbc6e0f3c435af0c56ee079c09fd1c46d6607b550054d7b65cd95

See more details on using hashes here.

File details

Details for the file oneport_apiwatch-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for oneport_apiwatch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ff700988be9c8dd28a0c1170bc19aa9f39aa52c962ed2c016746e051fe234df5
MD5 a936b4d790d596461a39db946544ab8b
BLAKE2b-256 fbfbfa4c8ccee05cbe361bd13a2032778dd9fd93ff2f6e2fa348215fd5f2ce0e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page