Score Jurídico via API: Como Construir um Modelo de Risco com Dados de Processos
O bureau de crédito tradicional enxerga histórico de pagamentos — o score jurídico revela o que está prestes a acontecer. O judiciário revela o que está prestes a acontecer: execuções em curso, ações trabalhistas em andamento, recuperações judiciais que ainda não chegaram ao Serasa. Um modelo de risco que ignora essa camada está tomando decisões com informação incompleta.
A Judit API expõe dados suficientes para construir um score jurídico robusto
Score jurídico via API: do CPF/CNPJ ao modelo de risco em uma pipeline com a Judit
O que é um score jurídico e como calculá-lo via API
Um score jurídico é uma pontuação que resume o perfil de risco processual de uma entidade (CPF ou CNPJ) com base em dados de processos judiciais. Diferente do score de crédito tradicional — que olha para o passado financeiro — o score jurídico olha para o passivo judicial atual e o padrão litigioso histórico.
O score pode ser usado de formas distintas dependendo do contexto:
- Fintech/crédito: como variável adicional no motor de aprovação, sinalizando risco de insolvência não capturado pelo bureau
- RH: como critério de triagem em contratações para cargos sensíveis
- Compliance/KYC: como gatilho para due diligence aprofundada em onboarding de alto risco
- Imobiliário: como verificação prévia de locatários, compradores e fiadores
- M&A: como componente do passivo contingente em due diligence
O modelo que você constrói depende do seu caso de uso — mas os dados disponíveis via Judit são os mesmos. A diferença está nas features que você extrai e nos pesos que atribui a cada uma.
Os três endpoints para construção de features
A Judit API tem três modos de consulta, cada um com um trade-off diferente entre velocidade e profundidade:
| Endpoint | Latência | Profundidade | Ideal para |
|---|---|---|---|
| Hot Storage (síncrono) | Imediata | Capa dos processos + partes | Onboarding, decisões em tempo real |
| Consulta Agrupada (síncrona) | Imediata | Contagens por dimensão | Score rápido, features sintéticas |
| Consulta Assíncrona | Segundos–minutos | Andamentos, fases, documentos | Análise aprofundada, modelos ML |
Para um score de risco operacional, a combinação mais eficiente é começar pela consulta agrupada para o perfil sintético e enriquecer com hot storage filtrado para as features que precisam de detalhe de processo.
Features disponíveis e como extraí-las
Features da consulta agrupada
A consulta agrupada (POST https://lawsuits.production.judit.io/requests/create/grouped) retorna contagens agrupadas que se traduzem diretamente em features:
import httpx
import os
API_KEY = os.environ['JUDIT_API_KEY']
def buscar_perfil_agrupado(documento: str, tipo: str) -> dict:
res = httpx.post(
'https://lawsuits.production.judit.io/requests/create/grouped',
headers={'api-key': API_KEY},
json={'search': {'search_type': tipo, 'search_key': documento}}
)
return res.json()
def extrair_features_agrupadas(perfil: dict) -> dict:
def count(lst, val):
return next((i['count'] for i in lst if i['value'] == val), 0)
total = perfil.get('lawsuits_count', 0)
# Features de polo
como_passivo = count(perfil.get('sides', []), 'PASSIVE')
como_ativo = count(perfil.get('sides', []), 'ACTIVE')
# Features de área do direito
trabalhistas = count(perfil.get('areas', []), 'DIREITO DO TRABALHO')
tributarios = count(perfil.get('areas', []), 'DIREITO TRIBUTÁRIO')
criminais = count(perfil.get('areas', []), 'DIREITO PENAL')
civis = count(perfil.get('areas', []), 'DIREITO CIVIL')
# Features de fase processual
em_andamento = count(perfil.get('phases', []), 'INICIAL')
em_execucao = (
count(perfil.get('phases', []), 'EXECUÇÃO') +
count(perfil.get('phases', []), 'EXECUÇÃO OU CUMPRIMENTO')
)
com_sentenca = count(perfil.get('phases', []), 'SENTENÇA')
transitados = count(perfil.get('phases', []), 'TRÂNSITO EM JULGADO OU ACORDO')
arquivados = count(perfil.get('phases', []), 'ARQUIVADO')
# Razões derivadas (evita divisão por zero)
razao_passivo = como_passivo / total if total > 0 else 0
razao_execucao = em_execucao / total if total > 0 else 0
razao_ativo = em_andamento / total if total > 0 else 0
return {
# Volumetria
'total_processos': total,
'processos_como_passivo': como_passivo,
'processos_como_ativo': como_ativo,
# Distribuição por área
'trabalhistas': trabalhistas,
'tributarios': tributarios,
'criminais': criminais,
'civis': civis,
# Distribuição por fase
'em_andamento': em_andamento,
'em_execucao': em_execucao,
'com_sentenca': com_sentenca,
'transitados': transitados,
'arquivados': arquivados,
# Razões derivadas
'razao_passivo': razao_passivo,
'razao_execucao': razao_execucao,
'razao_ativo_andamento': razao_ativo,
}
Features do hot storage filtrado
O hot storage (POST https://lawsuits.production.judit.io/lawsuits) permite consultas síncronas com filtros precisos. Útil para extrair features que dependem do valor da causa ou de tipo de tribunal específico:
def buscar_execucoes_fiscais(documento: str, tipo: str) -> dict:
"""Busca processos onde a entidade é passiva com valor acima de R$ 10k."""
res = httpx.post(
'https://lawsuits.production.judit.io/lawsuits',
headers={'api-key': API_KEY},
json={
'search': {
'search_type': tipo,
'search_key': documento,
'search_params': {
'filter': {
'side': 'Passive',
'amount_gte': 10000
}
}
},
'process_status': True # inclui phase e status no retorno
}
)
return res.json()
def extrair_features_hotstorage(dados: dict) -> dict:
processos = dados.get('response_data', [])
# Filtra apenas ativos (não arquivados)
ativos = [p for p in processos if p.get('status') == 'Ativo']
# Valor total em execução
valor_total_execucao = sum(
p.get('amount', 0)
for p in ativos
if p.get('phase') in ('Execução', 'Execução ou cumprimento')
)
# Processos trabalhistas ativos como réu
trabalhistas_ativos = [
p for p in ativos
if any(s.get('name', '').upper().startswith('TRABALH')
for s in p.get('subjects', []))
]
# Processos tributários ativos
tributarios_ativos = [
p for p in ativos
if 'TRIBUTÁRIO' in p.get('area', '').upper()
or any('FISCAL' in s.get('name', '').upper()
for s in p.get('subjects', []))
]
return {
'valor_total_passivo_ativo': sum(p.get('amount', 0) for p in ativos),
'valor_total_em_execucao': valor_total_execucao,
'qtd_trabalhistas_ativos': len(trabalhistas_ativos),
'qtd_tributarios_ativos': len(tributarios_ativos),
'tem_processo_ativo': len(ativos) > 0,
}
Calculando o score
Com as features extraídas, o score jurídico pode ser calculado de diferentes formas dependendo da maturidade do seu modelo:
Modelo de regras (baseline)
Para um primeiro modelo rápido, um sistema de pontuação baseado em regras já entrega valor imediato:
def calcular_score_juridico(features: dict) -> dict:
"""
Retorna um score de 0 a 100.
Menor score = maior risco judicial.
"""
score = 100
flags = []
# --- Penalizações por volumetria ---
total = features['total_processos']
if total == 0:
pass # Sem histórico — sem penalização
elif total <= 3:
score -= 5
elif total <= 10:
score -= 15
elif total <= 30:
score -= 25
else:
score -= 40
flags.append('alto_volume_processual')
# --- Penalizações por polo ---
if features['razao_passivo'] > 0.8 and total >= 3:
score -= 15
flags.append('predominantemente_passivo')
# --- Penalizações por fase ---
if features['em_execucao'] >= 3:
score -= 20
flags.append('multiplas_execucoes')
elif features['em_execucao'] >= 1:
score -= 10
if features['valor_total_em_execucao'] > 100_000:
score -= 15
flags.append('alto_valor_em_execucao')
elif features['valor_total_em_execucao'] > 30_000:
score -= 8
# --- Penalizações por área ---
if features['tributarios'] >= 5:
score -= 15
flags.append('alto_passivo_tributario')
if features['criminais'] >= 1:
score -= 20
flags.append('processo_criminal')
if features['trabalhistas'] >= 10:
score -= 10
flags.append('alto_passivo_trabalhista')
# --- Penalizações por dados cadastrais ---
# (integrar com o campo special_status da consulta de entity)
if features.get('recuperacao_judicial'):
score -= 40
flags.append('recuperacao_judicial')
# Garante que o score não seja negativo
score = max(0, score)
# Faixa de risco
if score >= 75:
faixa = 'BAIXO'
elif score >= 50:
faixa = 'MEDIO'
elif score >= 25:
faixa = 'ALTO'
else:
faixa = 'CRITICO'
return {
'score': score,
'faixa': faixa,
'flags': flags
}
Pipeline completo
def avaliar_risco_juridico(documento: str, tipo: str = 'cnpj') -> dict:
"""
Pipeline completo: coleta features e retorna score.
"""
# Etapa 1: perfil agrupado (síncrono, imediato)
perfil = buscar_perfil_agrupado(documento, tipo)
features = extrair_features_agrupadas(perfil)
# Etapa 2: hot storage filtrado para features de valor
dados_hotstorage = buscar_execucoes_fiscais(documento, tipo)
features.update(extrair_features_hotstorage(dados_hotstorage))
# Etapa 3: score
resultado = calcular_score_juridico(features)
return {
'documento': documento,
'tipo': tipo,
'features': features,
**resultado
}
# Exemplo de uso
resultado = avaliar_risco_juridico('00.000.000/0001-00', 'cnpj')
print(f"Score: {resultado['score']}/100")
print(f"Faixa: {resultado['faixa']}")
print(f"Flags: {', '.join(resultado['flags']) or 'nenhuma'}")
Filtros avançados para features específicas
Para features que exigem recortes precisos, os filtros da consulta histórica permitem isolar exatamente o subconjunto de processos relevante:
Processos recentes como passivo (últimos 2 anos)
{
"search": {
"search_type": "cnpj",
"search_key": "00.000.000/0001-00",
"search_params": {
"filter": {
"side": "Passive",
"distribution_date_gte": "2024-01-01T00:00:00.000Z"
}
}
}
}
Processos trabalhistas de alto valor
{
"search": {
"search_type": "cnpj",
"search_key": "00.000.000/0001-00",
"search_params": {
"filter": {
"side": "Passive",
"amount_gte": 50000,
"tribunals": {
"keys": ["TRT1","TRT2","TRT3","TRT4","TRT5","TRT6","TRT7","TRT8","TRT9",
"TRT10","TRT11","TRT12","TRT13","TRT14","TRT15","TRT16","TRT17",
"TRT18","TRT19","TRT20","TRT21","TRT22","TRT23","TRT24"],
"not_equal": false
}
}
}
}
}
Excluir processos arquivados e focar apenas nos ativos
O hot storage com process_status: true retorna o campo phase por processo. Filtre no código:
processos_ativos = [
p for p in response_data
if p.get('status') == 'Ativo'
and p.get('phase') not in ('Arquivado', 'Cancelado', 'Trânsito em julgado ou acordo')
]
Calibração do score jurídico por vertical de negócio
O score não tem peso universal. Os mesmos dados precisam ser interpretados de forma diferente dependendo do contexto de uso:
Crédito para PJ (fintechs, bancos): o peso maior vai para execuções fiscais ativas, valor total do passivo e presença de recuperação judicial. Processos trabalhistas de baixo valor são ruído para esse contexto.
RH e background check: o peso vai para processos criminais e mandados de prisão. Volume de processos cíveis é irrelevante aqui — um candidato pode ter muitos processos trabalhistas como autor (reclamante) sem que isso represente risco para o cargo.
Compliance/KYC: peso alto para processos criminais, PEP (pessoas politicamente expostas), lavagem de dinheiro e processos em instâncias superiores (STJ, STF).
Imobiliário: peso alto para ações de despejo (como passivo), execuções cíveis ativas e processos de cobrança recentes.
PESOS = {
'credito_pj': {
'em_execucao': 25,
'tributarios': 20,
'razao_passivo': 15,
'alto_volume_processual': 15,
'recuperacao_judicial': 40,
'criminais': 5,
},
'rh_background': {
'criminais': 40,
'em_execucao': 10,
'tributarios': 5,
'razao_passivo': 5,
},
'compliance_kyc': {
'criminais': 35,
'tributarios': 20,
'em_execucao': 15,
'alto_volume_processual': 10,
},
'imobiliario': {
'em_execucao': 20,
'civis': 15,
'razao_passivo': 15,
'tributarios': 10,
}
}
Features adicionais via entity data
A consulta de dados cadastrais (response_type: "entity") entrega um campo crítico para modelos de risco de PJ: special_status.
def buscar_dados_cadastrais(cnpj: str) -> dict:
res = httpx.post(
'https://lawsuits.prod.judit.io/requests/create',
headers={'api-key': API_KEY},
json={
'search': {
'search_type': 'cnpj',
'search_key': cnpj,
'response_type': 'entity'
}
}
)
dados = res.json()
entidade = dados.get('response_data', [{}])[0]
return {
'recuperacao_judicial': entidade.get('special_status') == 'RECUPERACAO JUDICIAL',
'falencia': entidade.get('special_status') == 'FALENCIA',
'inapta_receita': not entidade.get('revenue_service_active', True),
'capital_social': entidade.get('share_capital', 0),
'tem_processos': dados.get('has_lawsuits', False),
}
O campo has_lawsuits retornado nessa consulta é um boolean que pode ser usado como pré-filtro: se false, o modelo pode assumir score máximo sem acionar a consulta agrupada.
Boas práticas para produção
Cache agressivo para dados agrupados. O perfil litigioso de um CNPJ não muda em horas. Use cache_ttl_in_days: 7 para consultas de scoring em volume — economiza custo e reduz latência.
On-demand apenas para decisões críticas. O parâmetro on_demand: true força a busca direto no tribunal — mais preciso, mas mais lento. Reserve para aprovações de crédito de alto valor; para triagem em volume, o datalake é suficiente.
Separe features de score de features de auditoria. O request_id de cada consulta deve ser salvo junto com a decisão. Em caso de contestação regulatória, você precisa provar quais dados embasaram a recusa.
Trate ausência de dados como categoria. Um CPF sem histórico judicial não é necessariamente de baixo risco — pode ser uma conta recém-criada. Crie uma categoria explícita sem_historico e não confunda com risco_zero.
Monitore a distribuição do score ao longo do tempo. Se a proporção de entidades com score alto começar a cair sem mudança no critério de aprovação, provavelmente há uma mudança no padrão dos dados (novo sistema de tribunal, mudança de cobertura, etc.) que precisa ser investigada.
Próximos passos
Para implementar o score jurídico na sua operação:
Publicado em: abril de 2026 — Atualizado a cada trimestre





