Leitura para redes de escolas
API somente leitura para a rede de escolas puxar, de forma programática, quem são os usuários da rede, em que dias eles acessaram a plataforma e o que leram. Tempo e porcentagem de leitura seguem a mesma régua de leitura efetiva do painel da escola, então os números batem com o que a escola vê lá.
São três rotas, organizadas pelo grão do dado:
| Rota | Grão | Entrega |
|---|---|---|
GET /schools/v1/users | 1 linha por usuário | cadastro de alunos e professores |
GET /schools/v1/engagements | dia × usuário | dias com acesso e minutos de leitura efetiva |
GET /schools/v1/readings | usuário × livro | porcentagem lida, minutos e conclusão por livro |
Acesso
| Item | Valor |
|---|---|
| Host | https://livros.arvore.com.br |
| Base path | /api-arvore/schools/v1 |
| Autenticação | Header X-Api-Key: <chave> (alternativa: Authorization: Bearer <chave>). Chave emitida pela Árvore, exclusiva da rede e desta API. Sem chave válida: 401 |
| Tenant | A chave define a rede. A API nunca aceita identificador de rede do cliente e não devolve dado de outra rede |
| Formato | JSON UTF-8, somente GET |
| Rate limit | 30 requisições por minuto por chave; excedente recebe 429 com header Retry-After |
Exemplo:
curl -s \
-H 'X-Api-Key: <chave>' \
'https://livros.arvore.com.br/api-arvore/schools/v1/readings?startDate=2026-09-01&endDate=2026-09-30&pageSize=1000'
Convenções e tipos
| Tipo | Formato | Exemplo |
|---|---|---|
int | inteiro não-negativo | 1609673 |
string | texto UTF-8 | "Nos porões da Ditadura" |
bool | true / false | true |
date | YYYY-MM-DD, fuso de Brasília (UTC−3) | "2026-09-02" |
datetime | ISO 8601 em UTC | "2026-09-02T18:38:17Z" |
minutes | minutos com uma casa decimal | 25.5 |
percent | percentual de 0 a 100, com duas casas | 56.93 |
enum | um valor do conjunto listado no campo | "student" |
- Tempo de leitura é sempre em minutos, com uma casa decimal.
- Campos sem valor vêm como
null, nunca omitidos. - O histórico começa em 2026-01-01. Um
startDateanterior vira2026-01-01; um período que termina antes disso responde400. startDateeendDatesão inclusivos, emYYYY-MM-DD, e precisam existir no calendário (2026-02-30responde400). Quando omitidos, valem os 30 dias que terminam noendDate(ou hoje). UmendDateno futuro vira hoje.startDatedepois deendDateresponde400.userIdé o identificador do usuário na Árvore e é a chave que liga as três rotas.
Paginação
page começa em 1 (default 1) e vai até 100000. pageSize tem default 100 e máximo 1000 em todas as rotas; acima disso responde 400. O total de linhas vem em meta.page.totalRows. A ordenação é fixa em cada rota, então paginar em sequência não perde nem repete linha.
Cliente HTTP
O host fica atrás de um WAF que barra requisições com o User-Agent padrão do urllib do Python (Python-urllib/3.x), respondendo 403 antes de a chamada chegar à API. Defina um User-Agent próprio na sua integração, por exemplo Escola-Ingest/1.0. Clientes comuns (requests, axios, HttpClient do Java, Go) passam sem ajuste.
Atualização dos dados
Os dados vêm do lakehouse da Árvore, sincronizados ao longo do dia. Não são tempo real. Cada chamada leva alguns segundos (de 2 a 6 s numa rede de 10 mil usuários com o ano inteiro). Uma carga por dia costuma bastar.
Envelope de resposta
Toda rota responde neste formato. data.rows é o array de linhas.
{
"meta": {
"period": { "startDate": "2026-09-01", "endDate": "2026-09-30" },
"page": { "page": 1, "pageSize": 100, "totalRows": 9317 }
},
"data": { "rows": [] }
}
| Campo | Tipo | Descrição |
|---|---|---|
meta.period.startDate | date | Início efetivo do período consultado (após defaults e ajustes) |
meta.period.endDate | date | Fim efetivo do período |
meta.page.* | int | page, pageSize, totalRows |
data.rows | array | Linhas da rota |
meta.period aparece só em /engagements e /readings.
Leitura efetiva
Tempo e porcentagem de leitura usam a régua do painel da escola. A leitura chega em trechos, e um trecho conta como leitura efetiva quando tem de 5 a 800 palavras por minuto, ou quando não tem velocidade registrada e dura pelo menos 3 segundos. Trechos fora disso (página virada rápido demais, por exemplo) não entram no tempo nem na porcentagem.
A porcentagem de um livro é quanto dele foi coberto pelos trechos efetivos do período, somando os intervalos lidos sem contar duas vezes o que foi relido. Um livro é concluído quando essa porcentagem chega a 50%.
GET /schools/v1/users
Cadastro dos usuários da rede, alunos e professores. É o único lugar da API com dado nominal; as demais rotas referenciam o usuário por userId. Contas internas da Árvore e administradores ficam de fora. Ordenação fixa userId ASC.
Parâmetros
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
userId | int | não | Um usuário específico |
schoolId | int | não | Filtra por unidade |
profile | enum | não | student | teacher. Default: os dois |
page / pageSize | int | não | Paginação (máximo 1000) |
data.rows[]
| Campo | Tipo | Descrição |
|---|---|---|
userId | int | Identificador do usuário na Árvore |
name | string | null | Nome |
email | string | null | |
profile | enum | student aluno · teacher professor |
school | object | null | Unidade: { id: int, name: string | null } |
class | object | null | Turma: { id: int, name: string | null }; null para quem não está enturmado, como a maioria dos professores |
grade | int | null | Ano escolar (1 a 9); null quando a turma não tem ano, como no ensino médio sem série ou na educação infantil |
stage | string | null | Etapa: INFANTIL, FUNDAMENTAL 1, FUNDAMENTAL 2 ou MEDIO |
active | bool | Se a conta está ativa |
A unidade e a turma são as atuais do usuário. Cadastros de menores de idade estão na base: quem leva esses dados a outro sistema responde pelo tratamento no destino.
GET /schools/v1/engagements
Uma linha por usuário por dia em que ele acessou a plataforma. Conta como acesso o login, uma sessão aberta ou qualquer atividade no dia (leitura, audiolivro, atividade, avaliação de fluência). Receber uma indicação ou uma atividade sem abrir não conta. Dia sem acesso não gera linha. Ordenação fixa date ASC, userId ASC.
Parâmetros
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
startDate / endDate | date | não | Período (default: 30 dias) |
userId | int | não | Um usuário específico |
schoolId | int | não | Filtra por unidade |
profile | enum | não | student | teacher |
page / pageSize | int | não | Paginação (máximo 1000) |
data.rows[]
| Campo | Tipo | Descrição |
|---|---|---|
date | date | Dia do acesso |
userId | int | Usuário |
readingMinutes | minutes | Minutos de leitura efetiva no dia; 0 quando o usuário acessou sem ler |
A soma de readingMinutes no período é igual à soma do /readings no mesmo período e filtro.
GET /schools/v1/readings
Uma linha por usuário e livro com leitura efetiva no período. Ordenação fixa userId ASC, book.id ASC.
Parâmetros
| Param | Tipo | Obrigatório | Descrição |
|---|---|---|---|
startDate / endDate | date | não | Período (default: 30 dias) |
userId | int | não | Um usuário específico |
schoolId | int | não | Filtra por unidade |
profile | enum | não | student | teacher |
page / pageSize | int | não | Paginação (máximo 1000) |
data.rows[]
| Campo | Tipo | Descrição |
|---|---|---|
userId | int | Usuário |
book | object | { id: int, title: string | null, author: string | null, isbn: string | null } |
percent | percent | Quanto do livro foi lido de forma efetiva no período |
readingMinutes | minutes | Minutos de leitura efetiva do livro no período |
firstReadAt | datetime | Primeiro trecho efetivo no período |
lastReadAt | datetime | Último trecho efetivo no período |
finished | bool | true quando percent chega a 50% |
percent e finished são do período consultado: um livro lido 30% em setembro e mais 30% em outubro aparece com cerca de 30% em cada mês e com cerca de 60% (concluído) numa consulta de setembro a outubro.
Montar a planilha anual
Para o consolidado aluno × livro do ano, percorra GET /schools/v1/readings?startDate=2026-01-01&endDate=2026-12-31&pageSize=1000&page=N em sequência até meta.page.totalRows, e junte com /users pelo userId para trazer nome, unidade, turma e ano escolar.
Erros
| Status | Quando |
|---|---|
400 | Parâmetro inválido: data fora do formato, período que termina antes de 2026, startDate depois de endDate, pageSize acima de 1000, profile fora do conjunto |
401 | Sem chave, ou chave inválida |
429 | Mais de 30 requisições no minuto |