# Brazil Central Bank (BCB) Financial Institutions Lookup (`jgoia/bcb-institutions-lookup`) Actor

Check whether a company is a Banco Central do Brasil-authorized financial institution (bank, fintech/payment institution, credit union, brokerage, and more) by CNPJ or name, with authorization status and institution type. Pay only for matched records.

- **URL**: https://apify.com/jgoia/bcb-institutions-lookup.md
- **Developed by:** [Alison Moura](https://apify.com/jgoia) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 matched institution records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Brazil Central Bank (BCB) Financial Institutions Lookup

Check whether a company is a **Banco Central do Brasil-authorized financial
institution** — bank, fintech/payment institution, credit union
(cooperativa de crédito), brokerage (corretora/distribuidora), consórcio
administrator, virtual-asset service provider, and more — by CNPJ or by
name. Get back a clean, normalized record with authorization status,
institution type, and legal nature, sourced directly from BCB's own
supervised-entity registry (BcBase). Pay only for matched records.

### Who this is for

Anyone who needs to answer "is this company actually a regulated financial
institution, and is it currently authorized?" — fintech/KYC and AML
tooling verifying a counterparty isn't an unlicensed operator, payment
processors doing merchant due diligence, credit-risk and lending platforms
checking whether a partner bank/fintech is in good standing, or anyone
using [`cnpj-lookup`](https://apify.com/jgoia/cnpj-lookup) who also needs
to confirm a specific CNPJ is BCB-authorized rather than just registered
with Receita Federal (every CNPJ is in Receita Federal's registry; only a
small, specific subset is in this one).

### What you send in

```json
{
  "queries": ["18.236.120/0001-58", "Nubank", "BNDES"],
  "maxResultsPerQuery": 20,
  "maxConcurrency": 5
}
```

- `queries` — a list of CNPJs (full 14-character, or the 8-character
  "root" identifying the company regardless of branch/establishment;
  punctuation optional) and/or free-text institution name fragments, one
  per item. A value that cleans down to exactly 8 or 14 alphanumeric
  characters is treated as a CNPJ and matched exactly; anything else is
  treated as a case-insensitive substring search against the
  institution's legal name, short name, and trade name.
- `maxResultsPerQuery` — optional, default 20, max 50. Caps how many
  matched institutions a single name search can return (and therefore
  charge for) — a broad term like "banco" matches hundreds of entities.
  Does not affect CNPJ queries, which always resolve to at most one
  record.
- `maxConcurrency` — optional, default 5, max 10.

### What you get back

One dataset record per matched institution. Example (a CNPJ query for
Nubank's regulated payment-institution entity):

```json
{
  "cnpj": "18236120000158",
  "cnpj_formatted": "18.236.120/0001-58",
  "cnpj_root": "18236120",
  "bacen_id": "Z9318261",
  "sisbacen_code": "40923",
  "legal_name": "NU PAGAMENTOS S.A. - INSTITUIÇÃO DE PAGAMENTO",
  "legal_name_plain": "NU PAGAMENTOS S A    INSTITUICAO DE PAGAMENTO",
  "short_name": "NU PAGAMENTOS - IP",
  "trade_name": "NUBANK",
  "acronym": null,
  "institution_type": { "code": 41, "description": "Instituição de Pagamento" },
  "legal_nature": { "code": 9, "description": "Sociedade Anônima de Capital Fechado" },
  "authorization_status": {
    "code": 3,
    "description": "Autorizada em Atividade",
    "is_currently_authorized": true
  },
  "public_sector": { "is_public": false, "sphere_code": null, "sphere_description": null },
  "location": {
    "country": "Brasil", "country_iso3": "BRA", "state": "São Paulo",
    "municipality": "São Paulo", "municipality_ibge_code": "3550308"
  },
  "as_of_date": "2026-08-17",
  "_meta": {
    "source": "bcb-bcbase",
    "matched_query": "18.236.120/0001-58",
    "match_type": "cnpj",
    "retrieved_at": "2026-08-17T14:32:10+00:00",
    "data_license": "ODbL (Open Data Commons Open Database License) - Banco Central do Brasil, dadosabertos.bcb.gov.br"
  }
}
```

A name search returns one record per matching institution — searching
"Nubank" returns 3 distinct entities (the payment institution, the
brokerage, and the financing company), each a separate regulated entity
with its own CNPJ.

For a query that's invalid, not found, or errored, you still get a record
so you can see what happened to every input — it's just not charged:

```json
{ "query_input": "00000000000000", "query_type": "cnpj", "status": "invalid_format", "error": "invalid check digits (not a real CNPJ)" }
{ "query_input": "33.000.167/0001-01", "query_type": "cnpj", "status": "not_found" }
{ "query_input": "Zzzznonexistent", "query_type": "name", "status": "no_matches" }
```

Note: `not_found` for a CNPJ means "not in BCB's authorized-institution
registry," not "invalid company" — most Brazilian companies (e.g.
Petrobras) are legitimate, active CNPJs that simply aren't financial
institutions BCB supervises. Use
[`cnpj-lookup`](https://apify.com/jgoia/cnpj-lookup) to check general
company registration status instead.

#### What "authorized" means

`authorization_status.is_currently_authorized` is `true` only for status
code 3, "Autorizada em Atividade" (authorized and currently operating) —
deliberately narrower than any status merely containing the word
"Autorizada." BCB's registry also uses "Autorizada sem Atividade"
(authorized but not currently operating) as a distinct status, which this
field correctly reports as `false`. The full `authorization_status.code`/
`description` is always included so you can see the exact underlying
status (e.g. "Cancelada/Encerrada", "Em Liquidação Extrajudicial")
regardless of how the boolean simplifies it.

### Pricing

Pay-per-event: **you are charged only for matched institution records** —
the single record resolved from an exact CNPJ match, or each institution
returned by a name-fragment search. Not charged for CNPJs with no match,
name searches with no matches, invalid-format queries, or lookup errors.
See the Actor's Store page for the current per-event price.

**Set at $0.01/record**, matching
[`cnpj-lookup`](https://apify.com/jgoia/cnpj-lookup)'s price and lower
than [`pncp-lookup`](https://apify.com/jgoia/pncp-lookup) ($0.02) or
[`ceis-lookup`](https://apify.com/jgoia/ceis-lookup) ($0.03). Reasoning:
this Actor's real operational cost profile is closest to `cnpj-lookup`'s —
a single fast, unauthenticated, unpaginated query per CNPJ, no observed
rate limiting during development, no multi-registry fan-out (unlike
`ceis-lookup`'s 3 registries per document) and no multi-call search modes
or empirically-high baseline latency (unlike `pncp-lookup`). The narrower
dataset (a specific regulatory subset, not general company data) is a
value-add for the right buyer but doesn't change the cost to serve, so it
doesn't justify pricing above the cheapest tier already proven on this
same infrastructure pattern.

### Data source, terms of use & freshness

**Source**: Banco Central do Brasil's own open-data portal
(`dadosabertos.bcb.gov.br`), specifically the "Dados cadastrais de
entidades supervisionadas" dataset, served live via the Olinda OData API
(`olinda.bcb.gov.br/olinda/servico/BcBase/versao/v2/odata/`,
`EntidadesSupervisionadas`) — the same registry BCB itself uses
internally (Unicad/BCBase). No authentication required; verified live
2026-08-17 with zero auth headers on every call.

**Terms of use — verified directly against the primary source, not
assumed**: the dataset's own page on `dadosabertos.bcb.gov.br` (fetched
2026-08-17) shows an explicit **ODbL (Open Data Commons Open Database
License)** badge, and references this exact BcBase/`EntidadesSupervisionadas`/
Olinda API resource directly — not a generic portal-wide claim taken on
faith. ODbL's own text (Open Data Commons, `opendatacommons.org/licenses/
odbl/1-0/`) explicitly states granted rights "expressly include commercial
use, and do not exclude any field of endeavour." The one real obligation
it imposes — a notice making any user of an output ("Produced Work") aware
the content was obtained from the source database — is satisfied by the
`_meta.data_license` field this Actor already includes on every output
record; ODbL's share-alike clause applies to redistributing the
*database* itself, not to a derived/normalized output like this Actor's
records, so it doesn't apply here. This is a cleaner legal basis than
either `cnpj-lookup`'s (cleared by the *absence* of a resale prohibition
in a third-party API's ToS) or `ceis-lookup`'s (a general open-data-policy
argument) — closer to `pncp-lookup`'s explicit-permission case, and
confirmed against the specific dataset actually used here, not the
open-data portal in general.

**Freshness**: this Actor always queries with today's date as BCB's
`dataBase` snapshot parameter, so results reflect BCB's most current
published registry state — not a stale/cached copy. BCB itself updates
the underlying registry on its own schedule (not published as a fixed
interval); any of the fields here (institution status, name, etc.) can
lag a real-world change by BCB's own update cycle. For anything with
legal/compliance weight, treat this as a fast first-pass check, not a
substitute for BCB's own official channels.

### Rate limits & fair use

No published rate limit was found for this API (same situation as this
developer's `cnpj-lookup` and `pncp-lookup` Actors). This Actor paces its
own requests conservatively (~3 req/s ceiling) with retry/backoff, rather
than a number taken from documentation. `maxConcurrency` (max 10) lets you
tune parallelism within that ceiling.

***

### For the Aurora project (internal)

Fourth product built per `DECISIONS.md` ADR-008's owner-directed expansion
(alongside `ceis-lookup`, `tse-electoral-lookup`, and
`anvisa-products-lookup`). Code, `.actor/` schemas, Dockerfile, and
fixtures were built in an earlier session that was interrupted by a Claude
usage session limit before tests and this README were written (see
`BUSINESS.md`'s "2026-08-17" snapshot, "2 PARTIAL, paused"). This session
finished the product: wrote the missing test suite, found and fixed one
real bug the earlier session's code hadn't yet been run live against, and
verified the data source's terms of use directly rather than trusting the
`_meta.data_license` string already present in the code.

#### A real bug found and fixed during this session

The client (`bcb_institutions_lookup/client.py`) was passing OData
`$filter` values to `httpx` via its standard `params=` argument, which
encodes spaces as `+` (`application/x-www-form-urlencoded` convention).
This API's OData parser does **not** decode `+` back into a space — it
reads a `+` inside `$filter` as OData's own arithmetic-addition operator,
so every real query (both CNPJ-exact and name-search) failed with `HTTP
400 {"codigo":400,"mensagem":"The types 'Edm.Boolean' and 'Edm.String'
are not compatible."}`. This was invisible to the mocked unit tests
(`httpx.MockTransport` doesn't validate OData syntax the way the real
server does) and was only caught by this session's live end-to-end smoke
test — exactly the kind of gap `pncp-lookup`'s ADR-010 flagged as worth
specifically checking for, not assuming away. Fixed by building the query
string by hand with `%20`-encoded spaces
(`urllib.parse.urlencode(params, quote_via=urllib.parse.quote)`) instead
of `httpx`'s default encoding. Covered by a dedicated regression test
(`tests/test_client.py::TestQuerySuccess::test_space_encoding_uses_percent20_not_plus`)
that inspects the actual raw query string sent, not just that a mocked
200 comes back — a mock would have passed either way, which is exactly
why this bug survived the original mocked test suite undetected until a
real live call was made.

#### What was actually tested vs. not

**Tested and passing:**

- 97/97 tests (`pytest`, Python 3.12 venv — same reasoning as
  `cnpj-lookup`'s README for why 3.12 not the VPS's default 3.10: the
  `apify` SDK needs Python ≥3.11 and this VPS has no `python3.10-venv`
  installed and no passwordless sudo). Covers CNPJ/CNPJ-root cleaning and
  validation (`test_cnpj_utils.py`), raw-record-to-output normalization
  against real captured fixtures for 6 distinct real institutions —
  Nubank's payment-institution entity, BNDES (a public/federal entity),
  a cancelled/closed entity, a cooperativa de crédito, and a 3-result
  name search (`test_normalize.py`), the OData client's filter-building,
  response parsing, retry/backoff, pacing, and error-wrapping including
  the space-encoding regression above (`test_client.py`), and the main
  orchestration's input coercion, dedupe, CNPJ-vs-name query routing, and
  per-query push/charge behavior with a mocked client
  (`test_main.py`).
- `ruff` lint: clean, zero warnings.
- `apify-cli validate-schema`: passes for both `input_schema.json` and
  `dataset_schema.json`.
- **Full live end-to-end runs against the real internet**, twice, after
  the space-encoding fix: once via direct `python -m
  bcb_institutions_lookup` (4 queries: Nubank's CNPJ, "Nubank" name
  search, "BNDES" name search, and a deliberately invalid all-zeros
  CNPJ — result: 6 real institution records matched, 1 correctly rejected
  as invalid, 0 errors) and once via the official `apify-cli run` command
  (which reads `.actor/actor.json` exactly as the real platform would;
  "Itaú" name search + Nubank's CNPJ — 4 real institution records
  matched, 0 errors). Real data confirmed correct in both runs: exact
  CNPJ resolution, multi-result name search (3 distinct Nubank-group
  entities, 2 distinct BNDES-group entities), and clean invalid-format
  rejection.
- Terms of use verified directly against the primary source (the specific
  dataset page on `dadosabertos.bcb.gov.br`, not a generic portal claim)
  and against ODbL's own published license text — see "Data source, terms
  of use & freshness" above.

**Not tested / could not verify locally:**

- Real pay-per-event **charging** behavior: running locally, the Apify
  SDK correctly no-ops with the log line `"Ignored attempt to charge for
  an event - the Actor does not use the pay-per-event pricing"` (no
  platform pricing config exists outside a real platform run) — same
  situation as every other Aurora product before its own deployment. The
  charging code path is exercised (called on every matched record; the
  `event_charge_limit_reached` stop-early behavior is covered by mocked
  unit tests) but actual billing can only be confirmed after deploying
  and monetizing on the real platform.
- **Docker build**: this VPS has no Docker installed (no root/sudo
  available), so `Dockerfile` was never built into an image here. It
  follows the same template already proven to build successfully on
  Apify's infrastructure for `cnpj-lookup` and `pncp-lookup` (`FROM
  apify/actor-python:3.13`, byte-compile step, `CMD ["python", "-m",
  "bcb_institutions_lookup"]`).
- No real paid usage, no Apify Store listing yet — not deployed anywhere
  outside this repo and this VPS.
- The "all retries exhausted" / `BcbRequestError` path for a genuinely
  unreachable API is exercised by mocked unit tests only — there was no
  way to make the real live API fail on demand during this session (it
  was, in fact, fully reachable and working throughout).

#### Deployment

Same proven pattern as `cnpj-lookup` (ADR-006/007) and `pncp-lookup`
(ADR-011) — **no special access/key needed**, unlike `ceis-lookup`'s
Portal da Transparência key requirement. BCB's Olinda API is open, like
CNPJ's and PNCP's sources: no signup, no application, no auth header.
Account-level prerequisites (payout billing info, public profile, Store
Terms acceptance) already carried over from the `cnpj-lookup` deployment
and should need **no further owner action**.

Remaining steps, all executable without additional owner input once
run:

1. `cd products/bcb-institutions-lookup && npx apify-cli push` — build on
   Apify's infrastructure (same Dockerfile pattern already proven twice).
2. `npx apify-cli call -i '{"queries": ["18.236.120/0001-58", "Nubank"]}' -o`
   — real end-to-end smoke test from Apify's platform (different egress
   IP than this VPS; the space-encoding bug above was already caught and
   fixed from this VPS, but re-confirming from Apify's own network
   before monetizing costs nothing and matches the rigor `pncp-lookup`
   applied at this same step).
3. Set pay-per-event pricing (`pricingInfos`, event `institution-record`
   at $0.01, per `.actor/pay_per_event.json`) and publish (`isPublic:
   true`, categories Business + Lead generation or similar) via the
   Apify REST API `PUT /v2/acts/{actorId}` — the exact call pattern
   already proven twice (`cnpj-lookup` ADR-007, `pncp-lookup` ADR-011).
4. Optional: generate and upload a Store logo (non-blocking, same as
   `pncp-lookup`'s outstanding optional step).
5. Report real results (impressions, runs, revenue) into `BUSINESS.md`
   once real usage exists — do not fabricate numbers before they exist.

# Actor input Schema

## `queries` (type: `array`):

One per line/item. A value that cleans down to 8 or 14 alphanumeric characters is treated as a CNPJ (root or full) and matched exactly; anything else is treated as a case-insensitive name-fragment search against the institution's legal name, short name, and trade name.

## `maxResultsPerQuery` (type: `integer`):

Caps how many matched institutions a single name-fragment query can return (and therefore charge for) - a broad term like "banco" matches hundreds of entities. Does not affect CNPJ queries, which always resolve to at most one record. Max 50.

## `maxConcurrency` (type: `integer`):

How many queries to resolve in parallel. Kept low by default to stay well within the BCB Olinda API's unpublished but real request limits. Max 10.

## Actor input object example

```json
{
  "queries": [
    "18.236.120/0001-58",
    "Nubank"
  ],
  "maxResultsPerQuery": 20,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "18.236.120/0001-58",
        "Nubank"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jgoia/bcb-institutions-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "queries": [
        "18.236.120/0001-58",
        "Nubank",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("jgoia/bcb-institutions-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "18.236.120/0001-58",
    "Nubank"
  ]
}' |
apify call jgoia/bcb-institutions-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jgoia/bcb-institutions-lookup"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ebdrgfKIthLWicOft/builds/drIhtElFfQ2enecJQ/openapi.json
