Docs / Site feeds
Site feeds
A feed is a long-lived exchange between one camera at one site and the platform. Video is cut into short segments, each carrying a sequence number, and delivered inside a single session. Sites lose connectivity constantly — a truck parks on the line, an LTE modem reboots — so the protocol is built around resuming rather than restarting.
Endpoint
GET /api/v1/plants/feed/{plant}/{stream} HEAD /api/v1/plants/feed/{plant}/{stream} GET transfers a segment.
HEAD acknowledges one and requests the next window without
moving a body — on a metered LTE link the difference over a month is not academic.
plant is the site identifier, stream
the camera within it.
Opening a session
The session identifier travels in the session_id cookie and the
current sequence number in csrftoken. A session lives 24 hours
from the last exchange, so a healthy site opens one and keeps it for days.
curl -X POST https://<endpoint>/api/v1/plants/session \
-H "Authorization: Bearer $NORDVIK_SITE_KEY" \
-d "plant=gdansk-dc2" \
-d "stream=gate-a" \
-d "segment_seconds=4" Response:
{
"session": "sess_8f21ac",
"plant": "gdansk-dc2",
"stream": "gate-a",
"segment_seconds": 4,
"next_seq": 0,
"expires_at": "2026-08-04T18:00:00Z"
} Moving segments
Pull a segment:
curl -s "https://<endpoint>/api/v1/plants/feed/gdansk-dc2/gate-a?seq=4812" \
-H "Authorization: Bearer $NORDVIK_SITE_KEY" \
--cookie "session_id=sess_8f21ac; csrftoken=4812" \
-o segment-4812.ts Acknowledge it and request the next window:
curl -I "https://<endpoint>/api/v1/plants/feed/gdansk-dc2/gate-a?seq=4812" \
-H "Authorization: Bearer $NORDVIK_SITE_KEY" \
--cookie "session_id=sess_8f21ac; csrftoken=4812" Backfill after an outage
# After the uplink comes back, ask what we actually have:
curl -s "https://<endpoint>/api/v1/plants/session/sess_8f21ac" \
-H "Authorization: Bearer $NORDVIK_SITE_KEY"
# {"session":"sess_8f21ac","acked_seq":4811,"gap":[4780,4811]}
# Push the gap, then continue live. Recorders that restart from zero
# are the single most common integration mistake. Limits
| Segment length | 2 to 10 seconds, 4 by default |
| Session lifetime | 24 hours from the last exchange |
| Concurrent streams per site | up to 32 |
| Retention | 30 / 180 / 365 days depending on plan |
| Backfill window | up to 72 hours after the gap |
Response codes
| Code | Meaning |
|---|---|
200 | Segment returned. The body is the media payload. |
400 | No usable session: the session cookie is missing, the sequence number is malformed, or the request arrived outside an open session. A bare browser request to this endpoint lands here. |
404 | The session expired, or the plant and stream pair does not exist. |
429 | Too many concurrent segment requests for this site. Back off using the Retry-After header. |
Opening this URL in a browser returns 400, not an error page:
there is no session to attach the request to. That is expected and is not a sign the site is down —
check service status instead.