TRF3 — Tribunal Regional Federal da 3ª Região

Public process consultation (cpopg) for the federal courts under the third region (SP and MS), via the PJe ConsultaPublica system at pje1g.trf3.jus.br/pje/.

Feature Available
cpopg Yes
cposg No
cjsg Yes
cjpg No

Looking up a single process

import juscraper as jus

trf3 = jus.scraper("trf3")
df = trf3.cpopg("5005946-09.2025.4.03.6324")
print(df.shape)
df[["id_cnj", "processo", "classe", "data_distribuicao"]]
TRF3 cpopg: 100%|██████████| 1/1 [00:01<00:00,  1.08s/it]
(1, 12)
id_cnj processo classe data_distribuicao
0 50059460920254036324 5005946-09.2025.4.03.6324 PROCEDIMENTO DO JUIZADO ESPECIAL CÍVEL (436) 29/09/2025

Available columns

df.columns.tolist()
['processo',
 'data_distribuicao',
 'classe',
 'assunto',
 'jurisdicao',
 'orgao_julgador',
 'endereco_orgao',
 'polo_ativo',
 'polo_passivo',
 'movimentacoes',
 'documentos',
 'id_cnj']

The first three columns are the canonical scalars; the trailing four are list-typed and carry the nested arrays (parties, movements, attached documents).

Inspecting movements

movs = df.iloc[0]["movimentacoes"]
print(f"{len(movs)} events recorded")
for m in movs[:3]:
    print(f"  {m['data']} - {m['descricao']}")
11 events recorded
  29/04/2026 12:51:15 - Remetidos os Autos ao CEJUSC ou Centros de Conciliação/Mediação da Subseção Judiciária
  29/04/2026 12:37:09 - Expedida/certificada a intimação eletrônica
  29/04/2026 12:37:08 - Proferido despacho de mero expediente

Inspecting parties

print("Polo ativo:")
for p in df.iloc[0]["polo_ativo"]:
    print(f"  - {p['participante']}")

print()
print("Polo passivo:")
for p in df.iloc[0]["polo_passivo"]:
    print(f"  - {p['participante']}")
Polo ativo:
  - JOAO PAULO NASCIMENTO DA SILVA - CPF: 003.XXX.XXX-XX (AUTOR)

Polo passivo:
  - CAIXA ECONOMICA FEDERAL - CEF - CNPJ: 00.3XX.XXX/XXXX-XX (REU) Departamento Jurídico - Caixa Econômica Federal

Looking up multiple processes at once

cnjs = [
    "50059460920254036324",
    "50061271020254036324",
    "50035362120254036342",
]
df_batch = trf3.cpopg(cnjs)
df_batch[["id_cnj", "processo", "classe"]]

TRF3 cpopg:   0%|          | 0/3 [00:00<?, ?it/s]
TRF3 cpopg:  33%|███▎      | 1/3 [00:01<00:02,  1.42s/it]
TRF3 cpopg:  67%|██████▋   | 2/3 [00:03<00:01,  1.63s/it]
TRF3 cpopg: 100%|██████████| 3/3 [00:04<00:00,  1.30s/it]
TRF3 cpopg: 100%|██████████| 3/3 [00:04<00:00,  1.37s/it]
id_cnj processo classe
0 50059460920254036324 5005946-09.2025.4.03.6324 PROCEDIMENTO DO JUIZADO ESPECIAL CÍVEL (436)
1 50061271020254036324 5006127-10.2025.4.03.6324 PROCEDIMENTO DO JUIZADO ESPECIAL CÍVEL (436)
2 50035362120254036342 5003536-21.2025.4.03.6342 PROCEDIMENTO DO JUIZADO ESPECIAL CÍVEL (436)

Handling processes the public portal cannot return

When a CNJ does not surface in the public consultation (sigilo, archived, or simply not found), the row carries only id_cnj. The other columns come back as None/NaN, so callers can still distinguish “looked up but missing” from “never tried”.

import pandas as pd

df_missing = trf3.cpopg("00000000020994030000")
print("processo:", df_missing.iloc[0].get("processo"))
print("classe:", df_missing.iloc[0].get("classe"))

TRF3 cpopg:   0%|          | 0/1 [00:00<?, ?it/s]
TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  8.34it/s]
TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  8.19it/s]
processo: None
classe: None

Splitting download from parse

cpopg is a thin wrapper over cpopg_download (raw HTML) + cpopg_parse (HTML → DataFrame). Splitting them is useful when you want to cache the raw HTMLs to disk before processing.

htmls = trf3.cpopg_download("5005946-09.2025.4.03.6324")
print(f"got {len(htmls)} HTML(s), {len(htmls[0])} chars")

df_again = trf3.cpopg_parse(htmls, ["50059460920254036324"])
df_again[["id_cnj", "processo", "data_distribuicao"]]

TRF3 cpopg:   0%|          | 0/1 [00:00<?, ?it/s]
TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  2.43it/s]
TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  2.43it/s]
got 1 HTML(s), 85297 chars
id_cnj processo data_distribuicao
0 50059460920254036324 5005946-09.2025.4.03.6324 29/09/2025

Downloading peças (anexos)

By default cpopg returns metadata only: process info, parties, movements, and a documentos list (id / date / description). The actual peça files (HTML viewer payloads) are not downloaded.

To also download the peças, pass download_pecas=True and a diretorio. Each peça is saved as <diretorio>/<cnj>/<id_processo_doc>.html (a self- contained XHTML with images embedded as data: URLs), and the returned DataFrame gains a pecas column with the list of saved paths.

The flag lives on cpopg (instead of a separate cpopg_download_pecas method) because the ca tokens that identify each peça are bound to the Seam conversation that produced the detail page — peças must be fetched from the same requests.Session. A separate method would have to re-issue the detail GET internally just to obtain fresh tokens; folding the optional download into cpopg avoids that duplicate round-trip.

import tempfile

with tempfile.TemporaryDirectory() as tmp:
    df_pecas = trf3.cpopg(
        "5005946-09.2025.4.03.6324",
        download_pecas=True,
        diretorio=tmp,
    )
    saved = df_pecas.iloc[0]["pecas"]
    print(f"saved {len(saved)} peça(s)")
    for p in saved[:3]:
        print(" ", p)
TRF3 cpopg:   0%|          | 0/1 [00:00<?, ?it/s]TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  1.31it/s]TRF3 cpopg: 100%|██████████| 1/1 [00:00<00:00,  1.31it/s]
saved 4 peça(s)
  C:\Users\jtrec\AppData\Local\Temp\tmpkxwx55p9\50059460920254036324\582695147.html
  C:\Users\jtrec\AppData\Local\Temp\tmpkxwx55p9\50059460920254036324\581769486.html
  C:\Users\jtrec\AppData\Local\Temp\tmpkxwx55p9\50059460920254036324\578698643.html

Searching case law (cjsg)

cjsg searches the TRF3 case-law portal at web.trf3.jus.br/jurisprudencia, a system separate from the PJe. The base argument picks one of the four result sets the portal keeps apart:

base Content
"acordaos" (default) TRF3 appellate judgments
"turmas_recursais" judgments of the federal small-claims appeal panels (JEF)
"monocraticas" single-judge decisions of the TRF3
"monocraticas_turmas_recursais" single-judge decisions of the appeal panels

Each call opens its own session on the portal and reads the result list, which already carries the ementa and the full text of every document: the cost is three fixed requests (opening the tab, the search POST and the GET of its redirect, which is page 1) plus one per extra page, with sleep_time between pages.

df_cjsg = trf3.cjsg(
    "medicamento",
    paginas=range(1, 3),
    data_julgamento_inicio="2026-01-01",
    data_julgamento_fim="2026-06-30",
)
print(df_cjsg.shape)
df_cjsg[["processo", "classe", "orgao_julgador", "relator", "data_julgamento", "meio_publicacao"]].head()

The other bases

In the single-judge bases, ementa holds the text of the decision (there is no ementa) and orgao_julgador is empty; data_julgamento is the date of the decision.

In part of the older appeal-panel judgments (turmas_recursais), ementa is null because the portal sends an empty ementa block; inteiro_teor still carries the full text. When this was measured, every ementa from 2013 and 2014 was empty, and some from 2011.

for base in ["turmas_recursais", "monocraticas", "monocraticas_turmas_recursais"]:
    df_base = trf3.cjsg(
        "medicamento",
        base=base,
        paginas=1,
        data_julgamento_inicio="2026-01-01",
        data_julgamento_fim="2026-06-30",
    )
    print(base, df_base.shape, df_base["relator"].iloc[0])

Filters

relator and classe are free text matched against the portal’s index, not dropdown IDs. Use the spelling of the form’s dropdowns: relator="NERY JUNIOR", classe="AI - AGRAVO DE INSTRUMENTO".

orgao_julgador picks one option of the órgão dropdown of the tab, by its label ("3ª Turma", "11ª Turma Recursal da Seção Judiciária de São Paulo") or by its indexed name ("TERCEIRA TURMA", "11ª TURMA RECURSAL DE SÃO PAULO"), ignoring case and spacing. The value in the orgao_julgador column also works: as in the portal’s form, "1a. seção" becomes "1A", so "QUINTA TURMA - 1A. SEÇÃO" matches the option QUINTA TURMA - 1a. SEÇÃO. The search reads the dropdown from the tab it already opens and sends the pair the browser sends, the label and the indexed name of the option, and the portal returns the documents recorded under either (measured with “medicamento”: "QUINTA TURMA" finds 576 documents, against 443 for the label alone and 133 for the indexed name alone). A label shared by more than one option, such as "5ª Turma" in the acórdãos base, raises ValueError listing the indexed names that resolve it; text that matches no option is sent as is and only finds what the portal records with that spelling. The monocratic bases have no órgão filter.

The form has a single date pair, so pass either data_julgamento_* or data_publicacao_*, not both.

df_filtrado = trf3.cjsg(
    "medicamento",
    relator="NERY JUNIOR",
    classe="AI - AGRAVO DE INSTRUMENTO",
    data_publicacao_inicio="2026-01-01",
    data_publicacao_fim="2026-06-30",
    paginas=1,
)
df_filtrado[["processo", "relator", "relator_acordao", "data_publicacao"]]