curl --request GET \
--url https://api.novelai.com.br/v1/analytics/engagement \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.novelai.com.br/v1/analytics/engagement"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.novelai.com.br/v1/analytics/engagement', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.novelai.com.br/v1/analytics/engagement",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.novelai.com.br/v1/analytics/engagement"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.novelai.com.br/v1/analytics/engagement")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.novelai.com.br/v1/analytics/engagement")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"period_start": "2023-12-25",
"period_end": "2023-12-25",
"active_readers": 123,
"avg_chapters_read_per_active_reader": 123,
"completion_rate": 123,
"ending_distribution": [
{
"novel_id": "<string>",
"choice_id": "<string>",
"label": "<string>",
"count": 123
}
],
"minimum_cohort_size": 20,
"generated_at": "2023-11-07T05:31:56Z"
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}Métricas agregadas de engajamento de leitura, por parceiro e período
Suporte aos indicadores prometidos na proposta (DAU/MAU do ritual diário, take-rate, retenção) — sempre agregado ao nível do parceiro, nunca por assinante. Nenhuma resposta desta rota inclui subscriber_id em nenhum nível; ver Privacidade & LGPD para o racional.
Pré-requisito ainda não desenhado em outra página: para calcular qualquer coisa “por parceiro”, as tabelas de leitura (reading_progress e afins) precisam de uma coluna partner_id — hoje elas não distinguem um leitor que entrou pelo app oficial de um que entrou via SSO de um parceiro. Isso é mencionado como necessidade genérica em Isolamento multi-tenant, mas o backfill/migração real dessa coluna não está feito nem desenhado em detalhe — este endpoint está, portanto, mais atrás no roadmap do que os demais desta especificação, que só precisam do subscriber_id já resolvido pela Platform API.
ending_distribution omite qualquer combinação novela/final com menos de minimum_cohort_size leitores no período — supressão de cohort pequeno para reduzir risco de reidentificação, não um limite arbitrário de exibição.
curl --request GET \
--url https://api.novelai.com.br/v1/analytics/engagement \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.novelai.com.br/v1/analytics/engagement"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.novelai.com.br/v1/analytics/engagement', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.novelai.com.br/v1/analytics/engagement",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.novelai.com.br/v1/analytics/engagement"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.novelai.com.br/v1/analytics/engagement")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.novelai.com.br/v1/analytics/engagement")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"period_start": "2023-12-25",
"period_end": "2023-12-25",
"active_readers": 123,
"avg_chapters_read_per_active_reader": 123,
"completion_rate": 123,
"ending_distribution": [
{
"novel_id": "<string>",
"choice_id": "<string>",
"label": "<string>",
"count": 123
}
],
"minimum_cohort_size": 20,
"generated_at": "2023-11-07T05:31:56Z"
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}Authorizations
Client credentials — emitido uma única vez, de forma centralizada, pela Platform API (POST https://api.googa.com.br/v1/oauth2/token, ver aba API Platform). Este domínio (api.novelai.com.br) só valida o token — via a chave pública do projeto Supabase "Platform" — nunca emite um token próprio. Isso evita duplicar a verificação de client_id/client_secret em três projetos Supabase diferentes: o parceiro autentica uma vez, e o mesmo token (com os escopos certos) funciona em qualquer domínio de produto. Ver "Roteamento cross-projeto" em Modelo de autenticação sobre Supabase.
Response
Relatório agregado do período.
Assinantes distintos deste parceiro com alguma atividade de leitura no período.
Proporção de novelas iniciadas no período que chegaram a um final escolhido.
Omite combinações com menos de minimum_cohort_size leitores.
Show child attributes
Show child attributes
20