curl --request GET \
--url https://api.novelai.com.br/v1/subscriber/{id}/reading-progress \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.novelai.com.br/v1/subscriber/{id}/reading-progress"
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/subscriber/{id}/reading-progress', 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/subscriber/{id}/reading-progress",
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/subscriber/{id}/reading-progress"
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/subscriber/{id}/reading-progress")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.novelai.com.br/v1/subscriber/{id}/reading-progress")
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{
"subscriber_id": "<string>",
"active_novel_count": 123,
"active_novel_limit": 3,
"novels": [
{
"novel_id": "<string>",
"title": "<string>",
"started_at": "2023-11-07T05:31:56Z",
"current_chapter": 123,
"unlocked_chapters": 123,
"total_chapters": 123,
"finished_at": "2023-11-07T05:31:56Z",
"chosen_choice_id": "<string>"
}
]
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}{
"error": "invalid_request",
"error_description": "Novela inexistente ou removida do catálogo"
}Progresso de leitura consolidado de um assinante
Uma linha por novela que o assinante já abriu — em andamento ou concluída. Nomeado no singular (subscriber, não subscribers) para seguir a mesma convenção de GET /subscriber-status/{id} na Platform API.
active_novel_count nunca passa de active_novel_limit (hoje fixo em 3): é o limite de novelas simultâneas não finalizadas, aplicado no servidor por enforce_reading_rules() — o assinante precisa terminar (ou remover) uma novela em andamento antes de abrir uma quarta. Um parceiro que queira, por exemplo, avisar “sua base já está no limite” numa tela própria tem esse dado aqui sem precisar hardcodar o número 3.
curl --request GET \
--url https://api.novelai.com.br/v1/subscriber/{id}/reading-progress \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.novelai.com.br/v1/subscriber/{id}/reading-progress"
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/subscriber/{id}/reading-progress', 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/subscriber/{id}/reading-progress",
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/subscriber/{id}/reading-progress"
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/subscriber/{id}/reading-progress")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.novelai.com.br/v1/subscriber/{id}/reading-progress")
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{
"subscriber_id": "<string>",
"active_novel_count": 123,
"active_novel_limit": 3,
"novels": [
{
"novel_id": "<string>",
"title": "<string>",
"started_at": "2023-11-07T05:31:56Z",
"current_chapter": 123,
"unlocked_chapters": 123,
"total_chapters": 123,
"finished_at": "2023-11-07T05:31:56Z",
"chosen_choice_id": "<string>"
}
]
}{
"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.
Path Parameters
Identificador do assinante no sistema do parceiro.
Response
Progresso consolidado.
Novelas em andamento (finished_at nulo) agora.
Limite de novelas simultâneas não finalizadas — hoje fixo em 3, aplicado no servidor.
3
Toda novela que o assinante já abriu, em andamento ou concluída.
Show child attributes
Show child attributes