HOP Sensors API Reference
Connect your substation systems to HOP Sensors by sending HTTPS requests to the ingest API.
No proprietary hardware or protocol adapters required — any system capable of making HTTP
requests can integrate, including SCADA platforms, DMS systems, RTU data collectors, and
custom middleware. All query endpoints read from Redis and return sub-second responses.
This is the v6 API reference. If you are on an older integration, contact
info@hopsensorsllc.com for a migration guide.
Overview
| Property | Value |
|---|---|
| Protocol | HTTPS only |
| Format | JSON — Content-Type: application/json |
| Auth | X-API-Key header (pre-shared key) |
| Base URL | https://ingest.hopsensorsllc.com |
| Ingest endpoints | 4 — telemetry, transformer, event, outage |
| Query endpoints | 10 — dashboard read access |
| Timestamps | ISO 8601 UTC — e.g. 2026-06-11T14:32:00Z |
Authentication
Every request must include your API key in the X-API-Key header. Your key is provisioned when your account is created. It is scoped to your utility — you can only read and write data for your own substations.
# Required on every request X-API-Key: your-api-key-here # Example curl curl -H "X-API-Key: your-api-key-here" \ https://ingest.hopsensorsllc.com/query/substations
Base URL
All API endpoints share a single base URL:
https://ingest.hopsensorsllc.com
Full example for the telemetry ingest endpoint:
POST https://ingest.hopsensorsllc.com/ingest/telemetry
Errors
| Status | Meaning |
|---|---|
| 200 OK | Request succeeded. Response body confirms what was written. |
| 401 Unauthorized | Missing or invalid X-API-Key header. |
| 422 Unprocessable Entity | Request body failed validation. Response includes field-level error detail. |
| 500 Internal Server Error | Database write or query failed. Response includes a detail string. |
POST /ingest/telemetry Ingest
Write a real-time telemetry reading for one substation. Send one record per substation per polling cycle. The platform supports any rate up to once per second per substation. This endpoint drives the live telemetry panel, time-series charts, frequency monitor, and ML anomaly scoring.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
| substation_id | required | string | Your identifier for this substation. Must be consistent across all records. E.g. SUB001 |
| region_id | required | string | Region identifier. E.g. PGE-REGION-01 |
| voltage_kv | required | float | Bus voltage in kilovolts. |
| current_a | required | float | Line current in amperes. |
| power_mw | required | float | Active power in megawatts. |
| frequency_hz | required | float | Grid frequency in hertz. This field drives ML anomaly scoring. |
| feeder_id | optional | string | Feeder identifier. E.g. FDR002. Enables per-feeder event correlation. |
| equipment_id | optional | string | Device or RTU identifier. E.g. SUB001-RTU-01 |
| equipment_type | optional | string | Asset type tag. E.g. substation, transformer, feeder. Default: substation |
| site_name | optional | string | Human-readable site label shown in the dashboard. E.g. Portland North |
| utility_id | optional | string | Utility identifier. Inferred from API key if omitted. |
| status_code | optional | string | Operational status tag from your SCADA system. E.g. NORMAL, OUTAGE |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt if omitted. |
Example Request
// POST /ingest/telemetry // X-API-Key: your-api-key-here { "substation_id": "SUB001", "region_id": "PGE-REGION-01", "feeder_id": "FDR002", "equipment_id": "SUB001-RTU-01", "equipment_type": "substation", "site_name": "Portland North", "voltage_kv": 229.8, "current_a": 1187.4, "power_mw": 274.2, "frequency_hz": 60.002, "status_code": "NORMAL", "timestamp": "2026-06-11T14:32:00Z" }
{ "status": "ok", "utility": "pge", "substation_id": "SUB001" }POST /ingest/transformer Ingest
Write transformer health telemetry — oil temperature, tap position, and load percentage. Appears on the Telemetry tab transformer panel. Send one record per transformer per polling cycle.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
| substation_id | required | string | Parent substation identifier. |
| region_id | required | string | Region identifier. |
| equipment_id | required | string | Unique transformer identifier. E.g. XFMR-001 |
| oil_temp_c | optional | float | Oil temperature in Celsius. |
| tap_position | optional | integer | Current tap changer position. |
| load_percent | optional | float | Transformer load as a percentage of rated capacity. |
| voltage_kv | optional | float | Secondary voltage in kilovolts. |
| current_a | optional | float | Secondary current in amperes. |
| site_name | optional | string | Human-readable site label. |
| status_code | optional | string | Operational status. E.g. NORMAL, OVERLOAD |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt. |
Example Request
{
"substation_id": "SUB001",
"region_id": "PGE-REGION-01",
"equipment_id": "XFMR-001",
"oil_temp_c": 68.4,
"tap_position": 3,
"load_percent": 74.2,
"voltage_kv": 229.8,
"current_a": 312.7,
"status_code": "NORMAL",
"timestamp": "2026-06-11T14:32:00Z"
}{ "status": "ok", "utility": "pge", "equipment_id": "XFMR-001" }POST /ingest/event Ingest
Write a discrete grid event — a state change, alarm, or operational transition at a device or feeder. Events populate the Alarms & Outages tab. FEEDER_TRIP events with severity ALARM trigger email alerts to configured operators.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
| device_id | required | string | Device or RTU originating the event. E.g. SUB001-FDR002 |
| severity | required | string | ALARM triggers the alerting pipeline. INFO is logged only, no alert sent. |
| event_type | required | string | See event types table below. |
| feeder_id | optional | string | Feeder affected. Used for FEEDER_TRIP / FEEDER_RESTORE correlation. |
| old_state | optional | string | State before the transition. E.g. CLOSED |
| new_state | optional | string | State after the transition. E.g. OPEN |
| message | optional | string | Free-text description included in alert emails. |
| latitude | optional | float | Event location for map overlay. |
| longitude | optional | float | Event location for map overlay. |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt. |
Event Types
| event_type | severity | Effect |
|---|---|---|
| FEEDER_TRIP | ALARM | Logged + email alert sent (subject to rate limits). Marks feeder as tripped. |
| FEEDER_RESTORE | INFO | Logged + restore notification sent (only if a TRIP alert was previously sent for this feeder). |
| (any other) | INFO or ALARM | Logged to the events table. Visible in Alarms & Outages tab. |
Example — Feeder Trip
{
"device_id": "SUB001-FDR002",
"feeder_id": "FDR002",
"severity": "ALARM",
"event_type": "FEEDER_TRIP",
"old_state": "CLOSED",
"new_state": "OPEN",
"message": "Overcurrent protection operated — FDR002",
"latitude": 45.5231,
"longitude": -122.6765,
"timestamp": "2026-06-11T14:35:00Z"
}{ "status": "ok", "utility": "pge" }POST /ingest/outage Ingest
Write or update an outage record with customer impact data. Send an ACTIVE record when an outage begins and a RESTORED record when it is cleared. Outages appear on the Alarms & Outages tab, Executive View, and the outage map.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
| outage_id | required | string | Unique identifier for this outage. Must match on the restore record. E.g. OUT-SUB001-FDR002-1749650000 |
| feeder_id | required | string | Feeder affected. |
| customers_out | required | integer | Customers without power. Send 0 on restore. |
| status | required | string | ACTIVE — outage in progress. RESTORED — outage cleared. |
| substation_id | optional | string | Parent substation. |
| region_id | optional | string | Region identifier. |
| mw_interrupted | optional | float | Load interrupted in megawatts. |
| cause | optional | string | Cause category. E.g. STORM, EQUIPMENT, ANIMAL, UNKNOWN |
| etr_time | optional | string | Estimated time to restore — ISO 8601 UTC. |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt. |
Example — Active Outage
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"substation_id": "SUB001",
"region_id": "PGE-REGION-01",
"customers_out": 847,
"mw_interrupted": 12.4,
"status": "ACTIVE",
"cause": "STORM",
"etr_time": "2026-06-11T16:00:00Z",
"timestamp": "2026-06-11T14:35:00Z"
}Example — Restore
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"customers_out": 0,
"status": "RESTORED",
"timestamp": "2026-06-11T15:48:00Z"
}{ "status": "ok", "utility": "pge", "outage_id": "OUT-SUB001-FDR002-1749650000" }GET /query/telemetry/latest Query
Returns the most recent telemetry reading per substation. Drives the live telemetry panel on the dashboard. Data is served from Redis — sub-second response time.
[
{
"substation_id": "SUB001",
"frequency_hz": 60.002,
"voltage_kv": 229.8,
"current_a": 1187.4,
"power_mw": 274.2,
"time": "2026-06-11 14:32:00+00:00"
}
]GET /query/telemetry/timeseries Query
Returns voltage, power, and frequency time series for a single substation. Drives the time-series charts on the Telemetry tab. Data is sliced from Redis sorted sets with sub-second latency.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| substation_id | string | — | Required. Substation to query. |
| minutes | integer | 30 | Lookback window in minutes. Common values: 5, 15, 30, 60, 360, 1440, 10080 (7 days). |
GET /query/telemetry/timeseries?substation_id=SUB001&minutes=60
GET /query/telemetry/history Query
Returns full telemetry history for a single substation, backed by a 7-day rolling window in Redis.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| substation_id | string | — | Required. Substation to query. |
| hours | integer | 24 | Lookback window in hours. Max: 168 (7 days). |
GET /query/telemetry/history?substation_id=SUB001&hours=48
GET /query/transformer/latest Query
Returns the most recent transformer reading per transformer equipment ID. Includes oil temperature, tap position, and load percentage.
GET /query/transformer/latest
GET /query/ml/latest Query
Returns the most recent ML anomaly score per substation. Drives the score cards on the ML Anomaly tab. Scores are produced every 60 seconds by the ML service using a z-score model on frequency_hz.
[
{
"substation_id": "SUB001",
"anomaly_score": 0.234, // 0.0–1.0. >= 0.7 = anomaly
"is_anomaly": false,
"baseline_mean": 60.001,
"baseline_std": 0.0082,
"z_score": 0.585,
"latest_value": 60.002, // frequency_hz reading that was scored
"time": "2026-06-11 14:32:00+00:00"
}
]GET /query/ml/trend Query
Returns ML anomaly score history across all substations for trend visualization on the ML Anomaly tab.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window in minutes. |
GET /query/ml/trend?minutes=60
GET /query/events Query
Returns recent grid events — feeder trips, restores, and all other logged SCADA events — ordered most-recent first.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window in minutes. Max returned: 100 records. |
GET /query/events?minutes=60
GET /query/outages/active Query
Returns currently active outages — records with status = ACTIVE written within the last 2 hours, deduplicated by outage_id.
[
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"substation_id": "SUB001",
"customers_out": 847,
"mw_interrupted": 12.4,
"cause": "STORM",
"etr_time": "2026-06-11T16:00:00Z",
"time": "2026-06-11 14:35:00+00:00"
}
]GET /query/frequency/thresholds Query
Returns frequency readings for all substations for the threshold plot on the Frequency Monitor tab. Threshold bands are applied client-side: warn 59.85–60.15 Hz, alarm outside 59.5–60.5 Hz.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window in minutes. |
GET /query/frequency/thresholds?minutes=30
GET /query/substations Query
Returns all substations registered to your utility in the HOP Sensors platform. Useful for validating that your integration is sending data for the expected substations.
[
{ "substation_id": "SUB001" },
{ "substation_id": "SUB002" }
]Thresholds
The following thresholds are applied by the HOP Sensors ML and alerting services. Provided here for reference when interpreting query responses.
| Metric | Warning | Alarm |
|---|---|---|
| frequency_hz | 59.85 – 60.15 Hz | Outside 59.5 – 60.5 Hz |
| voltage_kv | 218 – 242 kV | Outside 212 – 248 kV |
| current_a | > 1,600 A | > 1,800 A |
| power_mw | > 400 MW | > 500 MW |
| anomaly_score | > 0.40 | > 0.70 |
| baseline_std | > 0.05 | > 0.10 |
Data Model
All data is written to InfluxDB 3 Core across four measurements and synced to Redis by the cache service. Understanding the schema helps when interpreting query responses.
scada_telemetry
Primary time-series measurement. Stores raw substation telemetry.
| Field | Type | Source |
|---|---|---|
| utility_id, region_id, substation_id | tag | Ingest |
| feeder_id, equipment_id, equipment_type, site_name | tag | Ingest |
| voltage_kv, current_a, power_mw, frequency_hz | field | Ingest |
| status_code | field | Ingest |
scada_transformers
Transformer health readings — oil temperature, tap position, load percentage.
scada_ml_scores
ML anomaly scores written every 60 seconds by the ML service.
| Field | Type | Source |
|---|---|---|
| utility_id, substation_id, equipment_id | tag | ML service |
| anomaly_score, is_anomaly, z_score | field | ML service |
| baseline_mean, baseline_std, latest_value, model_version | field | ML service |
scada_events
Discrete event log. Every feeder trip, restore, and state transition.
outages
Outage lifecycle records. Each outage has an ACTIVE record and a RESTORED record identified by the same outage_id.
GET /health
Returns API and Redis health status. No authentication required. Use for uptime monitoring and integration validation.
GET https://ingest.hopsensorsllc.com/health
{
"status": "ok",
"version": "v6",
"redis": true,
"redis_keys": 1842,
"timestamp": "2026-06-11T14:32:00.000000+00:00"
}