GitHub Traffic API: скользящее окно и почему uniques нельзя суммировать¶
Статус: Confirmed (поведение API и формат ответов — реальными запросами
к восьми публичным репозиториям 2026-10-10) + Observed (структура реального
снимка той же даты; сами значения не публикуются)
Область применения: корректное накопление статистики просмотров/клонов
собственных репозиториев за месяц, квартал и год; прежде всего — типовая
ошибка сложения «уникальных посетителей» из перекрывающихся окон
Проверено на: GitHub REST GET /repos/{owner}/{repo}/traffic/views и
.../traffic/clones; токен с правом чтения traffic-метрик; приватный снимок
владельца по 8 репозиториям saymer-alt (значения метрик здесь не
приводятся)
Последняя проверка: 2026-10-10
Source of truth: живое поведение GitHub Traffic API на дату проверки;
официальная документация —
как справочник форматов, не как гарантия сроков хранения
Provenance: оригинальная статья, написана по результатам проверки
скрипта github_traffic_snapshot.py в профильном репозитории
saymer-alt/saymer-alt
(issue #2)
Что решаем¶
GitHub показывает просмотры и клоны только за «последние ~14 дней». Чтобы строить графики за месяц и дольше, окно приходится снимать периодически и копить. Статья фиксирует, что именно возвращает API, что можно суммировать, а что — нельзя, и как правильно копить данные.
Контекст¶
- Ответ каждого эндпоинта содержит:
count(события за всё окно),uniques(уникальные посетители за всё окно) и серию суточных записейtimestamp/count/uniques. - Окно скользящее: каждый снимок покрывает примерно последние 14 дней, включая уже снятые ранее дни.
timestampсуточных записей — полночь UTC (2026-10-09T00:00:00Z).
Как это работает¶
Что аддитивно. Каждое «просмотр»/«клон» — это отдельное событие; события
за разные сутки не пересекаются, поэтому суточные count из серии можно
суммировать за любой период.
Что не аддитивно. uniques — результат дедупликации посетителей в
пределах одного запроса (для серии — в пределах суток, для верхнего
uniques — в пределах всего окна). Один и тот же посетитель дня X попадает
в ~14 последовательных снимков, потому что день X остаётся в скользящем
окне около двух недель. Сумма uniques по снимкам посчитает его до
четырнадцати раз; сумма верхних uniques двух снимков с разницей в неделю
пересчитает общие для обоих окон дни дважды.
Оценки уникальных за период — верхняя и нижняя границы. Синтетический пример: период — 10 дней, каждые сутки приходит 1 новый уникальный посетитель и «старый» посетитель A заходит каждый день. Тогда каждые сутки дают 2 uniques, каждое 14-дневное окно (если бы оно целиком покрывало период) — 11 uniques (10 новых + A).
- Верхняя граница уникальных за период — сумма
uniquesпо непересекающимся частям периода: она игнорирует посетителей, встречающихся в нескольких частях (в примере A посчитан десять раз), и поэтому никогда не меньше фактического числа. - Нижняя граница — максимум верхних
uniquesпо одному окну, входящему в период: внутри одного окна дедупликация уже выполнена (посетитель A посчитан один раз), но посетители, приходившие только вне этого окна, не учтены.
Следствие. Точное число уникальных посетителей за месяц из перекрывающихся 14-дневных окон восстановить нельзя: посетитель, приходивший 1-го и 20-го числа, ни в одном отдельном окне не выглядит «одним посетителем месяца». Любое агрегированное число за период — это оценка между нижней и верхней границей, а не точное значение.
Правильная схема накопления¶
- Снимать снимок не реже одного раза в 14 дней (лучше — ежедневно): данные дня, попавшего в очередь на «выпадение» из окна, иначе теряются навсегда.
- Хранить весь JSON-снимок за день: файл с именем
traffic-YYYY-MM-DD.json, без перезаписи старых дней. - Для графиков: суммировать суточные
countсерии views/clones;uniquesпоказывать только «как есть», с пометкой окна, либо давать границы периода — нижнюю (максимумuniquesодного окна) и верхнюю (суммаuniquesнепересекающихся частей), явно помечая их как оценки. - Суточная запись из более свежего снимка приоритетнее той же записи из более старого: GitHub может досчитать события дня до конца суток.
Требования¶
- Токен GitHub с правом чтения traffic-метрик репозитория (для собственных репозиториев — обычный token owner'а); traffic-эндпоинты недоступны анонимно.
- Репозитории, по которым снимается статистика; список — фиксированный, не по обходу каталога.
Проверка¶
Разовая проверка структуры ответа (поля и суточная серия):
gh api repos/<owner>/<repo>/traffic/views
Ожидаемо: count и uniques — целые; views — массив записей с
timestamp в полночь UTC. Число записей у свежего репозитория может быть
меньше 14 (окно растёт с момента первого дня с трафиком).
Проверка защиты от перезаписи (повторный запуск снимка в тот же день должен завершиться отказом, старый файл — остаться нетронутым):
python3 scripts/github_traffic_snapshot.py --output-dir ~/github-traffic-private
# повтор: FileExistsError на traffic-<сегодня>.json
Типичные проблемы¶
Сумма уникальных за месяц получилась больше суммы просмотров¶
Признак сложения uniques из перекрывающихся окон. Пересчитайте по
суточным count для событий и оставьте uniques только в пределах одного
окна/суток.
Часть дней в серии отсутствует¶
У репозитория не было трафика в эти дни, либо день уже выпал из скользящего окна к моменту первого снимка. Окно не восстанавливается задним числом.
HTTP 403 на traffic-запросе¶
Токену недоступна traffic-метрика (нужен доступ уровня push к репозиторию — на практике права владельца) или сработал rate limit. Скрипт снимка в профильном репозитории переводит любой такой отказ в явную ошибку и не пишет частичный файл.
Что подтверждено, а что нет¶
Confirmed (2026-10-10):
- Формат ответов (поля, полночь UTC, целочисленные счётчики) на 8 репозиториях; суточные серии по 14 записей, дни без повторов.
- Отказ API (403/404/429), пустой и некорректный JSON обрабатываются snapshot-скриптом как ошибки; при отказе в середине сбора файл не создаётся вовсе.
- Файл дня создаётся с
O_EXCL: повторный запуск не перезаписывает данные.
Observed:
- На реальном снимке владельца от 2026-10-10 по восьми публичным репозиториям формат ответов и поведение скрипта соответствовали описанию (значения метрик не публикуются; для иллюстраций в этой статье используются только синтетические примеры).
Still research / unknown:
- Точный момент дедупликации
uniquesвнутри окна (GitHub не раскрывает алгоритм); для накопления это несущественно — складывать uniques всё равно нельзя. - Гарантии сохранения истории самим GitHub: документировано только «~14 дней», поэтому внешнее копирование обязательно.
Источники и provenance¶
- REST API:
GET /repos/{owner}/{repo}/traffic/views|clones(справочник); - скрипт снимка:
scripts/github_traffic_snapshot.pyвsaymer-alt/saymer-alt; - проверка выполнена на реальном снимке владельца 2026-10-10; сырые метрики и пути к приватным данным здесь не приводятся.
Связанные материалы¶
- Основы Keenetic и Entware — смежная методология разделения подтверждённых фактов и наблюдений.