Media library filters

Filter, count and total the media library - the same filters the Zentag Studio library uses.

GET /media is the unified library: clips, highlights and streams under one
shape. It accepts the same filters as the Zentag Studio media library, so a
partner UI can offer the same browsing experience.

Three endpoints share one filter vocabulary:

EndpointAnswers
GET /mediaWhich items match?
GET /media/statsHow many, how long, what average rating?
GET /media/filters/countsWhich filter values are still worth offering?

Send the same parameters to all three and they describe the same selection.
That is what lets you render a list, a header total, and a filter sidebar that
greys out dead ends, from one piece of UI state.

How filters combine

Every list-valued filter is comma-separated. Within one parameter, values
are OR-ed; across parameters, they are AND-ed.

?players=p1,p2&rating=5

means (player p1 OR p2) AND rated 5.

Filter reference

Scope

ParameterNotes
searchCase-insensitive substring match on title
stream_idRestrict to one or more streams
streams_onlyReturn stream rows instead of the media inside them
media_typesSubset of clip, highlight, stream

Media attributes

ParameterNotes
aspect_ratioe.g. 16:9,9:16
clip_statusProcessing status, e.g. COMPLETED
download_statusDownload state
ai_event_typeAI-detected event type
category
platformDestination the item was published to
sourceHow the item was created
ratingStar ratings, e.g. 4,5
tagsTag names

Sport context

ParameterNotes
playersPlayer tag ids, from the players facet
actionsAction tag ids, from the actions facet
competition, seasonNames
matches, match_date, match_day
teams, venuesNames

players and actions take ids, not names. Read them from the id field of
the corresponding bucket in /media/filters/counts and pass them straight back.

Time and ordering

ParameterNotes
start_date, end_dateCreation date range, inclusive, YYYY-MM-DD
sort_bylatest (default), oldest, rating, duration, title

Hierarchy fast-path

ParameterFrom facet
hierarchy_competition_refhierarchy_competitions
hierarchy_season_idhierarchy_seasons
hierarchy_round_idhierarchy_rounds
hierarchy_matchday_idhierarchy_matchdays

These filter on structural ids rather than names. Prefer them for a drill-down
UI (competition → season → round → matchday): they are faster than the
equivalent name-based filters and immune to naming inconsistencies.

Building a filter sidebar

GET /media/filters/counts returns each filter's selectable values with the
number of items behind each, computed against the filters you already applied.
Values that would return nothing are absent, so you can hide or disable them.

{
  "object": "media_filter_counts",
  "aspect_ratios": [
    { "value": "16:9", "count": 812 },
    { "value": "9:16", "count": 194 }
  ],
  "players": [
    { "id": "665f...", "name": "A. Rossi", "count": 47 }
  ],
  "facet_pagination": {
    "players": { "total": 312, "has_more": true, "page": 1, "page_size": 20, "offset": 0,
                 "load_more_key": "players:2" }
  }
}

Note the two shapes: most facets are { value, count }, while the tag-backed
players and actions are { id, name, count }.

Buckets are optional

Read every bucket defensively. A bucket appears only when it was computed,
and two parameters deliberately narrow that:

  • counts_bundle computes only one named group of facets. Use it to keep
    sidebar refreshes cheap instead of recomputing everything on each click.
  • facet_load_more returns a single bucket's next page, so the response
    contains just that one bucket.

Paging within a facet

A facet with hundreds of values is paginated on its own. facet_pagination
reports the state per bucket; when has_more is true, send its load_more_key
back as facet_load_more to fetch the next page of that one facet without
recomputing the rest.

facet_page_size and facet_page control this directly if you would rather
drive it yourself.

The match index

Pass include_match_index=true to also receive match_index: the matches in
the current selection, with names, dates, teams and venues. This drives a
"jump to match" control.

Each entry carries a match_ref - a stable, opaque handle. It is safe to store
and compare across requests, but it is not a third-party provider's match id and
cannot be resolved against one. See Identifiers.

Showing totals

GET /media/stats returns the totals for the current selection in one request,
which is far cheaper than paging the whole result set to count it:

{
  "object": "media_stats",
  "total_items": 1204,
  "total_duration_seconds": 22344,
  "total_duration_label": "6h 12m",
  "average_rating": 4.1,
  "processing_count": 3,
  "clip_count": 1150,
  "highlight_count": 54,
  "stream_count": 12,
  "stream_media_count": 1204
}

Pagination

Paginate with cursor, not page. The cursor is opaque - pass next_cursor
back verbatim, and don't parse it. Changing any filter or sort_by invalidates
the cursor; restart from the first page. See Pagination.

Identifiers

Every id returned here is a Zentag identifier. Zentag ingests fixtures, squads
and match data from third-party sports data providers, but provider-internal
identifiers are deliberately never exposed through this API - not in media
items, not in tag metadata, and not in facet counts.

Player tag metadata is therefore a closed set: player_name, short_name,
position, jersey_number, nationality and roster_memberships. Do not
write code expecting other keys to appear, and do not attempt to join Zentag ids
against another provider's data.


Did this page help you?