Brazil Electoral Candidate Lookup - TSE (Batch) avatar

Brazil Electoral Candidate Lookup - TSE (Batch)

Under maintenance

Pricing

$20.00 / 1,000 matched candidate record returneds

Go to Apify Store
Brazil Electoral Candidate Lookup - TSE (Batch)

Brazil Electoral Candidate Lookup - TSE (Batch)

Under maintenance

Batch-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

Alison Moura

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

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, or VEREADOR (city councillor) - the only three offices in a municipal election.
  • includeAssets - default true. 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-off cnpj-lookup made 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:

  1. (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 includeAssets is set), then searches them in-process. No VPS-uptime dependency (runs entirely on Apify's infrastructure, same principle as cnpj-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 conditional ETag check, but that was left for a later iteration rather than adding complexity before this product has any validated demand - see "Not tested" below).
  2. (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 generator meta tag and its REST API (/api/3/action/package_list → 172 datasets;
    package_show?id= candidatos-2024
    → full resource list with direct cdn.tse.jus.br URLs).
  • Fetched real file sizes via HEAD requests before committing to this design, not assumed: consulta_cand_2024.zip 63.7MB,
    consulta_cand_ complementar_2024.zip
    30.9MB, bem_candidato_2024.zip 44.3MB, prestacao_de_contas_eleitorais_candidatos_2024.zip 1.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 _BRASIL duplicate-union file this Actor deliberately never reads (see tse_data.py module docstring).
  • Read the bundled leiame.pdf data dictionary in full (via pdfminer) - 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.csv contains 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",
    candidateNames: ["RICARDO NUNES", "NOME QUE NAO EXISTE XYZ"]
    ): 8.5s real time. Correctly returned 3 real matches (São Paulo's mayor, ELEITO, 32 real assets; two other real SP candidates whose full/ballot names contain "RICARDO NUNES") and one correct no_match record 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.

  1. npx apify-cli push from this directory.
  2. Real smoke test on Apify's own infrastructure (apify-cli call), the same way ADR-011 re-verified pncp-lookup after 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.
  3. Set pay-per-event pricing and publish via the Apify REST API, the same calls already proven working three times (cnpj-lookup ADR-006/007, pncp-lookup ADR-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 from cnpj-lookup's first deployment.
  4. Report real usage/revenue into BUSINESS.md once it exists - do not fabricate numbers before they exist.