API REST do Lexbase
A API REST do Lexbase dá acesso directo ao motor de pesquisa de legislação portuguesa consolidada — o mesmo que serve o MCP, sem camadas pelo meio. Cada resultado traz a citação exacta, o texto integral do artigo e o estado de vigência, incluindo quem revogou o quê e quando. Esta página documenta a autenticação, a pesquisa, a leitura de artigos e diplomas e os limites de utilização, com exemplos que pode copiar e correr.
Autenticação
Todos os endpoints exigem uma chave de API — excepto GET /health, que é público. A chave cria-se em lexbase.pt/mcp e começa por lx_. Envia-se no cabeçalho Authorization de todos os pedidos:
curl -s "https://api.lexbase.pt/themes" \ -H "Authorization: Bearer lx_A_SUA_CHAVE"
A base da API é https://api.lexbase.pt.
Sem chave válida, a resposta é 401 com um corpo JSON que diz exactamente o que falhou:
{"error": "invalid_token", "error_description": "unknown or invalid API key"}As descrições possíveis: no bearer token presented (não enviou o cabeçalho), malformed API key, unknown or invalid API key, API key expired e API key revoked. Um 401 não se resolve com retries — corrija a chave.
GET /health devolve o estado do serviço sem autenticação: índice carregado, dimensão do corpus e o tier de qualidade activo (rerank ou hybrid).
Pesquisar legislação
GET /search recebe uma pergunta em linguagem natural ou termos de pesquisa e devolve os fragmentos (artigos) mais relevantes do corpus:
curl -sG "https://api.lexbase.pt/search" \ --data-urlencode "q=cessação do contrato de arrendamento" \ -d "k=3" \ -H "Authorization: Bearer lx_A_SUA_CHAVE"
Parâmetros:
| Parâmetro | Por omissão | Descrição |
|---|---|---|
q | obrigatório | A pergunta ou os termos de pesquisa. |
k | 8 | Número de resultados. |
tipo | — | Filtra por tipo de diploma (o valor que vem em diploma.tipo nos resultados). |
tema_tag_id | — | Filtra por tema; os ids obtêm-se em GET /themes. |
in_force_only | true | Só lei vigente. Com false, inclui lei revogada — sempre marcada como tal. |
mode | rerank | O modo de maior qualidade. Aceita também hybrid, dense, sparse e bm25. |
A resposta é {"query": …, "total": N, "hits": […]}. Cada hit tem esta forma (encurtada):
{
"fragment_id": "1138222475",
"score": 0.97312,
"citation": "«designação do diploma», Artigo …º «epígrafe»",
"nome": "Artigo …º",
"epigrafe": "…",
"breadcrumb": ["…", "…"],
"texto": "texto integral e consolidado do artigo…",
"highlight_span": {"start": 132, "end": 508},
"term_spans": [{"start": 140, "end": 149}],
"diploma": {"diploma_id": "34509075", "designacao": "…", "tipo": "…",
"emissor": "…", "eli": "…", "fonte_url": "…"},
"in_force": {"data_entrada_vigor": "…", "vigente": true, "note": null},
"amendments": [],
"temas": []
}Os campos que interessam:
- `citation` — a citação pronta a usar: designação do diploma, artigo e epígrafe. É este o texto a citar num parecer ou numa peça.
- `texto` — o texto integral consolidado do artigo, não um excerto.
- `highlight_span` — o intervalo de caracteres dentro de
textoque correspondeu à pesquisa. Serve para destacar o trecho relevante sem voltar a processar o texto. - `term_spans` — as posições dos termos da pesquisa dentro desse trecho (no máximo 50), para sublinhar termo a termo.
- `breadcrumb` — o caminho estrutural até ao artigo (livro, título, capítulo, …).
- `in_force` — o estado de vigência. Leia a secção seguinte antes de citar seja o que for.
- `amendments` — as alterações registadas sobre o artigo.
- `diploma.eli` e `diploma.fonte_url` — o identificador ELI e a ligação à fonte oficial.
Cada hit inclui ainda chunk_id, score_components e diploma.diploma_legis_id, úteis para depuração e cruzamento de dados.
Vigência: vigente e revogado_por
Citar lei revogada é o erro caro desta área, e o contrato da API existe para o tornar difícil. Toda a resposta que contém texto de lei — um hit de pesquisa, um /fragment, um /diploma?render=full — traz um objecto in_force:
- `data_entrada_vigor` — quando a disposição entrou em vigor. Nos hits de pesquisa e no
/fragment, quando vigora desde a versão original do diploma,notediz"desde a versão original"(os nós de/diploma?render=fullnão incluem o camponote). - `vigente` —
trueoufalse. Éfalsese a disposição foi suspensa ou revogada, quer o diploma inteiro quer o artigo isolado. - Quando revogada, aparecem `revogado_por` (o diploma revogador) e `data_revogacao`:
"in_force": {
"data_entrada_vigor": "…",
"vigente": false,
"revogado_por": "…",
"data_revogacao": "…"
}A pesquisa exclui lei revogada por omissão (in_force_only=true). A lei revogada não é apagada do corpus — para litigar factos de 2015 é precisa a lei de 2015 — por isso in_force_only=false devolve-a de propósito, sempre marcada. A regra prática: antes de citar, verifique in_force.vigente; se for false, revogado_por diz quem a matou e data_revogacao diz quando.
Ler um artigo ou um diploma
`GET /fragment/{fragment_id}` é a leitura canónica de um artigo — a partir do fragment_id de qualquer hit:
curl -s "https://api.lexbase.pt/fragment/1138222475" \ -H "Authorization: Bearer lx_A_SUA_CHAVE"
Devolve fragment_id, diploma_id, citation, breadcrumb, nome, epigrafe, texto, in_force e temas. Com ?context=1 acrescenta context com parent, prev e next — o artigo anterior e o seguinte na estrutura do diploma.
`GET /fragment/{fragment_id}/amendments` devolve o histórico de alterações do artigo: {"target": {"fragment_id", "diploma_id", "nome"}, "amendments": […]}.
`GET /diploma/{diploma_id}` lê o diploma inteiro, com três níveis de detalhe via render:
render=meta(por omissão) — o cabeçalho: designação, sumário, emissor, ELI, datas de publicação e de consolidação, temas, número de fragmentos e de alterações.render=tree— acrescentanodes: a estrutura completa do diploma (fragment_id,nome,epigrafe,depth,has_amendmentspor nó), sem texto. É o modo certo para navegar.render=full— comotree, mas cada nó traz tambémtextoein_force. Atenção: é o documento inteiro — o Código Civil ronda os 4 MB.
Com ?focus=<fragment_id> em render=tree ou render=full, a resposta inclui path_to_focus: o caminho de fragment_ids da raiz até esse artigo. Com o render=meta por omissão, o focus é ignorado.
curl -s "https://api.lexbase.pt/diploma/34509075?render=tree" \ -H "Authorization: Bearer lx_A_SUA_CHAVE"
Um id desconhecido devolve 404 com {"detail": "diploma not found"} ou {"detail": "fragment not found"}.
`GET /themes` lista os temas do corpus — {"themes": [{"tag_id", "titulo", "act_count"}]} — e os tag_id servem de filtro tema_tag_id na pesquisa.
total: 0 significa «não temos»
Quando a pesquisa não encontra apoio no corpus, a resposta é HTTP 200 com total: 0, hits: [] e um campo note a explicar porquê. Não é um erro — é a resposta correcta:
{"query": "…", "total": 0, "hits": [], "note": "no matching legislation found"}Há dois casos principais:
- `no matching legislation found` — nada no corpus sustenta a pergunta.
- `out of scope: …` — a pergunta é sobre lei estrangeira. O corpus cobre exclusivamente legislação portuguesa, e uma pergunta sobre «lei brasileira» é recusada antes da pesquisa: um artigo português topicamente parecido seria a jurisdição errada, por muito bem que ficasse classificado.
(Um q vazio devolve igualmente total: 0, com note: "empty query".)
O que fazer com total: 0: tratá-lo como informação. Não repita a pesquisa com termos cada vez mais vagos à espera de um resultado qualquer e, se está a alimentar um LLM, não deixe o modelo preencher o vazio com memória. A abstenção honesta é preferível a uma citação inventada — é para isso que ela existe.
Limites de utilização
Cada pedido conta como uma pesquisa do seu saldo: pesquisar (GET /search), ler um artigo (GET /fragment) ou um diploma (GET /diploma, em qualquer render), o histórico de alterações e a lista de temas contam todos igual. Só GET /health não conta.
A conta Lexbase gratuita inclui:
| Limite | Conta gratuita |
|---|---|
| Pesquisas por mês | 200 |
| Pedidos por minuto | 20 |
| Chaves | 2 |
As pesquisas no navegador e os pedidos por chave saem do mesmo saldo mensal de 200 pesquisas.
Ao exceder um limite, a resposta é 429 com o cabeçalho Retry-After:
- Ritmo (por minuto) —
429comRetry-After: 60. Espere e retome. - Quota mensal —
429comerror: "quota_exceeded"; os números estão na descrição:
{
"error": "quota_exceeded",
"error_description": "monthly quota exceeded (200/200 pesquisas)"
}O Retry-After da quota aponta para a viragem do mês (00:00 UTC do dia 1). Um pedido recusado com 429 não consome pesquisas — a recusa devolve o que tinha debitado e conta apenas como pedido nas estatísticas. Retries em ciclo não inflacionam o saldo, mas também não servem ninguém: respeite o Retry-After.