Media Clips (MP4 Download)
Ring provides an endpoint to download historical video content as MP4 files. This complements live video streaming by enabling access to recorded footage for analysis, storage, or playback.
416 TIMESTAMP_NOT_FOUND error.Overview
- MP4 format: Standard video format for broad compatibility
- Configurable quality: Adjustable resolution, frame rate, and codec
- Audio support: Optional audio inclusion
- Flexible duration: Up to 15 minutes per request
- Partial content support: Handles cases where full requested duration isn't available
API Endpoint
Unlike other Ring APIs, the media download endpoint does not follow JSON:API specification and uses standard HTTP constructs with MIME types.
POST https://api.amazonvision.com/v1/devices/{device_id}/media/video/download
Authorization: Bearer <access_token>
Content-Type: application/json
Request Parameters
Required Parameters
| Parameter | Type | Description | Constraints |
|---|---|---|---|
timestamp |
integer | Start time in epoch milliseconds | ≤ now() |
duration |
integer | Video length in milliseconds | ≤ 900,000 (15 minutes) |
Optional Parameters
Video Options
| Parameter | Type | Description | Default |
|———–|——|————-|———|
| video_options.codec | string | Video codec ("avc" or "hevc") | Device default |
| video_options.frame_rate | integer | Frames per second | Device default (≤ 25) |
| video_options.resolution.width | integer | Video width in pixels | Device default (≤ 1920) |
| video_options.resolution.height | integer | Video height in pixels | Device default (≤ 1080) |
Audio Options
| Parameter | Type | Description | Default |
|———–|——|————-|———|
| audio_options.audio_enabled | boolean | Include audio in video file | false |
Request Example
{
"timestamp": 1699457230000,
"duration": 5000,
"video_options": {
"codec": "avc",
"frame_rate": 5,
"resolution": {
"width": 1080,
"height": 720
}
},
"audio_options": {
"audio_enabled": true
}
}
Response Handling
Response Headers
- Content-Type: MIME type for the media content
- X-Media-Timestamp: Actual start timestamp of the provided video
- X-Media-Length: Actual duration in milliseconds (for partial responses)
HTTP Status Codes
200 OK - Full Content
HTTP/1.1 200 OK
Content-Type: video/mp4
X-Media-Timestamp: 1699457230000
Content-Length: 2048576
[Binary MP4 data]
206 Partial Content
HTTP/1.1 206 Partial Content
Content-Type: video/mp4
X-Media-Timestamp: 1699457235000
X-Media-Length: 3000
[Binary MP4 data]
Partial content occurs when:
- Requesting video beyond current time
- Requesting video from before recording started
- Device was offline during part of the requested timeframe
301 Redirect
Redirects may occur when video processing is handled by different servers. Clients must follow redirects.
Implementation Example
import requests
from datetime import datetime
def download_video_clip(device_id, access_token, start_time, duration_ms):
url = f"https://api.amazonvision.com/v1/devices/{device_id}/media/video/download"
payload = {
"timestamp": int(start_time.timestamp() * 1000),
"duration": duration_ms
}
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers, allow_redirects=True)
if response.status_code == 200:
return {"status": "complete", "data": response.content}
elif response.status_code == 206:
return {
"status": "partial",
"data": response.content,
"actual_duration": response.headers.get("X-Media-Length")
}
else:
response.raise_for_status()
Recommended Workflow: Record Then Download
Since the download endpoint only retrieves existing footage, you must ensure the camera is recording before requesting a clip:
- Start a live video session (which triggers recording on supported devices)
- Wait for the desired recording duration
- Stop the session
- Download the clip using the time window from the session
import time
from datetime import datetime
def record_and_download(device_id, access_token, duration_seconds=10):
# Step 1: Start a live video session (triggers recording)
session = start_live_session(device_id, access_token)
record_start = datetime.utcnow()
# Step 2: Wait for the desired recording duration
time.sleep(duration_seconds)
# Step 3: Stop the live video session
stop_live_session(device_id, session["session_id"], access_token)
# Step 4: Small delay to allow media processing
time.sleep(2)
# Step 5: Download the recorded clip
clip = download_video_clip(
device_id=device_id,
access_token=access_token,
start_time=record_start,
duration_ms=duration_seconds * 1000
)
return clip
Common Mistakes
1. Assuming Download Triggers Recording
The most common mistake. This endpoint is read-only — it retrieves footage that was already recorded.
Wrong:
clip = download_video_clip(device_id, token, some_timestamp, 5000)
Correct:
session = start_live_session(device_id, token)
time.sleep(10)
stop_live_session(device_id, session["session_id"], token)
time.sleep(2)
clip = download_video_clip(device_id, token, session_start_time, 10000)
2. Requesting Clips from Periods with No Recording
If you request a clip for a time range when the device was idle, there is no stored footage. Use webhook notifications to track motion_detected events to confirm when the camera was recording.
Best Practices
- Reasonable durations: Request appropriate clip lengths
- Quality selection: Choose resolution and frame rate based on use case
- Audio consideration: Only include audio when needed to reduce file size
- Handle partial content: Process 206 responses appropriately
- Follow redirects: Always allow redirect following
- Retry logic: Implement exponential backoff for transient failures
Next: Error Handling →

