GET /api/v1/videos/id/{video_id}/status - Video Processing Status
Overview
After submitting a video with POST /api/v1/videos, the client needs a way to check whether processing is complete without fetching the full video object on every poll. This lightweight endpoint returns only the status field — a minimal projection from the videos table.
Why it exists: Polling for full video objects wastes bandwidth. By returning only the status field (a tiny payload), clients can poll at high frequency without significant overhead.
HTTP Details
- Method: GET
- Path:
/api/v1/videos/id/{video_id}/status - Auth Required: Yes — creator or moderator role
- Success Status: 200 OK
Path Parameters
| Parameter | Type | Description |
|---|---|---|
video_id |
UUID | The video to check |
Request
GET /api/v1/videos/id/550e8400-e29b-41d4-a716-446655440000/status
Authorization: Bearer <jwt-token>
Response Body
{
"videoId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSING"
}
Cassandra Concepts Explained
Lightweight Projections (Column Selection)
Instead of retrieving all columns of the videos row, this endpoint requests only the columns it needs: videoid and status. In CQL, this is done by naming specific columns in the SELECT:
SELECT videoid, status
FROM killrvideo.videos
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
Why this matters: Cassandra reads are row-oriented, meaning even a narrow SELECT still reads from the same partition. However, reading fewer columns reduces:
- Network transfer between Cassandra and the application server
- Application memory for deserializing the response
- Response serialization time
For a table with large columns (e.g., description could be several KB), projecting only status is meaningfully faster.
The Status Enum
The status column is stored as text in Cassandra but treated as a finite enumeration at the application layer:
| Value | Meaning |
|---|---|
PENDING |
Submitted, awaiting background worker |
PROCESSING |
Background worker is actively enriching |
READY |
Fully processed, visible to public |
ERROR |
Processing failed permanently |
The application validates that only these four values are written, but Cassandra itself has no enum type — it stores any text string you write.
Polling Pattern in Async Workflows
This endpoint exists specifically to support the polling pattern for asynchronous operations:
Client Server
| |
|-- POST /videos -----------------> | (submit)
|<-- 202 Accepted, videoId -------- |
| |
|-- GET /videos/{id}/status ------> | (poll)
|<-- 200 { status: "PENDING" } ---- |
| |
| [wait 2 seconds] |
| |
|-- GET /videos/{id}/status ------> | (poll)
|<-- 200 { status: "PROCESSING" } - |
| |
| [wait 2 seconds] |
| |
|-- GET /videos/{id}/status ------> | (poll)
|<-- 200 { status: "READY" } ------ |
| |
|-- GET /videos/{id} -------------> | (fetch full details)
|<-- 200 VideoDetailResponse ------- |
Data Model
Table: videos (projected columns)
CREATE TABLE killrvideo.videos (
videoid uuid PRIMARY KEY,
-- ...other columns...
status text, -- ← what this endpoint reads
error_reason text -- ← useful if status = ERROR
);
Database Queries
Query: Fetch Status by Video ID
Equivalent CQL:
SELECT videoid, status, error_reason
FROM killrvideo.videos
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
Performance: O(1) — partition key lookup. Extremely fast even under heavy polling.
Why error_reason too? If status is ERROR, clients need to know why (e.g., "YouTube video not found", "Private video") to display a useful message.
Implementation Flow
┌──────────────────────────────────────────────────────────┐
│ 1. Client sends GET /api/v1/videos/id/{video_id}/status │
│ with Authorization header │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 2. Authenticate: verify JWT │
│ ├─ Invalid/missing JWT? → 401 Unauthorized │
│ └─ Valid JWT? → Continue │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 3. Validate video_id UUID format │
│ └─ Invalid format? → 422 Validation Error │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 4. SELECT videoid, status FROM videos WHERE videoid = ? │
│ ├─ Not found? → 404 Video not found │
│ └─ Found? → Continue │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 5. Return 200 with VideoStatusResponse │
│ { videoId, status } │
└──────────────────────────────────────────────────────────┘
Total queries: 1 SELECT (narrow projection) Expected latency: < 5ms
Special Notes
1. Auth Requirement
This endpoint requires authentication to prevent enumeration attacks — an unauthenticated attacker should not be able to probe for video IDs by polling status endpoints. Only creators (who submitted videos) and moderators can check processing status.
In contrast, GET /api/v1/videos/{id} is public — once a video is READY, anyone can view it.
2. Terminal States
READY and ERROR are terminal states — once reached, the status will not change. Clients should stop polling when they receive either:
terminal_states = {"READY", "ERROR"}
while True:
response = get_video_status(video_id)
if response.status in terminal_states:
break
await asyncio.sleep(exponential_backoff(attempt))
3. Exponential Backoff
Clients should not poll at a fixed fast interval. Use exponential backoff:
- First poll: after 1 second
- Second poll: after 2 seconds
- Third poll: after 4 seconds
- Cap at 30 seconds
Processing typically takes 5–30 seconds. Aggressive polling wastes resources and can overwhelm the API.
4. Error Reason Is Informational Only
When status is ERROR, the error_reason field (if included in the response) describes the failure. This is useful for developer debugging but should be sanitized before displaying to end users — it may contain technical stack traces or API error messages.
5. Authorization Scope
This endpoint uses a broader authorization check: any authenticated creator or moderator can check the status of any video, not just their own. This simplifies the implementation — adding an ownership check would require an extra lookup of the video's userid field.
A stricter implementation would verify that the requesting user owns the video (or is a moderator) before returning status.
Developer Tips
Common Pitfalls
-
Polling too fast: Use exponential backoff. Hammering this endpoint at 10 requests/second provides no benefit over 1 request/5 seconds.
-
Not handling ERROR state: Build a UI for failure — show the error and offer a "retry" or "delete and resubmit" option.
-
Treating this response as authoritative video data: This returns only
videoIdandstatus. For anything else, use GET /api/v1/videos/{id}. -
Forgetting to include Authorization header: Unlike the public video detail endpoint, this requires a JWT.
Best Practices
-
Implement client-side polling with a timeout: If status has not reached READY within 5 minutes, assume ERROR and prompt the user.
-
Cache the terminal state: Once you receive READY or ERROR, cache it locally — no need to re-check.
-
Use WebSockets for real-time updates: A more advanced approach sends the status update to the client via WebSocket/SSE when processing completes, eliminating polling entirely.
-
Log ERROR reasons server-side: Even if you don't show them to users, log all ERROR states with their reason for operational monitoring.
Performance Expectations
| Scenario | Latency | Notes |
|---|---|---|
| Status check (any state) | < 5ms | Narrow column projection |
| 100 concurrent polls | < 10ms each | Cassandra handles this easily |
Related Endpoints
- POST /api/v1/videos - Submit a video (start the workflow)
- GET /api/v1/videos/{id} - Full details once READY
- PUT /api/v1/videos/{id} - Update metadata after READY