---
title: "Fetch a stream (usage + results summary once ended)"
method: GET
path: "/v1/streams/{id}"
tags: ["streams"]
---

# Fetch a stream (usage + results summary once ended)

`GET /v1/streams/{id}`

## Request

- Base URL: `https://api.primateintelligence.ai`
- URL: `https://api.primateintelligence.ai/v1/streams/{id}`
- Auth: HTTP bearer

## Path parameters

- `id` string, required

## Response `200`

The stream. Terminal streams carry usage {billed_seconds, credit_balance_after} + duration_s + results_summary.

- Stream: A real-time analysis stream (§4.8).
  - `id` string, required: Stream resource id (prefixed ULID). P6 §4.8.
  - `object` 'stream', required
  - `status` 'queued' | 'ready' | 'live' | 'ended', required: Stream lifecycle state (§4.8): queued → ready → live → ended. Terminal immutable.
  - `metering_rule` 'per_second' | 'per_frame': PRI-552: settlement rule pinned at create time. Streams created before the cutover report per_second.
  - `end_reason` 'completed' | 'canceled' | 'insufficient_credits' | 'error' | 'timeout' | 'ice_failed' | 'media_timeout', nullable, required: Why the stream ended. completed — normal end after live. canceled — ended before going live (client action). insufficient_credits — balance exhausted. error — server-side failure. timeout — max_session_s cutoff. ice_failed — WebRTC ICE never connected (see failure_diagnostic). media_timeout — connected but no media/results within the first-frame window. A stream that never went live is never `completed`.
  - `prompt` string, nullable, required
  - `query` object, nullable, required
  - `model` string, required
  - `queue_position` integer, nullable, required
  - `estimated_start_s` integer, nullable, required
  - `signaling` StreamSignaling, nullable, required: Present while the stream is joinable; null once ended.
    - `url` string, required: Versioned signaling WS. Auth: ?token= accepts a client token (pvct_) or first-party Clerk JWT — NEVER a secret key.
    - `expires_at` string, date-time, required
  - `ice_servers` object[], nullable, required
  - `limits` StreamLimits, nullable, required
    - `max_session_s` integer, required: Plan entitlement (max_session_s) — hard session cutoff.
    - `warn_at_remaining_s` integer, required: The `warning {remaining_s}` threshold surfaced mid-stream.
  - `duration_s` integer, nullable, required: Live-clock duration. Terminal only.
  - `results_summary` object, nullable, required: Terminal summary: {result_frames, frames (deprecated alias of result_frames — removed ~2026-08-28), last_detections}. result_frames counts result EVENTS emitted (results are sampled, not per-source-frame). Detection rows use the same enums/casing as the file-API result contract (answer: yes|no|indeterminate, lowercase; prompt echoed exactly as submitted).
  - `failure_diagnostic` object, nullable, required: Present when end_reason is ice_failed (and some media_timeout cases): ICE candidate summary from the server side — {local_candidates: string[], hint}. Use it to distinguish "server advertised only private/host candidates" from "client could not reach relay".
  - `recording` StreamRecordingState, nullable: PRI-496: recording state — null unless the stream opted in with recording: true at create.
    - `status` 'recording' | 'available' | 'failed' | 'none', required
  - `usage` StreamUsage, nullable, required: Terminal only. Same ledger as uploads — one balance.
    - `billed_seconds` integer, required: Settled ledger amount. per_frame streams (PRI-552, the default): derived from the processed-frame count. Historical per_second streams: LIVE-clock derived (join/negotiation free).
    - `credit_balance_after` integer, nullable, required
    - `metering_rule` 'per_second' | 'per_frame': PRI-552: the rule this stream settled under. Mirrors the top-level metering_rule for a self-contained terminal summary.
    - `frames` integer, nullable: PRI-552: processed-frame count (results_summary.result_frames). Present for both metering rules — frame counting predates the billing cutover; null when unknown.
  - `metadata` object: Free-form key/value metadata. ≤16 keys, key ≤40 chars [a-zA-Z0-9_.-], value ≤500 chars.
  - `created_at` string, date-time, required
  - `live_started_at` string, date-time, nullable, required
  - `ended_at` string, date-time, nullable, required

## Other responses

- `400`: validation_failed / url_forbidden / prompt errors (§15)
- `401`: invalid_api_key / key_revoked / key_expired
- `404`: resource_not_found (account-scoped; no existence oracle)
- `409`: resource_conflict / idempotency_key_reused / idempotency_unavailable
- `429`: rate_limit_exceeded / concurrency_limit_exceeded / quota_exceeded (always with Retry-After)

---

[API](https://skmtc.dev/primateintelligence/apis/primate-vision-api.md) · [All operations](https://skmtc.dev/primateintelligence/apis/primate-vision-api/llms.txt) · [OpenAPI document](https://skmtc.dev/primateintelligence/apis/primate-vision-api/revisions/aa51b01dc8ad?raw)
