Grade Channels API

The Channels API from Grade — 5 operation(s) for channels.

Operations 6

GET /api/channels Busca paginada do catálogo público, com as facetas de categoria da busca atual #
GET /api/channels/{id} Ficha completa de um canal, com os streams já apontando para o nosso hop #
GET /api/channels/{id}/health Por que o canal falha, para quem e onde — inclui geo-bloqueio, latência por… #
GET /api/channels/{id}/guia Programação de hoje do canal, grabada por nós: o que está no ar agora e o que… #
GET /api/channels/{id}/comments Comentários públicos de um canal, do mais novo para o mais antigo #
POST /api/channels/{id}/comments Escreve um comentário no canal. Teto de 20 por hora por dono #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/gradetv-channels-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

gradetv-channels-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Grade Channels API
  version: dbad45cb
  description: Catálogo IPTV, biblioteca pessoal com feeds e informações para produtores sobre transmissão autorizada sob consulta.
servers:
- url: https://gradetv.net
tags:
- name: Channels
paths:
  /api/channels:
    get:
      operationId: search_channels
      summary: Busca paginada do catálogo público, com as facetas de categoria da busca atual
      description: 'É a porta de entrada do produto. A resposta varia por navegador, sistema e país de quem pede — cada canal traz `social.your_fails`, o recorte do SEU ambiente — por isso ela é `Cache-Control: private`.

        Devolve: { items[{id,name,alt_names,country,categories,category_labels,languages,language_labels,is_nsfw,logo_url,website,playable_hint?,slug,network,owners,launched,replaced_by,feed_name,feed_format,timezones,broadcast_area,quality?,has_guide,guide_site,guide_lang,subdivision,city,kind,radio?,origem,guide_now?,health_ext,social?,api}], total, limit, offset, next_offset, facets{categories}, filters{q,country,category,language,network,quality,guide,subdivision,city,nsfw,playable,sort,online,kind,tag} }'
      security: []
      parameters:
      - name: q
        in: query
        required: false
        schema:
          type: string
        description: Texto livre no nome e nos apelidos do canal (busca full-text).
        example: globo
      - name: country
        in: query
        required: false
        schema:
          type: string
        description: País do canal, ISO 3166-1 alpha-2.
        example: BR
      - name: category
        in: query
        required: false
        schema:
          type: string
        description: ID de categoria do iptv-org.
        example: news
      - name: language
        in: query
        required: false
        schema:
          type: string
        description: Idioma do canal, ISO 639-3.
        example: por
      - name: network
        in: query
        required: false
        schema:
          type: string
        description: Nome exato da rede/emissora.
        example: Globo
      - name: quality
        in: query
        required: false
        schema:
          type: string
        description: Qualidade exata do stream.
        example: 1080p
      - name: guide
        in: query
        required: false
        schema:
          type: boolean
          default: '0'
          enum:
          - '0'
          - '1'
        description: '`1` traz só canal com grade de programação (EPG).'
      - name: subdivision
        in: query
        required: false
        schema:
          type: string
        description: Estado/província, código do iptv-org.
        example: BR-SP
      - name: city
        in: query
        required: false
        schema:
          type: string
        description: Cidade, código do iptv-org.
      - name: nsfw
        in: query
        required: false
        schema:
          type: boolean
          default: '0'
          enum:
          - '0'
          - '1'
        description: '`1` inclui conteúdo adulto; exige consentimento 18+ gravado, senão 403.'
      - name: playable
        in: query
        required: false
        schema:
          type: boolean
          default: '1'
          enum:
          - '0'
          - '1'
        description: '`0` inclui canal sem stream utilizável conhecido.'
      - name: sort
        in: query
        required: false
        schema:
          type: string
          default: name
          enum:
          - name
          - score
          - votes
        description: '`score` ordena pela saúde medida por terceiro (IPTV Nexus), melhor primeiro; canal não medido vai para o fim. `votes` ordena pelos votos da comunidade do Radio Browser (rádio). `name` é a ordem alfabética.'
      - name: online
        in: query
        required: false
        schema:
          type: boolean
          default: '0'
          enum:
          - '0'
          - '1'
        description: '`1` traz só canal visto online pela fonte (IPTV Nexus para TV, Radio Browser para rádio) nas 48 h anteriores à última recarga do catálogo (`health_ext.online`); com o catálogo parado há mais de 48 h o filtro não devolve ninguém.'
      - name: kind
        in: query
        required: false
        schema:
          type: string
          default: tv
          enum:
          - tv
          - radio
          - all
        description: '`tv` (padrão) é o catálogo de TV; `radio` são as estações do Radio Browser; `all` junta os dois. Sem `kind`, rádio nunca aparece.'
      - name: tag
        in: query
        required: false
        schema:
          type: string
        description: Tag da estação de rádio (vocabulário livre do Radio Browser, ex. `mpb`, `news`); veja `GET /api/tags`.
        example: mpb
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          default: 20
        description: Itens por página. Acima de 50 é silenciosamente reduzido a 50.
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          default: 0
        description: Quantos itens pular. Use `next_offset` da resposta anterior.
      responses:
        '200':
          description: '{ items[{id,name,alt_names,country,categories,category_labels,languages,language_labels,is_nsfw,logo_url,website,playable_hint?,slug,network,owners,launched,replaced_by,feed_name,feed_format,timezones,broadcast_area,quality?,has_guide,guide_site,guide_lang,subdivision,city,kind,radio?,origem,guide_now?,health_ext,social?,api}], total, limit, offset, next_offset, facets{categories}, filters{q,country,category,language,network,quality,guide,subdivision,city,nsfw,playable,sort,online,kind,tag} }'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginaDeCanais'
        '403':
          description: Pediu `nsfw=1` sem consentimento 18+ gravado. A recusa não descreve o canal.
      tags:
      - Channels
  /api/channels/{id}:
    get:
      operationId: get_channel
      summary: Ficha completa de um canal, com os streams já apontando para o nosso hop
      description: 'Devolve: { id, name, alt_names, country, categories, category_labels, languages, language_labels, is_nsfw, logo_url, website, playable_hint?, slug, network, owners, launched, replaced_by, feed_name, feed_format, timezones, broadcast_area, quality?, has_guide, guide_site, guide_lang, subdivision, city, kind, radio?{tags,votes,clicks,codec,bitrate,geo}, origem, guide_now?{day,site,agora,a_seguir}, health_ext{score,online,checked_at}, social?{plays,fails,favorites,comments,last_fail_code,health,your_plays,your_fails,your_fail_code,your_country_ok,your_country_fail,your_geo_ok,your_latency_ms,your_latency_grade}, api, streams[{id,feed,title,url,quality,needs_headers,label,scheme,kind,youtube?,playable_hint,origem,health_ext}], _links{self,app,api_index} }'
      security: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: '{ id, name, alt_names, country, categories, category_labels, languages, language_labels, is_nsfw, logo_url, website, playable_hint?, slug, network, owners, launched, replaced_by, feed_name, feed_format, timezones, broadcast_area, quality?, has_guide, guide_site, guide_lang, subdivision, city, kind, radio?{tags,votes,clicks,codec,bitrate,geo}, origem, guide_now?{day,site,agora,a_seguir}, health_ext{score,online,checked_at}, social?{plays,fails,favorites,comments,last_fail_code,health,your_plays,your_fails,your_fail_code,your_country_ok,your_country_fail,your_geo_ok,your_latency_ms,your_latency_grade}, api, streams[{id,feed,title,url,quality,needs_headers,label,scheme,kind,youtube?,playable_hint,origem,health_ext}], _links{self,app,api_index} }'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CanalCompleto'
        '403':
          description: Canal adulto sem consentimento 18+ gravado.
        '404':
          description: Canal não existe no catálogo.
      tags:
      - Channels
  /api/channels/{id}/health:
    get:
      operationId: channel_health
      summary: Por que o canal falha, para quem e onde — inclui geo-bloqueio, latência por…
      description: 'É o que separa ''o canal está fora do ar'' de ''o canal está bloqueado no seu país''. `geo` diz se é restrição regional ou falha geral (com os países), `regions[]` traz sucesso/falha por país, `latency[]` a velocidade de abertura medida no hop por país, e `pra_voce` resume tudo para o país de quem chama. Sem relato da comunidade (`POST /api/play-report`) o painel fica vazio.

        Devolve: { channel_id, plays, fails, favorites, comments, health, last_ok_at, last_fail_at, last_fail_code, min_relatos, reasons[{code,label,hint,count,last_at}], environments[{browser,os,country,label,plays,fails,health,last_fail_code,last_at}], your_environment{browser,os,country,label,plays,fails,health,last_fail_code,last_at}, regions[{country,plays,fails,health,latency_ms,latency_grade}], geo{tipo,bloqueado_em,funciona_em,label}, latency[{country,samples,avg_ms,grade,grade_label,last_at}], your_country{country,plays,fails,health,latency_ms,latency_grade}, pra_voce, codes, _links{self,channel,report} }'
      security: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: '{ channel_id, plays, fails, favorites, comments, health, last_ok_at, last_fail_at, last_fail_code, min_relatos, reasons[{code,label,hint,count,last_at}], environments[{browser,os,country,label,plays,fails,health,last_fail_code,last_at}], your_environment{browser,os,country,label,plays,fails,health,last_fail_code,last_at}, regions[{country,plays,fails,health,latency_ms,latency_grade}], geo{tipo,bloqueado_em,funciona_em,label}, latency[{country,samples,avg_ms,grade,grade_label,last_at}], your_country{country,plays,fails,health,latency_ms,latency_grade}, pra_voce, codes, _links{self,channel,report} }'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaudeCanal'
        '404':
          description: Canal não existe no catálogo.
      tags:
      - Channels
  /api/channels/{id}/guia:
    get:
      operationId: get_channel_guide
      summary: 'Programação de hoje do canal, grabada por nós: o que está no ar agora e o que…'
      description: 'Vem do grabber do c3 (iptv-org/epg em sites como mi.tv e meuguia.tv) e vale por dois dias. Só canal com guia (`guide=1`) tem; a ficha já traz o resumo em `guide_now`. Nada disto vira página indexável.

        Devolve: { channel_id, day, site, agora{inicio,fim,titulo,desc?,categoria?}, a_seguir{inicio,fim,titulo,desc?,categoria?}, programas[{inicio,fim,titulo,desc?,categoria?}] }'
      security: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: '{ channel_id, day, site, agora{inicio,fim,titulo,desc?,categoria?}, a_seguir{inicio,fim,titulo,desc?,categoria?}, programas[{inicio,fim,titulo,desc?,categoria?}] }'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GuiaDoDia'
        '404':
          description: Canal sem guia do dia (ou com guia velha).
      tags:
      - Channels
  /api/channels/{id}/comments:
    get:
      operationId: list_comments
      summary: Comentários públicos de um canal, do mais novo para o mais antigo
      description: 'Com credencial na chamada, cada comentário seu vem com `mine: true` — é assim que a interface sabe o que dá para apagar.

        Devolve: { items[{id,channel_id,author,body,created_at,mine,api}], total, limit, offset, next_offset, api, channel_id, max_length }'
      security: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          default: 20
        description: Itens por página. Acima de 50 é silenciosamente reduzido a 50.
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          default: 0
        description: Quantos itens pular. Use `next_offset` da resposta anterior.
      responses:
        '200':
          description: '{ items[{id,channel_id,author,body,created_at,mine,api}], total, limit, offset, next_offset, api, channel_id, max_length }'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginaDeComentarios'
        '404':
          description: Canal não existe no catálogo.
      tags:
      - Channels
    post:
      operationId: post_comment
      summary: Escreve um comentário no canal. Teto de 20 por hora por dono
      description: 'Sem `author`, o apelido é gerado e fica estável para o mesmo dono — a pessoa não vira um nome diferente a cada mensagem.

        Devolve: { ok, comment{id,channel_id,author,body,created_at,mine,api} }'
      security:
      - bearerAuth: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  description: O texto do comentário; o teto vem em `max_length` da listagem.
                author:
                  type: string
                  description: Apelido a usar; sem ele o servidor gera um estável.
              required:
              - body
            example:
              body: Só abre no formato 720p.
              author: Wendel
      responses:
        '200':
          description: '{ ok, comment{id,channel_id,author,body,created_at,mine,api} }'
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    description: Sempre `true` quando o comentário entrou.
                  comment:
                    allOf:
                    - $ref: '#/components/schemas/Comentario'
                    description: O comentário criado, do jeito que ele aparece na listagem.
                required:
                - ok
                - comment
        '400':
          description: Texto vazio ou acima de `max_length`.
        '401':
          description: Sem credencial ou credencial inválida. Veja a auth deste endpoint.
        '404':
          description: Canal não existe.
        '429':
          description: Passou de 20 comentários na hora.
      tags:
      - Channels
components:
  schemas:
    SaudeMedidaStream:
      type: object
      properties:
        status:
          type: string
          description: '`online`, `offline`, `blocked` (geo), `timeout`, `error` ou `unknown`.'
        score:
          type: integer
          description: '0–100, média móvel: uma falha só não derruba o stream.'
          nullable: true
        checked_at:
          type: string
          description: Instante (ISO-8601) da sondagem gravada; regravado quando o status ou o score mudam, ou a cada 7 dias.
          nullable: true
        resolution:
          type: string
          description: Resolução vista pelo ffprobe, ex. `1080p`.
          nullable: true
        bitrate:
          type: integer
          description: Bitrate em bits por segundo, quando medido.
          nullable: true
        latency_ms:
          type: integer
          description: Tempo até o primeiro byte na sondagem, em ms.
          nullable: true
      required:
      - status
      - score
      - checked_at
      - resolution
      - bitrate
      - latency_ms
      description: A sondagem mais recente deste stream pelo IPTV Nexus; `null` quando o stream não foi medido.
    FiltrosCanal:
      type: object
      properties:
        q:
          type: string
          description: Busca textual aplicada.
          nullable: true
        country:
          type: string
          description: País aplicado, já em maiúsculas.
          nullable: true
        category:
          type: string
          description: Categoria aplicada.
          nullable: true
        language:
          type: string
          description: Idioma aplicado, já normalizado para ISO 639-3.
          nullable: true
        network:
          type: string
          description: Rede aplicada.
          nullable: true
        quality:
          type: string
          description: Qualidade aplicada.
          nullable: true
        guide:
          type: boolean
          description: Se o filtro de grade de programação estava ligado.
        subdivision:
          type: string
          description: Subdivisão aplicada.
          nullable: true
        city:
          type: string
          description: Cidade aplicada.
          nullable: true
        nsfw:
          type: boolean
          description: Se o conteúdo adulto foi incluído.
        playable:
          type: boolean
          description: Se só canal com stream utilizável entrou.
        sort:
          type: string
          description: 'Ordem aplicada: `name`, `score` (saúde medida por terceiro) ou `votes` (rádio).'
        online:
          type: boolean
          description: Se só canal visto online pela fonte nas 48 h anteriores à última recarga entrou.
        kind:
          type: string
          description: '`tv`, `radio` ou `all` — sem `kind` na chamada, é `tv`.'
        tag:
          type: string
          description: Tag de rádio aplicada.
          nullable: true
      required:
      - q
      - country
      - category
      - language
      - network
      - quality
      - guide
      - subdivision
      - city
      - nsfw
      - playable
      - sort
      - online
      - kind
      - tag
      description: O que o servidor entendeu do que você mandou — útil para saber por que um filtro não pegou.
    RegiaoSaude:
      type: object
      properties:
        country:
          type: string
          description: País, alpha-2; `ZZ` quando a borda não disse.
        plays:
          type: integer
          description: Relatos de sucesso neste país.
        fails:
          type: integer
          description: Relatos de falha neste país.
        health:
          type: integer
          description: Percentual de sucesso aqui; `null` sem relato bastante.
          nullable: true
        latency_ms:
          type: integer
          description: Latência média da playlist medida na borda deste país; só em `your_country`.
          nullable: true
        latency_grade:
          type: string
          description: 'Nota da latência: `otima`, `boa`, `lenta` ou `ruim`; só em `your_country`.'
          nullable: true
      required:
      - country
      - plays
      - fails
      - health
      - latency_ms
      - latency_grade
      description: O comportamento do canal num país — sem quebrar por navegador/SO.
    GuiaAgora:
      type: object
      properties:
        day:
          type: string
          description: Dia grabado, YYYY-MM-DD (UTC do grabber).
        site:
          type: string
          description: Site de programação de onde a grade veio (ex. `mi.tv`).
          nullable: true
        agora:
          allOf:
          - $ref: '#/components/schemas/Programa'
          description: O programa no ar neste instante; `null` fora da grade.
          nullable: true
        a_seguir:
          allOf:
          - $ref: '#/components/schemas/Programa'
          description: O próximo programa; `null` no fim da grade.
          nullable: true
      required:
      - day
      - site
      - agora
      - a_seguir
      description: 'Resumo da guia do dia que a ficha carrega: o programa de agora e o próximo.'
    Stream:
      type: object
      properties:
        id:
          type: string
          description: ID do stream; é o `:id` de `GET /api/s/:id`.
        feed:
          type: string
          description: Qual feed do canal este stream serve.
          nullable: true
        title:
          type: string
          description: Título do stream, quando a fonte declara.
          nullable: true
        url:
          type: string
          description: URL de reprodução no nosso hop — conta o play (uma vez por pessoa, canal e dia) e devolve a playlist.
        quality:
          type: string
          description: Qualidade declarada deste stream.
          nullable: true
        needs_headers:
          type: boolean
          description: Se a origem exige Referer/User-Agent — o hop cuida disso.
        label:
          type: string
          description: Rótulo curto para escolher entre streams.
          nullable: true
        scheme:
          type: string
          description: 'Esquema da URL de origem: `http`, `https`, ou `youtube` (transmissão no YouTube, tocável só pelo player do site, nunca pelo M3U).'
        kind:
          type: string
          description: 'Como tocar: `hls`, `dash`, `ts`, `flv`, `audio` (rádio contínua), `youtube` (player oficial embutido) ou `externo` (RTMP/RTSP).'
        youtube:
          allOf:
          - $ref: '#/components/schemas/Youtube'
          description: 'Só em stream do YouTube: ids e as URLs de embed e de assistir.'
        playable_hint:
          type: boolean
          description: Se a última verificação achou este stream utilizável.
        origem:
          type: string
          description: 'Fonte que trouxe este stream: `iptv-org` ou uma das listas creditadas em `/sobre`.'
        health_ext:
          allOf:
          - $ref: '#/components/schemas/SaudeMedidaStream'
          description: A sondagem mais recente deste stream pelo IPTV Nexus; `null` quando não foi medido.
          nullable: true
      required:
      - id
      - feed
      - title
      - url
      - quality
      - needs_headers
      - label
      - scheme
      - kind
      - playable_hint
      - origem
      - health_ext
      description: Uma das transmissões de um canal. A `url` já é o hop nosso, não a origem.
    FacetaCategoria:
      type: object
      properties:
        id:
          type: string
          description: ID da categoria no iptv-org, ex. `news`.
        name:
          type: string
          description: Nome da categoria para exibição.
        count:
          type: integer
          description: Canais desta categoria dentro do filtro atual.
        icon:
          type: string
          description: Nome do ícone usado na interface.
          nullable: true
      required:
      - id
      - name
      - count
      - icon
      description: Categoria com a contagem dentro da busca que acabou de ser feita.
    Radio:
      type: object
      properties:
        tags:
          type: array
          items:
            type: string
          description: Tags da estação, vocabulário livre da fonte.
        votes:
          type: integer
          description: Votos da comunidade do Radio Browser.
        clicks:
          type: integer
          description: Cliques contados pelo Radio Browser.
        codec:
          type: string
          description: Codec do stream (`MP3`, `AAC+`…), quando a fonte sabe.
          nullable: true
        bitrate:
          type: integer
          description: Bitrate em kbps, quando a fonte sabe.
          nullable: true
        geo:
          type: object
          description: '`{ lat, lon }` da estação, quando a fonte tem; senão `null`.'
          nullable: true
      required:
      - tags
      - votes
      - clicks
      - codec
      - bitrate
      - geo
      description: O que só uma estação de rádio tem (Radio Browser). Vem em `Canal.radio` quando `kind` é `radio`.
    GeoCanal:
      type: object
      properties:
        tipo:
          type: string
          description: '`geo` (falha numa região, funciona noutra), `down` (falha em toda região medida), `ok` ou `unknown` (sem relato suficiente).'
        bloqueado_em:
          type: array
          items:
            type: string
          description: Regiões onde só há relato de falha (teto de 8; `ZZ` = país desconhecido).
        funciona_em:
          type: array
          items:
            type: string
          description: Países com pelo menos um sucesso (teto de 8).
        label:
          type: string
          description: O veredito em uma frase; vazio quando não há o que dizer.
      required:
      - tipo
      - bloqueado_em
      - funciona_em
      - label
      description: O que separa 'está bloqueado onde eu moro' de 'saiu do ar para todo mundo'.
    PaginaDeCanais:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Canal'
          description: 'Os canais desta página, na ordem pedida (`sort`): nome, ou melhor saúde medida primeiro.'
        total:
          type: integer
          description: Canais que casam com o filtro, ignorando a paginação.
        limit:
          type: integer
          description: Tamanho de página aplicado (teto de 50).
        offset:
          type: integer
          description: Deslocamento aplicado nesta página.
        next_offset:
          type: integer
          description: Offset da próxima página; `null` quando acabou.
          nullable: true
        facets:
          allOf:
          - $ref: '#/components/schemas/FacetasCanal'
          description: Contagem por categoria DENTRO do filtro atual — serve para montar o menu lateral.
        filters:
          allOf:
          - $ref: '#/components/schemas/FiltrosCanal'
          description: Os filtros como o servidor os entendeu, já normalizados.
      required:
      - items
      - total
      - limit
      - offset
      - next_offset
      - facets
      - filters
      description: A resposta da busca de canais. Não usa o envelope `Pagina<T>` porque troca `api` por `facets` e `filters`.
    Youtube:
      type: object
      properties:
        video:
          type: string
          description: Id do vídeo da live (11 caracteres), quando a fonte deu um vídeo.
        channel:
          type: string
          description: 'Id do canal (`UC…`), quando a fonte deu o canal: o embed abre a live corrente.'
        embed:
          type: string
          description: URL do embed oficial sem cookies (`youtube-nocookie.com`).
        assistir:
          type: string
          description: URL para abrir no YouTube (botão do player e destino do hop).
      required:
      - embed
      - assistir
      description: 'Um stream que é uma live do YouTube: o player embute o oficial; M3U/XSPF não o levam.'
    GuiaDoDia:
      type: object
      properties:
        channel_id:
          type: string
          description: ID do canal no iptv-org.
        day:
          type: string
          description: Dia grabado, YYYY-MM-DD.
        site:
          type: string
          description: Site de programação de origem.
          nullable: true
        agora:
          allOf:
          - $ref: '#/components/schemas/Programa'
          description: O programa no ar neste instante.
          nullable: true
        a_seguir:
          allOf:
          - $ref: '#/components/schemas/Programa'
          description: O próximo programa.
          nullable: true
        programas:
          type: array
          items:
            $ref: '#/components/schemas/Programa'
          description: Todos os programas do dia, em ordem (até 200).
      required:
      - channel_id
      - day
      - site
      - agora
      - a_seguir
      - programas
      description: A guia inteira do dia de um canal, grabada por nós (iptv-org/epg no c3).
    Social:
      type: object
      properties:
        plays:
          type: integer
          description: Relatos de que o canal tocou.
        fails:
          type: integer
          description: Relatos de que o canal falhou.
        favorites:
          type: integer
          description: Quantas pessoas favoritaram — conta pessoas, não cliques.
        comments:
          type: integer
          description: Comentários públicos no canal.
        last_fail_code:
          type: string
          description: Código da falha mais recente relatada.
          nullable: true
        health:
          type: integer
          description: Percentual de sucesso; `null` enquanto houver menos de 3 relatos.
          nullable: true
        your_plays:
          type: integer
          description: Relatos de sucesso no SEU navegador, sistema e país.
        your_fails:
          type: integer
          description: Relatos de falha no seu ambiente — é o que distingue 'fora do ar' de 'bloqueado para você'.
        your_fail_code:
          type: string
          description: Código da última falha no seu ambiente.
          nullable: true
        your_country_ok:
          type: integer
          description: Relatos de sucesso no SEU país, sem quebrar por navegador/SO.
        your_country_fail:
          type: integer
          description: Relatos de falha no seu país.
        your_geo_ok:
          type: boolean
          description: '`false` = todas as tentativas relatadas no seu país falharam (suspeita de geo-bloqueio); `null` sem relato suficiente.'
          nullable: true
        your_latency_ms:
          type: integer
          description: Latência média da playlist medida no hop, na borda do seu país; `null` sem medição.
          nullable: true
        your_latency_grade:
          type: string
          description: '`otima`, `boa`, `lenta` ou `ruim`; `null` sem medição.'
          nullable: true
      required:
      - plays
      - fails
      - favorites
      - comments
      - last_fail_code
      - health
      - your_plays
      - your_fails
      - your_fail_code
      - your_country_ok
      - your_country_fail
      - your_geo_ok
      - your_latency_ms
      - your_latency_grade
      description: Contadores da comunidade sobre um canal, incluindo o recorte do ambiente e do país de quem pediu.
    LinksSaude:
      type: object
      properties:
        self:
          type: string
          description: Este mesmo painel.
        channel:
          type: string
          description: Ficha do canal.
        report:
          type: string
          description: Onde mandar um relato novo.
      required:
      - self
      - channel
      - report
      description: Endereços relacionados ao painel de saúde.
    FacetasCanal:
      type: object
      properties:
        categories:
          type: array
          items:
            $ref: '#/components/schemas/FacetaCategoria'
          description: Categorias presentes no resultado, com a contagem de cada uma.
      required:
      - categories
      description: Recortes da busca atual. Hoje só categoria; o formato aceita mais sem quebrar cliente.
    MotivoFalha:
      type: object
      properties:
        code:
          type: string
          description: 'Código: `cors`, `geo`, `sumiu`, `codec`, `playlist`, `sem_resposta`, `protocolo`, `sem_stream`, `outro`.'
        label:
          type: string
          description: O motivo em uma frase curta.
        hint:
          type: string
          description: O que a pessoa pode fazer a respeito.
        count:
          type: integer
          description: Quantos relatos trouxeram este código.
        last_at:
          type: string
          description: Relato mais recente com este código (UTC).
          nullable: true
      required:
      - code
      - label
      - hint
      - count
      - last_at
      description: Um motivo de falha agregado, já com o texto que a interface mostra.
    LatenciaPais:
      type: object
      properties:
        country:
          type: string
          description: País onde a medição aconteceu, alpha-2.
        samples:
          type: integer
          description: Quantas aberturas entraram na média.
        avg_ms:
          type: integer
          description: Te

# --- truncated at 32 KB (52 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/gradetv/refs/heads/main/openapi/gradetv-channels-api-openapi.yml