Analytics
Query views, downloads and the audience watching now.
You will measure activity for your own videos over a date range, group it by a useful dimension, and read the current viewer count.
Query views
curl --fail-with-body --request GET 'https://api.todayfiles.com/v1/analytics/views' \
--header "Authorization: Bearer $TODAYFILES_API_KEY"const response = await fetch("https://api.todayfiles.com/v1/analytics/views", {
method: "GET",
headers: {
Authorization: "Bearer " + process.env.TODAYFILES_API_KEY,
},
});
if (!response.ok) throw new Error(await response.text());
const result = response.status === 204 ? null : await response.json();
console.log(result);import json
import os
import requests
response = requests.request(
"GET", "https://api.todayfiles.com/v1/analytics/views",
headers={
"Authorization": "Bearer " + os.environ["TODAYFILES_API_KEY"],
},
timeout=30,
)
response.raise_for_status()
print(response.json() if response.content else None)<?php
$client = curl_init("https://api.todayfiles.com/v1/analytics/views");
curl_setopt_array($client, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TODAYFILES_API_KEY'),
],
]);
$body = curl_exec($client);
if ($body === false) throw new RuntimeException(curl_error($client));
$status = curl_getinfo($client, CURLINFO_RESPONSE_CODE);
curl_close($client);
if ($status >= 400) throw new RuntimeException($body);
$result = $body === '' ? null : json_decode($body, true, 512, JSON_THROW_ON_ERROR);
print_r($result);package main
import (
"encoding/json"
"io"
"net/http"
"os"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://api.todayfiles.com/v1/analytics/views", nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + os.Getenv("TODAYFILES_API_KEY"))
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1 << 20))
if err != nil { panic(err) }
if response.StatusCode >= 400 { panic(string(body)) }
var result any
if len(body) > 0 {
if err := json.Unmarshal(body, &result); err != nil { panic(err) }
}
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil { panic(err) }
}Add from=2026-10-30&to=2026-11-01&group_by=date,video to group a range by date and video. Use dates as YYYY-MM-DD. Rows contain views, completed_views and unique_viewers. Add video_id=h87x to select one video.
Choose a comma-separated combination of date, video, country, device, referrer and player. Dimension fields appear only when you group by them. limit accepts 1 to 100 rows.
Query downloads
curl --fail-with-body --request GET 'https://api.todayfiles.com/v1/analytics/downloads' \
--header "Authorization: Bearer $TODAYFILES_API_KEY"const response = await fetch("https://api.todayfiles.com/v1/analytics/downloads", {
method: "GET",
headers: {
Authorization: "Bearer " + process.env.TODAYFILES_API_KEY,
},
});
if (!response.ok) throw new Error(await response.text());
const result = response.status === 204 ? null : await response.json();
console.log(result);import json
import os
import requests
response = requests.request(
"GET", "https://api.todayfiles.com/v1/analytics/downloads",
headers={
"Authorization": "Bearer " + os.environ["TODAYFILES_API_KEY"],
},
timeout=30,
)
response.raise_for_status()
print(response.json() if response.content else None)<?php
$client = curl_init("https://api.todayfiles.com/v1/analytics/downloads");
curl_setopt_array($client, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TODAYFILES_API_KEY'),
],
]);
$body = curl_exec($client);
if ($body === false) throw new RuntimeException(curl_error($client));
$status = curl_getinfo($client, CURLINFO_RESPONSE_CODE);
curl_close($client);
if ($status >= 400) throw new RuntimeException($body);
$result = $body === '' ? null : json_decode($body, true, 512, JSON_THROW_ON_ERROR);
print_r($result);package main
import (
"encoding/json"
"io"
"net/http"
"os"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://api.todayfiles.com/v1/analytics/downloads", nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + os.Getenv("TODAYFILES_API_KEY"))
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1 << 20))
if err != nil { panic(err) }
if response.StatusCode >= 400 { panic(string(body)) }
var result any
if len(body) > 0 {
if err := json.Unmarshal(body, &result); err != nil { panic(err) }
}
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil { panic(err) }
}Download rows contain downloads and unique_downloaders. They accept the same dimensions, plus quality.
Unique counts are exact at the daily reporting dimension. Summing across days or dimensions can count the same viewer again; these totals are an upper bound on distinct people over the combined range.
Read the current audience
curl --fail-with-body --request GET 'https://api.todayfiles.com/v1/analytics/realtime' \
--header "Authorization: Bearer $TODAYFILES_API_KEY"const response = await fetch("https://api.todayfiles.com/v1/analytics/realtime", {
method: "GET",
headers: {
Authorization: "Bearer " + process.env.TODAYFILES_API_KEY,
},
});
if (!response.ok) throw new Error(await response.text());
const result = response.status === 204 ? null : await response.json();
console.log(result);import json
import os
import requests
response = requests.request(
"GET", "https://api.todayfiles.com/v1/analytics/realtime",
headers={
"Authorization": "Bearer " + os.environ["TODAYFILES_API_KEY"],
},
timeout=30,
)
response.raise_for_status()
print(response.json() if response.content else None)<?php
$client = curl_init("https://api.todayfiles.com/v1/analytics/realtime");
curl_setopt_array($client, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TODAYFILES_API_KEY'),
],
]);
$body = curl_exec($client);
if ($body === false) throw new RuntimeException(curl_error($client));
$status = curl_getinfo($client, CURLINFO_RESPONSE_CODE);
curl_close($client);
if ($status >= 400) throw new RuntimeException($body);
$result = $body === '' ? null : json_decode($body, true, 512, JSON_THROW_ON_ERROR);
print_r($result);package main
import (
"encoding/json"
"io"
"net/http"
"os"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://api.todayfiles.com/v1/analytics/realtime", nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + os.Getenv("TODAYFILES_API_KEY"))
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1 << 20))
if err != nil { panic(err) }
if response.StatusCode >= 400 { panic(string(body)) }
var result any
if len(body) > 0 {
if err := json.Unmarshal(body, &result); err != nil { panic(err) }
}
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil { panic(err) }
}The response contains viewers and window_seconds. The window is 300 seconds. These requests use the analytics rate class, separate from ordinary reads. Use the returned rate-limit headers to choose your refresh interval.