# Brazil Public Tenders (PNCP) (`homelabmobile/brazil-pncp-tenders`) Actor

Brazilian public tenders and contracts from the official PNCP API: buyer, modality, object, estimated and contract values, dates, state. Date, state, keyword filters. LGPD-safe.

- **URL**: https://apify.com/homelabmobile/brazil-pncp-tenders.md
- **Developed by:** [Home Lab Mobile](https://apify.com/homelabmobile) (community)
- **Categories:** Business, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 1,000 results

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Brazil Public Tenders (PNCP)

Get Brazilian public tenders (licitações, dispensas, inexigibilidades) and signed public contracts as clean JSON, straight from the official
PNCP API (Portal Nacional de Contratações Públicas), the national portal every Brazilian public body must publish to under Lei 14.133/2021.
Each row has the buyer, state and municipality, modality, object, estimated or contract value in BRL, key dates and a link to the PNCP page.
No login, key or account is needed. Natural-person suppliers are removed under the LGPD.

### What it does

- Lists **tenders** published on PNCP in a date window (last N days, or explicit dates), for the procurement modalities you choose.
- Lists **contracts** published in a date window, with supplier company (CNPJ and name), value and term.
- Filters by **state (UF)** and by **keyword** in the object description (case- and accent-insensitive, `|` for alternatives).
- Strips personal data (LGPD): suppliers that are natural persons (CPF) or foreign are returned without ID and name; MEI sole traders
  keep their CNPJ but not their name; those names and any CPF-shaped numbers are removed from description text.
- Returns one result per tender or contract. You pay only for valid results; malformed rows are dropped and not charged.
- Fails loudly: if the API returns nothing or its format changes, the run fails instead of returning an empty or broken dataset.
  A run with zero rows succeeds only when every page was read and the API (plus your filters) really had no matches.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `dataset` | string | no | `tenders` (default) or `contracts`. |
| `daysBack` | integer | no | Publication window from N days before today (Brasília time) to today. Default 3; 0 = today. |
| `dateFrom` / `dateTo` | string | no | Explicit window, `YYYY-MM-DD`. Overrides `daysBack`. Max 365 days. |
| `modalities` | integer\[] | no | Tenders only. PNCP modality codes, default `[6, 4, 8, 9]`. 1 Leilão eletrônico, 2 Diálogo competitivo, 3 Concurso, 4 Concorrência eletrônica, 5 Concorrência presencial, 6 Pregão eletrônico, 7 Pregão presencial, 8 Dispensa, 9 Inexigibilidade, 10 Manifestação de interesse, 11 Pré-qualificação, 12 Credenciamento, 13 Leilão presencial. |
| `state` | string | no | Two-letter UF of the buyer, e.g. `SP`. |
| `keyword` | string | no | Text that must appear in the object or complementary info, e.g. `notebook\|computador`. |
| `maxResults` | integer | no | Stop after this many results (default 100, max 10,000). Caps the cost of a run. |
| `maxPages` | integer | no | Safety cap on API pages read (50 records each). Default 200. |

Examples:

```json
{ "dataset": "tenders", "daysBack": 3, "modalities": [6], "maxResults": 50 }
```

```json
{ "dataset": "contracts", "dateFrom": "2026-10-01", "dateTo": "2026-10-07", "state": "MG", "keyword": "ambulância", "maxResults": 200 }
```

#### Calling from an AI agent (MCP)

This Actor works as a tool through the [Apify MCP server](https://mcp.apify.com). Example tool call:

```json
{ "actor": "homelabmobile/brazil-pncp-tenders", "input": { "dataset": "tenders", "daysBack": 2, "state": "SP", "keyword": "software", "maxResults": 10 } }
```

### Output

One JSON object per tender or contract. Fields that do not apply to the record type are `null`.
Dates from PNCP are Brasília local time without an offset. Tender example (from a real run):

```json
{
    "recordType": "tender",
    "pncpControlNumber": "83021808000182-1-000652/2026",
    "url": "https://pncp.gov.br/app/editais/83021808000182/2026/652",
    "buyerCnpj": "83021808000182",
    "buyerName": "MUNICIPIO DE CHAPECO",
    "buyerSphere": "M",
    "unitName": "Prefeitura Municipal de Chapecó",
    "state": "SC",
    "municipality": "Chapecó",
    "modalityId": 6,
    "modalityName": "Pregão - Eletrônico",
    "status": "Divulgada no PNCP",
    "object": "CONTRATAÇÃO DE EMPRESA PARA FORNECIMENTO DE PLACAS EM CHAPA DE METAL ADESIVADA, COM IMPRESSÃO DIGITAL",
    "estimatedValue": 58915.2,
    "awardedValue": null,
    "currency": "BRL",
    "proposalOpenAt": "2026-10-08T17:15:01",
    "proposalCloseAt": "2026-10-22T08:55:01",
    "publishedAt": "2026-10-09T00:00:12",
    "scrapedAt": "2026-10-14T02:37:12.000Z"
}
```

Contract example (abridged):

```json
{
    "recordType": "contract",
    "pncpControlNumber": "83551549000100-2-000152/2026",
    "tenderControlNumber": "83551549000100-1-000085/2026",
    "url": "https://pncp.gov.br/app/contratos/83551549000100/2026/152",
    "buyerName": "BALNEARIO CAMBORIU CAMARA DE VEREADORES",
    "state": "SC",
    "category": "Serviços",
    "object": "Aquisição de 02(duas) vagas no curso “ O Inventário e a Gestão Patrimonial” ...",
    "contractGlobalValue": 2200,
    "currency": "BRL",
    "supplierType": "PJ",
    "supplierCnpj": "01484706000139",
    "supplierName": "INSTITUTO GAMMA DE ASSESSORIA A ORGAOS PUBLICOS LTDA",
    "supplierRedacted": false,
    "signedDate": "2026-10-09",
    "validFrom": "2026-10-09",
    "validTo": "2026-10-21"
}
```

#### Personal data (LGPD)

| Supplier | `supplierCnpj` | `supplierName` | `supplierRedacted` |
|---|---|---|---|
| Company (PJ) | kept | kept | `false` |
| MEI sole trader (PJ whose registered name contains the owner's CPF, e.g. "OWNER NAME 12345678909") | kept | `null` | `true` |
| Natural person (PF) | `null` (CPF dropped) | `null` | `true` |
| Foreign supplier (PE) or malformed ID | `null` | `null` | `true` |

Subcontractors follow the same rules (`subcontractorCnpj`, `subcontractorName`). Every redacted name, with or without its CPF, is
replaced by `[nome removido]` in `object` and `additionalInfo`, and any CPF-shaped number in those texts becomes `[CPF removido]`.
The PNCP API has no MEI or company-size flag, so a CPF-shaped number in a company name is what identifies an MEI. An MEI registered
under a trade name without a CPF keeps that trade name.
The full field list with descriptions is in the dataset schema. Each run also writes a `RUN_SUMMARY` record (status, counts) to its key-value store.

### Pricing

Pay per event:

| Event | Price | Per 1,000 |
|---|---|---|
| Result (one valid tender or contract) | $0.0025 | $2.50 |

Plus Apify's standard Actor start charge. A typical run of 50 results costs about $0.125.

### Data source and licence

Data comes from the PNCP public consultation API (`https://pncp.gov.br/api/consulta`), operated by the Brazilian federal government (Serpro).
PNCP states: "O PNCP disponibiliza dados para consulta, reutilização e acompanhamento por meio de recursos de dados abertos" (PNCP FAQ).
Lei 14.133/2021, art. 174 §4, requires PNCP to use open data format. The gov.br site footer says its content is published under
Creative Commons Attribution-NoDerivs 3.0 (CC BY-ND 3.0). Attribution: **Fonte: Portal Nacional de Contratações Públicas (PNCP), pncp.gov.br**.
Rows are passed through with field renaming and personal-data removal only; values are not altered.

### Known limits

- Window is by **publication date** on PNCP, not by bid deadline. Records published today may still be arriving.
- The tenders API must be paged per modality; more modalities and longer windows mean more pages and slower runs
  (we read at most 30 pages per minute to stay polite: about 1,500 rows per minute before filters).
- The contracts API has no state parameter, so `state` on contracts is applied after download and reads more pages.
- Keyword filtering happens after download; rare keywords over long windows can hit `maxPages` before `maxResults`.
- Items (lots), results per item, price registration minutes (atas) and documents are not included.
- Natural-person, foreign and MEI supplier names are always redacted (no opt-out); see Personal data above.
- The PNCP API is sometimes slow or returns 5xx; the Actor retries each page up to 5 times with backoff. If a
  page still fails, that query stops (logged as an error) and the others continue; if nothing could be read, the run fails.
- Public, logged-out API only. It does not log in or bypass any access control.

### Support

Open an issue on the Actor's Issues tab. We reply within 2 business days.

# Actor input Schema

## `dataset` (type: `string`):

"tenders" = procurement notices (licitações, dispensas, etc.) published on PNCP in the date window. "contracts" = signed contracts published in the date window, with supplier and value. Default: tenders.

## `daysBack` (type: `integer`):

Relative date window: records published from this many days before today (Brasília time) up to today. 0 = today only. Ignored when dateFrom is set. Example: 3.

## `dateFrom` (type: `string`):

Optional explicit start of the publication date window, YYYY-MM-DD (e.g. 2026-10-01). Overrides daysBack.

## `dateTo` (type: `string`):

Optional explicit end of the publication date window, YYYY-MM-DD (e.g. 2026-10-07). Default: today (Brasília time). The window may span at most 365 days.

## `modalities` (type: `array`):

PNCP modality codes to include when dataset is "tenders". The API pages each modality separately, so fewer codes = faster runs. Codes: 1 Leilão eletrônico, 2 Diálogo competitivo, 3 Concurso, 4 Concorrência eletrônica, 5 Concorrência presencial, 6 Pregão eletrônico, 7 Pregão presencial, 8 Dispensa de licitação, 9 Inexigibilidade, 10 Manifestação de interesse, 11 Pré-qualificação, 12 Credenciamento, 13 Leilão presencial. Default: \[6, 4, 8, 9].

## `state` (type: `string`):

Optional two-letter Brazilian state code of the buying body, e.g. SP, RJ, MG. Leave empty for the whole country. Tenders are filtered by the API; contracts are filtered after download (the contracts API has no state parameter, so state-filtered contract runs read more pages).

## `keyword` (type: `string`):

Optional word or phrase that must appear in the object description or complementary information. Case- and accent-insensitive, e.g. "computador" or "ambulância". Several alternatives can be separated by | (e.g. "notebook|computador"). Filtering happens after download, so rare keywords read more pages.

## `maxResults` (type: `integer`):

Stop after this many results. You are charged per result, so this caps the cost of a run.

## `maxPages` (type: `integer`):

Safety cap on PNCP API pages read in this run (each page is up to 50 records). Matters mainly with keyword or state filters on contracts. Default 200.

## Actor input object example

```json
{
  "dataset": "tenders",
  "daysBack": 3,
  "modalities": [
    6,
    4
  ],
  "maxResults": 50,
  "maxPages": 200
}
```

# Actor output Schema

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

One JSON object per result; fields are described in the dataset schema.

## `runSummary` (type: `string`):

RUN_SUMMARY: status, counts and the silent-failure verdict for this run.

# 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 = {
    "daysBack": 3,
    "modalities": [
        6,
        4
    ],
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("homelabmobile/brazil-pncp-tenders").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 = {
    "daysBack": 3,
    "modalities": [
        6,
        4,
    ],
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("homelabmobile/brazil-pncp-tenders").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 '{
  "daysBack": 3,
  "modalities": [
    6,
    4
  ],
  "maxResults": 50
}' |
apify call homelabmobile/brazil-pncp-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,homelabmobile/brazil-pncp-tenders"
        }
    }
}
```

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/TQNgv2wwd9OEVB3tU/builds/86OsQhx3UoeqBYTlz/openapi.json
