TAŇBA-OOH Разработчикам

City operational report - plays and screens, load, anomalies

GET /api/v1/stats/reports

https://app.tanba-ooh.kz/api/v1/stats/reports

Технический текст справочника написан на английском.

Scope read:stats. Own organisation; the authority sees the city. Answered from the hourly aggregates; days are Asia/Almaty days. series holds plays and active screens (structures with a play) per period, district and format; load the distribution of plays per screen per day; anomalies the screens to look at; reconciliation the plays an operator imported against the plays the players reported, per structure, approval and finished day.

Аутентификация

Ключ API в заголовке Authorization или сессия консоли.

Право доступа: read:stats

Параметры

ИмяГдеТипОбязательныйОписаниеПример
fromзапросstring (date)нетFirst day (default 29 days back).2026-09-08
toзапросstring (date)нетLast day (default today).2026-10-06
grainзапросstringнетLength of one period: day, week or month. day, week, monthday

Ответы

200 The report sections.

application/json

ИмяТипОбязательныйОписаниеПример
fromstring (date)даFirst day of the report."2026-09-08"
tostring (date)даLast day of the report."2026-10-06"
grainstringдаLength of one period in series: day, week or month."day"
seriesarray of objectдаPlays and active screens for each period, district and format.
series[].periodstring (date)даFirst day of the period."2026-10-05"
series[].districtstringдаCode of the district."bostandyk"
series[].formatstringдаCode of the format."cityboard"
series[].playsintegerдаPlays in the period.48210
series[].screensintegerдаScreens with at least one play in the period.36
loadobjectдаHow plays per screen per day are distributed.
load.screen_daysintegerдаNumber of screen-days counted.1008
load.meanintegerдаMean plays per screen per day.1340
load.medianintegerдаMedian plays per screen per day.1325
load.p10integerдаPlays per screen per day that 10 % of screen-days stay under.980
load.p90integerдаPlays per screen per day that 90 % of screen-days stay under.1710
load.maxintegerдаMost plays on one screen in one day.2010
load.histogramarray of objectдаScreen-days by bands of plays.
load.histogram[].fromintegerдаLower edge of the band, in plays.1000
load.histogram[].tointegerдаUpper edge of the band, in plays.1500
load.histogram[].countintegerдаScreen-days in the band.412
anomaliesarray of objectдаThe screens to look at.
anomalies[].kindstringдаWhat was found. low_plays, silent, refusal_spike
anomalies[].structure_idstring (uuid)даIdentifier of the structure."b7e4a2c9-0d1f-4a68-8e35-91c7f2d40b16"
anomalies[].codestringдаCode of the structure."ALM-0012"
anomalies[].addressstringдаStreet address of the structure."Al-Farabi Ave 77/7"
anomalies[].districtstringдаCode of the district."bostandyk"
anomalies[].observedintegerдаPlays observed on that day.120
anomalies[].baselineintegerдаPlays expected from the structure's own history.1300
anomalies[].daystringдаThe Asia/Almaty day concerned."2026-10-05"
reconciliationarray of objectдаImported plays against reported plays, for each structure, approval and finished day.
reconciliation[].daystring (date)даThe Asia/Almaty day compared."2026-10-05"
reconciliation[].structurestringдаStructure code."ALM-0012"
reconciliation[].approval_numberstringдаNumber of the approval compared."TNB-2026-000123"
reconciliation[].imported_playsintegerдаPlays the operator imported for that day.2830
reconciliation[].reported_playsintegerдаPlays the players reported for that day.2841
reconciliation[].reconciled_rationumber | nullдаImported divided by reported; null while the players reported nothing.0.996

Пример

{
  "from": "2026-09-08",
  "to": "2026-10-06",
  "grain": "day",
  "series": [
    {
      "period": "2026-10-05",
      "district": "bostandyk",
      "format": "cityboard",
      "plays": 48210,
      "screens": 36
    }
  ],
  "load": {
    "screen_days": 1008,
    "mean": 1340,
    "median": 1325,
    "p10": 980,
    "p90": 1710,
    "max": 2010,
    "histogram": [
      {
        "from": 1000,
        "to": 1500,
        "count": 412
      }
    ]
  },
  "anomalies": [
    {
      "kind": "low_plays",
      "structure_id": "b7e4a2c9-0d1f-4a68-8e35-91c7f2d40b16",
      "code": "ALM-0012",
      "address": "Al-Farabi Ave 77/7",
      "district": "bostandyk",
      "observed": 120,
      "baseline": 1300,
      "day": "2026-10-05"
    }
  ],
  "reconciliation": [
    {
      "day": "2026-10-05",
      "structure": "ALM-0012",
      "approval_number": "TNB-2026-000123",
      "imported_plays": 2830,
      "reported_plays": 2841,
      "reconciled_ratio": 0.996
    }
  ]
}

Ошибки

Ошибки приходят документом problem (application/problem+json) со стабильным полем code.

СтатусЗначение
401Нет действительной сессии или ключа API (unauthenticated, session_expired, invalid_api_key).
403Роль или право ключа этого не допускает (forbidden, key_scope_denied, key_route_denied).
422Ошибки полей (validation_failed).
defaultОшибка в виде документа problem.

Примеры кода

curl

curl 'https://app.tanba-ooh.kz/api/v1/stats/reports' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Python
import requests

response = requests.get(
    "https://app.tanba-ooh.kz/api/v1/stats/reports",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
    },
)
print(response.status_code, response.text)
Go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://app.tanba-ooh.kz/api/v1/stats/reports", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}