Brazil Electoral Candidate Lookup - TSE (Batch)
Under maintenancePricing
$20.00 / 1,000 matched candidate record returneds
Brazil Electoral Candidate Lookup - TSE (Batch)
Under maintenanceBatch-search Brazil's official TSE electoral registry for the 2024 municipal elections: candidate profile (party, coligation, office, election result) plus declared assets (bens declarados), by candidate name. Charged per matched candidate record.
Pricing
$20.00 / 1,000 matched candidate record returneds
Rating
0.0
(0)
Developer
Alison Moura
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Batch-search Brazil's official electoral court (TSE) open-data registry for the 2024 municipal elections: candidate profile (party, coligation, office, election result across every round) plus declared assets (bens declarados), by candidate name. Get back one clean, normalized JSON record per matched candidate, ready for a PEP-screening/compliance check, journalism/research pipeline, or civic-transparency tool.
Who this is for
Compliance and KYC/AML teams doing politically-exposed-person (PEP)
screening, journalists and researchers studying candidates' declared
wealth or election outcomes, civic-tech and transparency projects, or
anyone using cnpj-lookup or
ceis-lookup who also needs to
check whether a person has run for public office in Brazil, what they
declared owning at the time, and how they did.
What you send in
{"candidateNames": ["RICARDO NUNES", "VERA FRANÇA"],"uf": "SP","cargo": "PREFEITO","includeAssets": true,"maxResultsPerName": 10}
candidateNames- list of names to search for (full legal name or ballot/urna name). Matching is substring, case-insensitive, and accent-insensitive against both the candidate's full legal name and their ballot name -"franca"matches"VERA LÚCIA SANTOS DE FRANÇA".uf- optional 2-letter state code (e.g.SP,RJ,MG) applied to every name in this run. Strongly recommended: restricts the search to one state's file instead of scanning all 26 (Distrito Federal has no municipal elections and isn't in this dataset - see below), which is faster. Omit to search nationwide.cargo- optional office filter:PREFEITO(mayor),VICE-PREFEITO, orVEREADOR(city councillor) - the only three offices in a municipal election.includeAssets- defaulttrue. Whether to join each matched candidate's declared-assets records into the output.maxResultsPerName- cap on matches returned per name query (default 10, max 50), to bound output for common names like "SILVA".
There is no CPF/CNPJ search. See "Personal data & intended use" below for why: TSE itself does not disclose candidate CPFs in this dataset.
What you get back
One record per matched candidate. Real example (São Paulo's mayor, re-elected 2024 - a public official's public candidacy record, trimmed here to 2 of his 32 declared assets):
{"status": "matched","query_name": "RICARDO NUNES","sq_candidato": "250002098117","nr_candidato": "15","nome_completo": "RICARDO LUIS REIS NUNES","nome_urna": "RICARDO NUNES","cpf": null,"cpf_note": "Not disclosed by TSE for this dataset (Resolução TSE 23.609/2019 art.33 §2º, as amended by Resolução 23.729/2024) - see 'Personal data & intended use'.","cargo": "PREFEITO","uf": "SP","municipio": "SÃO PAULO","partido": { "sigla": "MDB", "nome": "MOVIMENTO DEMOCRÁTICO BRASILEIRO", "numero": 15 },"coligacao": { "nome": "CAMINHO SEGURO PRA SÃO PAULO", "composicao": "PP / MDB / PL / PSD / ...", "tipo_agremiacao": "COLIGAÇÃO" },"nascimento": { "data": "13/11/1967", "uf": "SP" },"genero": "MASCULINO", "raca_cor": "BRANCA", "grau_instrucao": "SUPERIOR INCOMPLETO","estado_civil": "CASADO(A)", "ocupacao": "PREFEITO","eleicao": { "ano": 2024, "descricao": "Eleições Municipais 2024" },"rounds": [{ "turno": 1, "data_eleicao": "06/10/2024", "situacao_totalizacao": "2º TURNO" },{ "turno": 2, "data_eleicao": "27/10/2024", "situacao_totalizacao": "ELEITO" }],"situacao_final": "ELEITO","situacao_candidatura": "DEFERIDO","processo_numero": "06007550320246260001","despesa_max_campanha_brl": 26910445.8,"reeleicao": true,"bens_declarados": [{ "tipo": "Apartamento", "descricao": "APARTAMENTO CIDADE DUTRA", "valor_brl": 605000.0, "atualizado_em": "07/07/2026" },{ "tipo": "Terreno", "descricao": "TERRENO EM CAMPINAS-SP", "valor_brl": 390000.0, "atualizado_em": "07/07/2026" }],"bens_total_brl": 4843350.91,"bens_count": 32}
A name that matches zero candidates still gets a record, so you can see what happened to every query - it's just not charged:
{ "query_name": "NOME QUE NAO EXISTE XYZ", "status": "no_match" }
Personal data & intended use
This is legally public data: Brazilian electoral law (and TSE's own regulations) require candidate registration data and declared-asset disclosures to be published, precisely so voters and oversight bodies can scrutinize people who have chosen to run for public office. It is not scraped, leaked, or repurposed private data - it's an official transparency mechanism, and this Actor's per-dataset license (Creative Commons Attribution, see below) is TSE's own explicit grant to reuse it.
Legitimate uses: PEP screening and AML/compliance checks, journalism and academic research on candidates' wealth and campaign outcomes, civic transparency and election-monitoring tools, due diligence before entering a business relationship with a current or former public official.
Not an intended use, and this Actor should not be used for: harassment
or doxxing of candidates or their families, building profiles to target
individuals outside a transparency/compliance/journalism purpose, or using
the demographic fields present in the source data (gender, race/color,
marital status, education level - raca_cor, genero, estado_civil,
grau_instrucao) for discriminatory screening of any kind. These fields
are included because TSE publishes them as part of the official candidate
record (Brazil tracks candidate demographics for representation-monitoring
purposes), not as an invitation to filter or discriminate on them - don't.
CPF is never returned, for every candidate, always - not a partial
mask like cnpj-lookup's partner CPFs, a hard null with an explanatory
cpf_note. TSE itself stopped disclosing NR_CPF_CANDIDATO in this
dataset (verified directly in the bundled leiame.pdf data dictionary,
which quotes Resolução TSE nº 23.609/2019 art. 33 §2º as amended by
Resolução nº 23.729/2024: candidates' CPF, personal address, personal
phone/email, and ID document are withheld from public disclosure and filed
as a sealed document in the candidacy registration process instead). This
is a real, current TSE policy, not a design choice made by this Actor -
confirmed empirically too: every row in the downloaded 2024 file has
NR_CPF_CANDIDATO = "-4" (TSE's own sentinel for "not disclosed").
Interestingly, the in-progress 2026 dataset does still show real CPF
digits as of this build (2026-08-17) - one more reason "most recent
complete cycle" (2024) and "most recent cycle" (2026) are genuinely
different datasets with different disclosure properties, not just
different years of the same thing.
Why the 2024 cycle, not 2026
As of this build (2026-08-17), TSE's portal already has a candidatos-2026
dataset (Brazil's next general election is October 2026, and the candidacy
registration deadline already passed). It was deliberately not used:
2026's data is provisional - candidacies can still be legally challenged
(impugnação) up to and after election day, and there are no final "elected/
not elected" results yet. 2024 (the last municipal elections) is the
most recent dataset where every candidacy has gone through final judicial
review and every race has a final, adjudicated result - a meaningfully
different reliability guarantee, not just an older number. This also means
every 2024 record's situacao_candidatura (DEFERIDO/INDEFERIDO/etc.)
and situacao_final (ELEITO/NÃO ELEITO/etc.) reflect a settled outcome.
Scope: candidates + assets, not campaign donations (for now)
TSE also publishes campaign-finance data ("Prestação de Contas Eleitorais")
- itemized donations and expenses per candidate. It was evaluated and
deliberately excluded from this V1: the 2024 candidate-finance file
alone (
prestacao_de_contas_eleitorais_candidatos_2024.zip) is 1.35GB compressed - roughly 20x the combined size of the candidate-profile (63MB) and declared-assets (44MB) files this Actor actually uses - and splits across separate revenue/expense/donor sub-files with their own distinct schemas. That's a materially different, much larger engineering effort (most plausibly a self-hosted/pre-indexed dataset rather than a per-run bulk download, the same option-(a)-vs-(b) trade-offcnpj-lookupmade explicit for the Receita Federal CNPJ dump) than a clean V1 batch-lookup product justifies before this Actor has any real, validated demand. If this Actor gets real paid usage, campaign-finance/donation search is the natural next product to evaluate - not built here.
Pricing
Pay-per-event: you are charged once per unique candidate matched and
returned (event candidate-record), deduped across election rounds (a
candidate who appears in both the first and second round of a mayoral race
is one charged record, with both rounds' results in rounds). A name
query that matches zero candidates is not charged. See the Actor's Store
page for the current per-event price.
Pricing rationale: set at the same tier as
pncp-lookup ($0.02/record, 2x
cnpj-lookup's $0.01), for a different reason than PNCP's own (PNCP is
priced up for live-API rate-limiting/latency; this Actor's underlying
source has no rate limit at all - it's static bulk files). Here the cost
driver is data volume and join complexity: every search downloads and
scans TSE's bulk candidate CSV (single-state or, for a nationwide search,
all 26 states' files, ~240MB uncompressed total), then does a second
targeted pass to join party/coligation/status detail and declared-assets
data - a richer, multi-source-joined record than cnpj-lookup's single-API
lookup, even though (unlike PNCP) there's no per-request network
round-trip cost or rate limit driving it.
Data source, access method & terms of use
Source: Portal de Dados Abertos do TSE
(Tribunal Superior Eleitoral), Brazil's electoral court's official open-data
portal. It runs CKAN (confirmed via the portal's own generator meta tag
and its live REST API, /api/3/action/package_show, fetched 2026-08-17).
Access method - bulk file downloads, not a per-record API: unlike this
developer's other Aurora Actors, CKAN's REST API here only returns
dataset metadata (title, resource list, license, update frequency) -
there is no query/search endpoint for individual candidate records. The
actual data is published exclusively as ZIP-of-CSV bulk downloads on
cdn.tse.jus.br, refreshed on TSE's own schedule ("Frequência de
atualização: Diária" per the dataset's own CKAN metadata). This Actor
downloads the relevant ZIP(s) directly from cdn.tse.jus.br on each run
and searches them in-process - see "For the Aurora project" below for the
full design rationale.
License - explicit and per-dataset, verified directly, not inferred:
every dataset returned by the CKAN API (including candidatos-2024, the
one this Actor uses) carries "license_id": "cc-by",
"license_title": "Creative Commons Attribution" as structured metadata,
confirmed live via package_show?id=candidatos-2024 (2026-08-17). CC-BY is
a standard, unambiguous license that explicitly permits commercial reuse,
requiring only attribution - the strongest, most explicit legal basis of
any Aurora Actor's data source so far (stronger than an inferred statutory
argument, and unlike a source's terms simply not prohibiting resale).
This is independently corroborated by the portal's own public FAQ
(dadosabertos.tse.jus.br/pages/perguntasfrequentes, fetched 2026-08-17),
which defines "Dados Abertos" using Brazil's statutory open-data definition
(the same one underpinning pncp-lookup's legal basis, Decreto 8.777/2016):
data "disponibilizados sob licença aberta que permita sua livre utilização,
consumo ou cruzamento, limitando-se a creditar a autoria ou a fonte" (made
available under an open license permitting free use, consumption, or
cross-referencing, limited only to crediting authorship/source).
One access gap, disclosed rather than glossed over: TSE's general
privacy-policy/terms-of-use page
(tse.jus.br/transparencia/politica-de-privacidade-e-termos-de-uso,
linked from the open-data portal's footer) could not be fetched directly
from this environment - it returned HTTP 403 "Access Denied" from an
Akamai edge WAF, reproduced identically via a plain curl, a browser-
User-Agent curl, and the fetch tool (2026-08-17); a wayback-machine
fallback was also attempted and hit an unrelated rate limit at the same
time. This was not treated as a blocker the way DataJud's or
ReceitaWS's explicit prohibitions were (see DECISIONS.md ADR-005/009):
those were real prohibition clauses actually read in full; this is an
inability to reach one general page, while the dataset-specific,
machine-readable license (CC-BY, above) and the portal's own FAQ - both
successfully read in full - independently agree on unrestricted, credited
reuse. If the general terms page turns out to narrow that when it becomes
reachable, that would need to be revisited before this Actor's pricing is
treated as settled.
Rate limits & fair use
cdn.tse.jus.br publishes no rate limit and, being static file hosting
(not a live query API), has no per-record processing cost to protect -
this Actor's own real-world runs (see "What was actually tested vs. not")
took 8-13 seconds end to end, including the download. There is nothing to
tune here comparable to maxConcurrency on the other Aurora Actors: this
product's cost driver is file size/scan scope (see uf/cargo filters
above), not request concurrency, since a search is one download-and-scan
pass, not many independent network calls.
For the Aurora project (internal)
Fourth product, built same-day as [bcb-institutions-lookup],
[ceis-lookup], and [anvisa-products-lookup] per DECISIONS.md
ADR-008's owner-directed expansion (see OPPORTUNITIES.md and ADR-004/005/
009/010/011 for the pattern this follows and its precedents).
Design decision: bulk-download-and-scan, not a live per-record API
Every other Aurora Actor so far calls a live API per input record. TSE's open-data portal doesn't offer one for candidate data (see "Access method" above) - only bulk ZIP-of-CSV downloads. Two designs were considered:
- (Chosen) Download-and-scan per run: each Actor run downloads the
needed ZIP(s) from
cdn.tse.jus.br(63MB candidate + 30MB complementar- 44MB assets, only downloaded if
includeAssetsis set), then searches them in-process. No VPS-uptime dependency (runs entirely on Apify's infrastructure, same principle ascnpj-lookup's option-(a) choice), no infrastructure to maintain, ships immediately. Real downside, accepted deliberately: every run re-downloads ~100-140MB even though TSE only refreshes daily - there's no cross-run cache (Apify's default KV store could hold one, keyed by a conditionalETagcheck, but that was left for a later iteration rather than adding complexity before this product has any validated demand - see "Not tested" below).
- 44MB assets, only downloaded if
- (Not chosen) Self-hosted pre-built index: download the ZIPs once,
build a real index (SQLite/similar) on a persistent volume, serve
queries against that instead of re-scanning CSVs per run. Faster and
avoids repeat downloads, but needs either VPS-hosted infrastructure
(this Aurora VPS's uptime becomes a dependency the Actor doesn't have
today) or Apify's paid persistent-storage tiers - the same class of
trade-off
cnpj-lookup's README already reasoned through for RFB's full CNPJ dump, and the same conclusion applies: not justified before V1 demand is validated.
Performance turned out much better than the file sizes suggested it might
- see the real timings below.
Source research (verified 2026-08-17, not assumed)
- Confirmed CKAN via the portal's live
generatormeta tag and its REST API (/api/3/action/package_list→ 172 datasets;→ full resource list with directpackage_show?id= candidatos-2024cdn.tse.jus.brURLs). - Fetched real file sizes via
HEADrequests before committing to this design, not assumed:consulta_cand_2024.zip63.7MB,30.9MB,consulta_cand_ complementar_2024.zipbem_candidato_2024.zip44.3MB,prestacao_de_contas_eleitorais_candidatos_2024.zip1.35GB (the file that drove the campaign-finance exclusion above). - Downloaded and inspected the real ZIP contents directly: each bundles
one CSV per state (26 - confirmed Distrito Federal has no municipal
elections, so is legitimately absent, not a data gap) plus a
_BRASILduplicate-union file this Actor deliberately never reads (seetse_data.pymodule docstring). - Read the bundled
leiame.pdfdata dictionary in full (viapdfminer) - the authoritative, dataset-specific source for encoding (Latin-1), delimiter (;), sentinel values (#NULO/#NE/-1/-3/-4), and the CPF non-disclosure policy quoted verbatim in this README. It contains no licensing/terms clauses of its own (checked directly, not assumed) - it's purely a technical layout reference. - Confirmed the CPF-masking finding empirically, not just from the PDF:
every row of the real downloaded 2024 file has
NR_CPF_CANDIDATO = "-4"; by contrast, the same field in the real downloaded (in-progress)consulta_cand_2026_AP.csvcontains real 11-digit CPF numbers - a concrete, verified difference between the two datasets, not a guess. - Picked São Paulo's mayor (Ricardo Nunes, re-elected 2024,
SQ_CANDIDATO 250002098117) as the real-public-figure fixture per this task's own instruction, since he's an unambiguous, verifiable public official with a large, real declared-assets list (32 items, ~R$4.8M) - good coverage for both the "profile" and "assets" join paths.
What was actually tested vs. not
Tested and passing: 47/47 tests (pytest, Python 3.12 venv - same
reasoning as cnpj-lookup for using 3.12 over the VPS's default 3.10, see
that README), covering: accent/case-insensitive name normalization and
substring matching (name_match.py); joining multi-round candidate rows,
complementar status rows, and asset rows into one output record, including
the CPF-always-null guarantee, BRL parsing for both of TSE's two decimal
notations, and the "no complementar row found" edge case
(normalize.py); ZIP download/caching/streaming-read behavior including a
missing-member (no data for that UF) case and download-failure handling,
via httpx.MockTransport - no real network calls in the unit test suite
(tse_data.py); and full search orchestration - single-UF and nationwide
scans, cargo filtering, the assets-join toggle, multi-query runs, and
"zero matches" handling (search.py). ruff clean, zero warnings.
apify-cli validate-schema passes for both input_schema.json and
dataset_schema.json.
All fixtures are real TSE data captured 2026-08-17 (trimmed to a handful of real rows, not synthesized), per this task's instruction to use a real public figure's public candidacy record - not hand-written mocks pretending to be TSE's schema.
Full end-to-end runs against the live internet (apify-cli run, which
reads .actor/actor.json exactly as the real platform would) - genuinely
completed, not just attempted:
- Single-state search (
uf: "SP",): 8.5s real time. Correctly returned 3 real matches (São Paulo's mayor,candidateNames: ["RICARDO NUNES", "NOME QUE NAO EXISTE XYZ"]ELEITO, 32 real assets; two other real SP candidates whose full/ballot names contain "RICARDO NUNES") and one correctno_matchrecord for the nonexistent name. - Nationwide search (no
uf,candidateNames: ["RICARDO NUNES"],maxResultsPerName: 5): 13.1s real time scanning all 26 states' files (~240MB). Correctly returned 5 real distinct candidates from 5 different states (MA, MG, PR×2, RS) whose names contain "Ricardo Nunes", respecting the cap.
Both runs are real production evidence this Actor works end to end against
the live source, not a claim inferred from unit tests alone - a stronger
starting position than pncp-lookup's disclosed pre-deployment gap
(ADR-010/011) or ceis-lookup's (no test API key available locally).
Not tested / could not verify locally:
- Real pay-per-event charging behavior: same as every other Aurora
Actor, the SDK correctly no-ops charging outside a real platform run
(
"Ignored attempt to charge for an event..."in the local run logs above) - the charging code path is exercised on every match, but actual billing can only be confirmed after deploying and monetizing. - Docker build: no Docker on this VPS (same constraint as every prior Aurora Actor - no root/sudo to install it). The Dockerfile matches the other three products' (and Apify's official template) line-for-line except the package name.
- Cross-run caching: not implemented (see "Design decision" above) - every run re-downloads the ZIPs fresh; not yet validated whether real usage patterns would make a KV-store cache worth the added complexity.
- The general TSE terms-of-use page (see "Data source... one access gap, disclosed rather than glossed over").
Deployment (status: built, not yet deployed - no owner-only blocker)
Unlike ceis-lookup (blocked on a Gov.br API key only a human can obtain),
this product needs no owner action before deployment - same as
cnpj-lookup/pncp-lookup, since cdn.tse.jus.br needs no API key,
account, or credential of any kind.
npx apify-cli pushfrom this directory.- Real smoke test on Apify's own infrastructure (
apify-cli call), the same way ADR-011 re-verifiedpncp-lookupafter this VPS's own downloads: e.g.-i '{"candidateNames": ["RICARDO NUNES"], "uf": "SP"}'and confirm the mayor's real record comes back with 32 assets. - Set pay-per-event pricing and publish via the Apify REST API, the same
calls already proven working three times (
cnpj-lookupADR-006/007,pncp-lookupADR-011) - should need no owner action for the publish step itself, since payout billing info, public profile, and Store Terms acceptance are account-level settings that already carried over fromcnpj-lookup's first deployment. - Report real usage/revenue into
BUSINESS.mdonce it exists - do not fabricate numbers before they exist.