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

PropertyValue
ProtocolHTTPS only
FormatJSON — Content-Type: application/json
AuthX-API-Key header (pre-shared key)
Base URLhttps://ingest.hopsensorsllc.com
Ingest endpoints4 — telemetry, transformer, event, outage
Query endpoints10 — dashboard read access
TimestampsISO 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
Never expose your API key in client-side code, public repositories, or logs. If you believe your key has been compromised, contact info@hopsensorsllc.com immediately for a rotation.

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

StatusMeaning
200 OKRequest succeeded. Response body confirms what was written.
401 UnauthorizedMissing or invalid X-API-Key header.
422 Unprocessable EntityRequest body failed validation. Response includes field-level error detail.
500 Internal Server ErrorDatabase write or query failed. Response includes a detail string.

POST /ingest/telemetry Ingest

POST /ingest/telemetry

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

FieldRequiredTypeDescription
substation_idrequiredstringYour identifier for this substation. Must be consistent across all records. E.g. SUB001
region_idrequiredstringRegion identifier. E.g. PGE-REGION-01
voltage_kvrequiredfloatBus voltage in kilovolts.
current_arequiredfloatLine current in amperes.
power_mwrequiredfloatActive power in megawatts.
frequency_hzrequiredfloatGrid frequency in hertz. This field drives ML anomaly scoring.
feeder_idoptionalstringFeeder identifier. E.g. FDR002. Enables per-feeder event correlation.
equipment_idoptionalstringDevice or RTU identifier. E.g. SUB001-RTU-01
equipment_typeoptionalstringAsset type tag. E.g. substation, transformer, feeder. Default: substation
site_nameoptionalstringHuman-readable site label shown in the dashboard. E.g. Portland North
utility_idoptionalstringUtility identifier. Inferred from API key if omitted.
status_codeoptionalstringOperational status tag from your SCADA system. E.g. NORMAL, OUTAGE
timestampoptionalstringISO 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"
}
Response — 200 OK
{ "status": "ok", "utility": "pge", "substation_id": "SUB001" }

POST /ingest/transformer Ingest

POST /ingest/transformer

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

FieldRequiredTypeDescription
substation_idrequiredstringParent substation identifier.
region_idrequiredstringRegion identifier.
equipment_idrequiredstringUnique transformer identifier. E.g. XFMR-001
oil_temp_coptionalfloatOil temperature in Celsius.
tap_positionoptionalintegerCurrent tap changer position.
load_percentoptionalfloatTransformer load as a percentage of rated capacity.
voltage_kvoptionalfloatSecondary voltage in kilovolts.
current_aoptionalfloatSecondary current in amperes.
site_nameoptionalstringHuman-readable site label.
status_codeoptionalstringOperational status. E.g. NORMAL, OVERLOAD
timestampoptionalstringISO 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"
}
Response — 200 OK
{ "status": "ok", "utility": "pge", "equipment_id": "XFMR-001" }

POST /ingest/event Ingest

POST /ingest/event

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

FieldRequiredTypeDescription
device_idrequiredstringDevice or RTU originating the event. E.g. SUB001-FDR002
severityrequiredstringALARM triggers the alerting pipeline. INFO is logged only, no alert sent.
event_typerequiredstringSee event types table below.
feeder_idoptionalstringFeeder affected. Used for FEEDER_TRIP / FEEDER_RESTORE correlation.
old_stateoptionalstringState before the transition. E.g. CLOSED
new_stateoptionalstringState after the transition. E.g. OPEN
messageoptionalstringFree-text description included in alert emails.
latitudeoptionalfloatEvent location for map overlay.
longitudeoptionalfloatEvent location for map overlay.
timestampoptionalstringISO 8601 UTC. Defaults to time of receipt.

Event Types

event_typeseverityEffect
FEEDER_TRIPALARMLogged + email alert sent (subject to rate limits). Marks feeder as tripped.
FEEDER_RESTOREINFOLogged + restore notification sent (only if a TRIP alert was previously sent for this feeder).
(any other)INFO or ALARMLogged 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"
}
Response — 200 OK
{ "status": "ok", "utility": "pge" }

POST /ingest/outage Ingest

POST /ingest/outage

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

FieldRequiredTypeDescription
outage_idrequiredstringUnique identifier for this outage. Must match on the restore record. E.g. OUT-SUB001-FDR002-1749650000
feeder_idrequiredstringFeeder affected.
customers_outrequiredintegerCustomers without power. Send 0 on restore.
statusrequiredstringACTIVE — outage in progress. RESTORED — outage cleared.
substation_idoptionalstringParent substation.
region_idoptionalstringRegion identifier.
mw_interruptedoptionalfloatLoad interrupted in megawatts.
causeoptionalstringCause category. E.g. STORM, EQUIPMENT, ANIMAL, UNKNOWN
etr_timeoptionalstringEstimated time to restore — ISO 8601 UTC.
timestampoptionalstringISO 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"
}
Response — 200 OK
{ "status": "ok", "utility": "pge", "outage_id": "OUT-SUB001-FDR002-1749650000" }

GET /query/telemetry/latest Query

GET /query/telemetry/latest

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.

All query endpoints are scoped to your utility. You will only receive data for your own substations.
Response — array of objects
[
  {
    "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

GET /query/telemetry/timeseries

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

ParamTypeDefaultDescription
substation_idstringRequired. Substation to query.
minutesinteger30Lookback 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

GET /query/telemetry/history

Returns full telemetry history for a single substation, backed by a 7-day rolling window in Redis.

Query Parameters

ParamTypeDefaultDescription
substation_idstringRequired. Substation to query.
hoursinteger24Lookback window in hours. Max: 168 (7 days).
GET /query/telemetry/history?substation_id=SUB001&hours=48

GET /query/transformer/latest Query

GET /query/transformer/latest

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

GET /query/ml/latest

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.

Response — array of objects
[
  {
    "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

GET /query/ml/trend

Returns ML anomaly score history across all substations for trend visualization on the ML Anomaly tab.

Query Parameters

ParamTypeDefaultDescription
minutesinteger30Lookback window in minutes.
GET /query/ml/trend?minutes=60

GET /query/events Query

GET /query/events

Returns recent grid events — feeder trips, restores, and all other logged SCADA events — ordered most-recent first.

Query Parameters

ParamTypeDefaultDescription
minutesinteger30Lookback window in minutes. Max returned: 100 records.
GET /query/events?minutes=60

GET /query/outages/active Query

GET /query/outages/active

Returns currently active outages — records with status = ACTIVE written within the last 2 hours, deduplicated by outage_id.

Response — array of objects
[
  {
    "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

GET /query/frequency/thresholds

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

ParamTypeDefaultDescription
minutesinteger30Lookback window in minutes.
GET /query/frequency/thresholds?minutes=30

GET /query/substations Query

GET /query/substations

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.

Response — array of objects
[
  { "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.

MetricWarningAlarm
frequency_hz59.85 – 60.15 HzOutside 59.5 – 60.5 Hz
voltage_kv218 – 242 kVOutside 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.

FieldTypeSource
utility_id, region_id, substation_idtagIngest
feeder_id, equipment_id, equipment_type, site_nametagIngest
voltage_kv, current_a, power_mw, frequency_hzfieldIngest
status_codefieldIngest

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.

FieldTypeSource
utility_id, substation_id, equipment_idtagML service
anomaly_score, is_anomaly, z_scorefieldML service
baseline_mean, baseline_std, latest_value, model_versionfieldML 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

GET /health

Returns API and Redis health status. No authentication required. Use for uptime monitoring and integration validation.

GET https://ingest.hopsensorsllc.com/health
Response — 200 OK
{
  "status":      "ok",
  "version":     "v6",
  "redis":       true,
  "redis_keys":  1842,
  "timestamp":   "2026-06-11T14:32:00.000000+00:00"
}