# Contratos a Vencer — PNCP (`kman-br/contratos-a-vencer`) Actor

Contratos públicos do PNCP cuja vigência termina entre hoje+X e hoje+Y dias, com fornecedor atual (CNPJ) e valor. Base incremental local, filtros por UF, objeto, valor, órgão e fornecedor.

- **URL**: https://apify.com/kman-br/contratos-a-vencer.md
- **Developed by:** [Kman](https://apify.com/kman-br) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 contrato retornados

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

## Contratos a Vencer — PNCP

### Português

Encontre contratos públicos **que vencem nos próximos X a Y dias** (padrão: 30 a 180), com o fornecedor atual (CNPJ), o valor e o link oficial. Contrato prestes a vencer vira aditivo ou nova licitação: é o melhor gatilho de venda para equipes comerciais B2G, consultorias e para o próprio fornecedor.

A API do PNCP só filtra por **data de publicação**, não por fim de vigência. Este Actor mantém uma **base local incremental** (key-value store nomeado) e filtra o vencimento sobre ela, sem chamar a API para recalcular `dias_para_vencer`.

#### Como funciona

1. **Incremental:** a cada run lê só o que o PNCP publicou ou atualizou desde o último run.
2. **Backfill:** monta o histórico dos últimos meses (até 60) aos poucos, do mais novo ao mais antigo, até `maxPaginasPorRun` páginas por run; o run seguinte continua de onde parou. O `RUN_SUMMARY` mostra a cobertura.
3. **Filtro local:** janela de vencimento, UF, palavras-chave no objeto, valor mínimo, órgão e fornecedor.
4. Com `cnpjOrgao`, o backfill usa o filtro nativo da API e fica rápido e barato (bom para começar).

#### Cobrança

Só itens com `status: ok` são cobrados (evento `contrato-retornado`). Itens `parcial` (algum campo central ausente na fonte) aparecem no dataset, com warning, mas **não são cobrados**. Itens `erro` (falha da fonte ou input inválido) também não são cobrados.

#### Somente novidades (`somenteNovidades`)

O padrão é `false`. Com `false`, cada run devolve a janela inteira de contratos, e **cada contrato é cobrado de novo a cada run**. Isso serve para uma consulta pontual, ou para o teste diário da Apify, que precisa sempre trazer itens.

Com `true`, o Actor guarda no key-value store da base (`baseStoreName`) só o `numeroControlePNCP` e um hash de cada contrato já entregue, separado por filtro. Nos runs seguintes com o mesmo filtro:

- contrato já entregue e **sem mudança** não sai na saída e **não é cobrado** de novo;
- contrato **novo** sai e é cobrado;
- contrato **alterado** (valor, vigência, fornecedor ou qualquer outro campo que a fonte muda) volta na saída e é cobrado de novo.

Mudar qualquer filtro (UF, palavras-chave, valor mínimo, órgão etc.) cria outro conjunto de contratos entregues, então o primeiro run com o filtro novo devolve a janela inteira.

**Recomendação:** use `true` para monitoramento agendado (run diário ou semanal sobre o mesmo filtro), para pagar só pelo que é novo. Use `false` para uma consulta avulsa, quando você quer a lista completa.

#### Entrada

| Campo | Padrão | Descrição |
|---|---|---|
| `venceEmDiasMin` / `venceEmDiasMax` | 30 / 180 | janela de vencimento, em dias a partir de hoje (Brasília) |
| `ufs` | todas | UFs da unidade compradora |
| `palavrasChave` | — | qualquer uma, no objeto, sem diferenciar acento/caixa |
| `valorMin` | — | valor global mínimo (R$) |
| `cnpjOrgao` | UFPB (24098477000110) | 14 dígitos; o padrão garante resultados em run sem configuração. Apague para busca nacional |
| `cnpjFornecedor` | — | 14 dígitos. CPF (11) é recusado |
| `maxItems` | 50 | limite de contratos retornados (só os de status `ok` são cobrados) |
| `somenteNovidades` | false | só contratos novos/alterados desde o último run com o mesmo filtro |
| `fornecedorPessoaFisica` | omitir | `omitir` (mantém o contrato sem CPF/nome) ou `excluir` |
| `verificarAditivos` | false | consulta termos aditivos (1 chamada por contrato) |
| `baseStoreName` | contratos-a-vencer-base | nome do key-value store da base |
| `mesesBackfill`, `maxPaginasPorRun`, `intervaloMs` | 3, 300, 1200 | avançado |

#### Saída (um item por contrato)

`numeroControlePNCP`, `orgao_cnpj`, `orgao_nome`, `uf`, `municipio`, `fornecedor_tipo`, `fornecedor_cnpj`, `fornecedor_nome`, `objeto`, `tipo_contrato` (inclui empenhos), `numero_contrato`, `valor_global`, `valor_acumulado`, `data_assinatura`, `data_vigencia_inicio`, `data_vigencia_fim`, `dias_para_vencer`, `data_publicacao_pncp`, `teve_aditivo`, `link_pncp`, e em **todo** item: `source_url` (JSON oficial do contrato), `retrieved_at` (ISO 8601 com fuso), `warnings[]`, `status` (`ok` | `parcial` | `erro`), `error_message`. Campo ausente na fonte = `null` + warning; nada é inventado.

O key-value store padrão recebe `RUN_SUMMARY`: registros lidos, gravados (novos/alterados), páginas com erro, hash da fonte e cobertura do backfill.

#### Erros

Falha da fonte (429, 5xx, timeout) é repetida com espera e depois vira item `status: erro` e `RUN_SUMMARY.status = parcial`. Se a fonte não respondeu nenhuma vez, o run **falha** (nunca "verde" e vazio). Itens `erro` não são cobrados.

#### LGPD

Só dados públicos de contratos. Fornecedor pessoa física (CPF): o CPF e o nome nunca entram na base nem na saída (modo `omitir`) ou o contrato é removido (`excluir`). O campo `usuarioNome` da fonte, dados de subcontratada e qualquer enriquecimento externo não são usados. Atenção: CPF embutido no nome é sempre removido, e razão social de MEI/empresário individual (`<8 dígitos> NOME` ou só o nome de pessoa, sem termo empresarial) é mascarada (`62.265.801 O*** M*** T***`, `J*** D*** S*** ME`); o CNPJ é mantido. É uma heurística (a fonte não traz porte): na dúvida, mascara.

#### Limitações

Contratos anteriores à adoção do PNCP (Lei 14.133) podem não estar na fonte. A base guarda só contratos ainda vigentes. O primeiro run nacional leva vários runs de backfill.

### English

Find Brazilian public contracts **expiring in the next X–Y days** (default 30–180) from PNCP, the national procurement portal, with the current supplier (CNPJ), value and an official source link. The PNCP API only filters by *publication* date, so the Actor keeps an incremental local base in a named key-value store, backfills history in slices (resuming across runs) and filters `data_vigencia_fim` locally.

**Billing:** only items with `status: ok` are charged (event `contrato-retornado`). `parcial` items (a core field missing in the source) appear in the dataset with a warning but are **not charged**; `erro` items are not charged either.

| Input | Default | Description |
|---|---|---|
| `somenteNovidades` | false | only new or changed contracts since the last run with the same filter (see below) |

#### Only new (`somenteNovidades`)

The default is `false`. With `false`, every run returns the full window of contracts, and **each contract is charged again on every run**. This suits a one-off query, or the Apify daily test run, which must always return items.

With `true`, the Actor keeps in the base key-value store (`baseStoreName`) only the `numeroControlePNCP` and a hash of each contract already delivered, separately for each filter. On later runs with the same filter:

- a contract already delivered and **unchanged** is left out of the output and is **not charged** again;
- a **new** contract is returned and charged;
- a **changed** contract (value, validity, supplier or any other field the source changes) is returned again and charged again.

Changing any filter (state, keywords, minimum value, agency, etc.) starts a new set of delivered contracts, so the first run with the new filter returns the full window.

**Recommendation:** use `true` for scheduled monitoring (a daily or weekly run on the same filter), so you pay only for what is new. Use `false` for a one-off query, when you want the complete list.

Every item has `source_url`, `retrieved_at` (ISO 8601 with offset), `warnings[]` and `status` (`ok` | `parcial` | `erro`). Missing source fields are `null` plus a warning. Source failures never produce a silent empty green run: they become `erro` items and `RUN_SUMMARY.status`, and the run fails if the source never answered. Natural-person suppliers (CPF) are omitted by default (or the contract is excluded); the source's user name is never exposed; no enrichment. Use `cnpjOrgao` for a fast, cheap start; the default key-value store `RUN_SUMMARY` reports read/written counts, failed pages, source hash and backfill coverage.

# Actor input Schema

## `venceEmDiasMin` (type: `integer`):

Início da janela: contratos cuja vigência termina daqui a pelo menos N dias.

## `venceEmDiasMax` (type: `integer`):

Fim da janela: contratos cuja vigência termina daqui a no máximo N dias.

## `ufs` (type: `array`):

Estados da unidade compradora. Vazio = todo o Brasil.

## `palavrasChave` (type: `array`):

Retorna contratos cujo objeto contenha QUALQUER uma das palavras (sem diferenciar acentos e maiúsculas).

## `valorMin` (type: `integer`):

Ignora contratos com valor global menor que este.

## `cnpjOrgao` (type: `string`):

14 dígitos (pontuação é ignorada). Padrão: Universidade Federal da Paraíba, órgão com muitos contratos, para que o run sem configuração sempre traga itens. Apague o campo para a busca nacional (o backfill leva vários runs). Ativa o modo por órgão: o backfill usa o filtro da própria API.

## `cnpjFornecedor` (type: `string`):

14 dígitos. Busca por CPF (11 dígitos) não é suportada.

## `maxItems` (type: `integer`):

Para ao atingir este número de contratos retornados (cada um é um evento cobrado).

## `somenteNovidades` (type: `boolean`):

Se ligado, retorna apenas contratos novos ou alterados desde o último run com o mesmo filtro (use em execuções agendadas para não pagar duas vezes pelo mesmo contrato). Desligado por padrão: todo run devolve a lista inteira da janela.

## `fornecedorPessoaFisica` (type: `string`):

LGPD. omitir = mantém o contrato sem CPF nem nome do fornecedor; excluir = remove o contrato.

## `verificarAditivos` (type: `boolean`):

Consulta os termos aditivos de cada contrato retornado (1 chamada à fonte por contrato).

## `baseStoreName` (type: `string`):

Key-value store nomeado que guarda a base incremental. Use o mesmo nome entre runs para não refazer o backfill.

## `mesesBackfill` (type: `integer`):

Quanto histórico de publicação carregar na base (máx. 60). Padrão 3 meses: basta para a janela padrão e roda em poucos minutos. Contratos publicados antes disso ficam de fora até aumentar o valor.

## `maxPaginasPorRun` (type: `integer`):

Páginas de 500 registros lidas por run para montar a base; o backfill continua de onde parou no run seguinte.

## `intervaloMs` (type: `integer`):

Ritmo mínimo entre chamadas ao PNCP, que limita rajadas (HTTP 429).

## Actor input object example

```json
{
  "venceEmDiasMin": 30,
  "venceEmDiasMax": 180,
  "cnpjOrgao": "24098477000110",
  "maxItems": 50,
  "somenteNovidades": false,
  "fornecedorPessoaFisica": "omitir",
  "verificarAditivos": false,
  "baseStoreName": "contratos-a-vencer-base",
  "mesesBackfill": 3,
  "maxPaginasPorRun": 300,
  "intervaloMs": 1200
}
```

# Actor output Schema

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

Itens do dataset padrão.

# 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 = {
    "cnpjOrgao": "24098477000110"
};

// Run the Actor and wait for it to finish
const run = await client.actor("kman-br/contratos-a-vencer").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 = { "cnpjOrgao": "24098477000110" }

# Run the Actor and wait for it to finish
run = client.actor("kman-br/contratos-a-vencer").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 '{
  "cnpjOrgao": "24098477000110"
}' |
apify call kman-br/contratos-a-vencer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kman-br/contratos-a-vencer"
        }
    }
}
```

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/J2r9t0waFgD5QFjcC/builds/a9p4NSRJdKwgTT05S/openapi.json
