{"openapi":"3.1.0","info":{"title":"LulaHub API","version":"1.1.0","description":"Dados do 1º turno de 2026 por cidade e bairro, a prioridade de cada lugar (onde mais gente não escolheu ninguém) e o perfil estimado de quem não votou. Só dados agregados e públicos (TSE e IBGE); nenhum dado de pessoa."},"servers":[{"url":"https://squid.sts.lol/api/v1"}],"security":[{"bearer":[]},{"chave":[]}],"paths":{"/lugares":{"get":{"operationId":"listarLugares","summary":"Lista cidades e bairros","description":"Cidades (todas as 5.571) e bairros (nas cidades com 50 mil eleitores ou mais), com o resultado do 1º turno de 2026 para presidente. Por padrão vem quem tem mais gente sem votar primeiro.","parameters":[{"name":"tipo","in":"query","description":"Só cidades ou só bairros. Sem o parâmetro, vêm os dois.","schema":{"type":"string","enum":["cidade","bairro"]}},{"name":"uf","in":"query","description":"Sigla do estado.","schema":{"type":"string","pattern":"^[A-Za-z]{2}$"},"example":"RJ"},{"name":"cidade","in":"query","description":"Id de uma cidade: traz ela e os bairros dela.","schema":{"type":"string","pattern":"^[A-Z]{2}-\\d+$"},"example":"RJ-58777"},{"name":"prioridade","in":"query","description":"Prioridade calculada pelo LulaHub.","schema":{"type":"string","enum":["alta","media","normal"]}},{"name":"ordem","in":"query","description":"fora = mais gente sem votar (número) primeiro; taxa = maior proporção primeiro; nome = alfabética.","schema":{"type":"string","enum":["fora","taxa","nome"],"default":"fora"}},{"name":"limite","in":"query","description":"Itens por página.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"pagina","in":"query","description":"Página, a partir de 1.","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"Página de lugares.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListaLugares"},"example":{"total":275,"pagina":1,"por_pagina":1,"itens":[{"id":"RJ-60011|CAMPO GRANDE","tipo":"bairro","nome":"Campo Grande","cidade_id":"RJ-60011","municipio":"Rio de Janeiro","uf":"RJ","aptos":298699,"abstencoes":71940,"brancos":4617,"nulos":7909,"fora":84466,"taxa_fora":28.3,"prioridade":"alta","lat":-22.897727,"lng":-43.563765,"tem_perfil":true}]}}}},"400":{"description":"Parâmetro inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"parametro_invalido","mensagem":"uf deve ser a sigla de um estado (ex.: RJ)."}}}}},"401":{"description":"Sem chave, ou chave inválida ou revogada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"chave_invalida","mensagem":"Chave de API inválida ou revogada."}}}}},"429":{"description":"Passou do limite de pedidos por minuto da chave. Veja o cabeçalho Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"limite","mensagem":"Limite de 120 pedidos por minuto. Tente de novo no próximo minuto."}}}}}}}},"/lugares/{id}":{"get":{"operationId":"obterLugar","summary":"Um lugar, com o perfil de quem não votou","description":"Resultado do lugar e o perfil estimado de quem não votou (idade, gênero, escolaridade, cor ou raça, religião e locais de votação). Id de bairro tem \"|\": codifique como %7C.","parameters":[{"name":"id","in":"path","required":true,"description":"Id da cidade (RJ-58777) ou do bairro (RJ-58777|MOSELA, na URL RJ-58777%7CMOSELA).","schema":{"type":"string"},"example":"RJ-58777|MOSELA"}],"responses":{"200":{"description":"O lugar. perfil vem null quando ainda não foi calculado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LugarComPerfil"}}}},"400":{"description":"Id em formato inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"parametro_invalido","mensagem":"id deve ser de cidade (RJ-58777) ou bairro (RJ-58777|MOSELA)."}}}}},"401":{"description":"Sem chave, ou chave inválida ou revogada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"chave_invalida","mensagem":"Chave de API inválida ou revogada."}}}}},"404":{"description":"Lugar não existe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"nao_encontrado","mensagem":"Lugar não encontrado."}}}}},"429":{"description":"Passou do limite de pedidos por minuto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Erro"},"example":{"erro":{"codigo":"limite","mensagem":"Limite de 120 pedidos por minuto. Tente de novo no próximo minuto."}}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Authorization: Bearer lh_..."},"chave":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Erro":{"type":"object","required":["erro"],"properties":{"erro":{"type":"object","required":["codigo","mensagem"],"properties":{"codigo":{"type":"string"},"mensagem":{"type":"string"}}}}},"Lugar":{"type":"object","required":["id","tipo","nome","cidade_id","municipio","uf","aptos","abstencoes","brancos","nulos","fora","prioridade"],"properties":{"id":{"type":"string","description":"Cidade: UF-código TSE. Bairro: id da cidade + \"|\" + nome normalizado."},"tipo":{"type":"string","enum":["cidade","bairro"]},"nome":{"type":"string"},"cidade_id":{"type":"string"},"municipio":{"type":"string"},"uf":{"type":"string"},"aptos":{"type":"integer","description":"Eleitores aptos no 1º turno de 2026."},"abstencoes":{"type":"integer","description":"Não foram votar."},"brancos":{"type":"integer"},"nulos":{"type":"integer"},"fora":{"type":"integer","description":"Não escolheram ninguém: abstenções + brancos + nulos."},"taxa_fora":{"type":"number","description":"fora / aptos, em %."},"prioridade":{"type":"string","enum":["alta","media","normal"],"description":"alta = volume de \"fora\" entre os 5% maiores, ou proporção entre as 25% maiores com volume acima da mediana."},"lat":{"type":["number","null"],"description":"Centro dos locais de votação (só bairros)."},"lng":{"type":["number","null"]},"tem_perfil":{"type":"boolean","description":"Se já existe perfil calculado (só na lista)."}}},"ListaLugares":{"type":"object","required":["total","pagina","por_pagina","itens"],"properties":{"total":{"type":"integer"},"pagina":{"type":"integer"},"por_pagina":{"type":"integer"},"itens":{"type":"array","items":{"$ref":"#/components/schemas/Lugar"}}}},"LugarComPerfil":{"allOf":[{"$ref":"#/components/schemas/Lugar"},{"type":"object","properties":{"perfil":{"oneOf":[{"$ref":"#/components/schemas/Perfil"},{"type":"null"}]},"perfil_atualizado_em":{"type":["string","null"],"format":"date-time"}}}]},"Grupo":{"type":"object","required":["nome","eleitores","faltou"],"properties":{"nome":{"type":"string"},"eleitores":{"type":"integer","description":"Eleitores do grupo no lugar (TSE)."},"faltou":{"type":"integer","description":"Estimativa de quantos do grupo não foram votar."}}},"Fatia":{"type":"object","required":["nome","faltou"],"properties":{"nome":{"type":"string"},"faltou":{"type":"integer","description":"Estimativa de quantos de quem faltou."}}},"LocalVotacao":{"type":"object","properties":{"nome":{"type":"string"},"endereco":{"type":"string"},"lat":{"type":["number","null"]},"lon":{"type":["number","null"]},"eleitores":{"type":"integer"},"abstencoes":{"type":"integer","description":"Real (TSE)."},"pct70":{"type":"number","description":"% dos eleitores do local com 70 anos ou mais (0 a 100)."},"faltou70":{"type":"integer","description":"Estimativa de quantos com 70+ não foram votar."}}},"Perfil":{"type":"object","description":"Estimativa para grupos, nunca sobre pessoas. Veja o método em /docs/api.","properties":{"v":{"const":1,"description":"Versão do formato."},"aptos":{"type":"integer"},"abstencoes":{"type":"integer"},"brancos":{"type":"integer"},"nulos":{"type":"integer"},"eleitores":{"type":"integer","description":"Eleitores no perfil por seção do TSE (pode diferir um pouco de aptos)."},"previsto":{"type":"integer","description":"Abstenções que as taxas de 2022 previam, antes do ajuste ao real de 2026."},"idade":{"type":"array","items":{"$ref":"#/components/schemas/Grupo"},"description":"Faixas: 16-17, 18-24, 25-34, 35-44, 45-59, 60-69, 70+ (16-17 e 70+ votam por opção)."},"genero":{"type":"array","items":{"$ref":"#/components/schemas/Grupo"},"description":"mulheres, homens, não informado."},"escolaridade":{"type":"array","items":{"$ref":"#/components/schemas/Grupo"},"description":"sem estudo formal, fundamental, médio incompleto, médio completo, superior, não informado."},"grupos":{"type":"array","description":"Os 6 maiores grupos gênero · idade · escolaridade entre quem faltou.","items":{"type":"object","properties":{"nome":{"type":"string"},"faltou":{"type":"integer"},"taxa":{"type":"number","description":"% do grupo que faltou."}}}},"raca":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Fatia"}},{"type":"null"}],"description":"Cor ou raça de quem faltou (Censo 2022 da vizinhança, por faixa etária). null quando a vizinhança não bate com o eleitorado (veja censo.razao)."},"religiao":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Fatia"}},{"type":"null"}],"description":"Religião de quem faltou (Censo 2022 do município, por sexo e idade)."},"locais":{"type":"array","items":{"$ref":"#/components/schemas/LocalVotacao"},"description":"Locais de votação (só em bairros)."},"censo":{"type":["object","null"],"description":"Moradores da vizinhança (setores do Censo 2022 mais próximos dos locais de votação do lugar).","properties":{"setores":{"type":"integer"},"moradores":{"type":"integer"},"pct70":{"type":"number","description":"% de moradores com 70+ (0 a 100)."},"razao":{"type":"number","description":"eleitores / moradores da vizinhança. Normal: ~0,7–0,8. Fora de 0,3–2,5 a vizinhança não representa o eleitorado, e perfil.raca vem null."},"raca":{"type":"array","items":{"type":"object","properties":{"nome":{"type":"string"},"pct":{"type":"number"}}}}}}}}}}}