# Colombia SECOP II Public Contracts - Per Contract (`nexgensignal/co-secop-contract-records`) Actor

Colombia SECOP II public contracts (datos.gov.co) as clean per-record data by signing year - entity, contract id, status, type, modality, dates, value, awarded organisation. Organisations only (natural-person providers and 18 person/bank fields dropped). CC BY-SA 4.0. $0.05 per record.

- **URL**: https://apify.com/nexgensignal/co-secop-contract-records.md
- **Developed by:** [NexGen Signal](https://apify.com/nexgensignal) (community)
- **Categories:** Business, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $33.50 / 1,000 secop contract records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## Colombia SECOP II Public Contracts - Per Contract

Colombia's public contracts from **SECOP II** (Sistema Electronico de Contratacion Publica), operated by
Colombia Compra Eficiente, as clean, per-contract records - for the signing year you choose, **organisations
only**. Entity, department, contract id, status, type, modality, dates, value, and the awarded organisation.

### What one record represents

The source is the SECOP II *Contratos Electronicos* dataset on the national open-data portal
`datos.gov.co` (Socrata resource `jbjy-vk9h`). Each record is **one electronic contract**: the contracting
entity (name, NIT, department, city, order, sector, branch), the purchase-process id, the contract id and
reference, its status, the main category code, the contract type and contracting modality, the signature and
start/end dates, the awarded provider (organisation name, NIT, whether it is a group or an SME), the contract
value and amount paid, the source of funds, and the public process URL. It is the contract-registry view of
Colombian public spending, at the grain of one signed contract.

### Coverage, volume and the year partition

The SECOP II contract master is very large - a live `count(*)` over the resource returns about **5.98 million
rows** across all years, and organisation-only (NIT provider) contracts number about **896,000**. Because a
single run over six million rows would be neither cheap nor useful, the Actor takes a required **year** input
(the contract-signing year, prefilled to the current year) and scopes each run to that year's organisation
contracts. For the current year that is on the order of tens of thousands of contracts; you raise **Maximum
records** to pull the whole year, or lower it to sample. Paging is by Socrata's indexed row id, so a run is a
stable, resumable sweep of the selected year.

### Person data: a two-layer structural exclusion

Colombian contract data carries a great deal of personal data about the individuals around a contract, and this
cell removes all of it, structurally, in two independent layers.

The first layer is a **named-field exclusion**: eighteen columns that identify natural persons or bank accounts -
the legal representative's name, nationality, domicile, identification type, identification number and gender;
the *ordenador del gasto*, supervisor and *ordenador de pago* name, document type and document number; and the
bank name, account type and account number - are **never placed in the query and never delivered**. The run
receipt lists them. A record that somehow carried one would fail a built-in assertion before it could be pushed.

The second layer is a **provider-type gate**. Every SECOP II contract records the provider's document type
(`tipodocproveedor`). Where that is anything other than **NIT** (the organisation tax id) the provider is a
natural person - a *Cedula de Ciudadania*, *Cedula de Extranjeria* or passport holder - and the whole row is
dropped. The filter is applied server-side in the query and re-checked on every row before delivery, so a
natural-person provider is never emitted. Only organisation (NIT) contracts reach the dataset. In a typical
recent year this drops the large majority of rows: the delivered set is the clean, organisation-only subset,
and the awarded-provider field is therefore always a company legal name, never a person.

### Licence

Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). You are free to share and adapt the material for any purpose, including commercially, provided you give appropriate credit AND distribute any derived material under the same CC BY-SA 4.0 licence. ShareAlike: any redistribution or derived dataset built from these records must itself be licensed under CC BY-SA 4.0. The attribution and the ShareAlike obligation ride on every record and are
restated here so any downstream redistribution carries them.

### Who buys this, and how they use it

This cell is bought by teams that need the source's full published set as a typed, keyed table they can hold and
refresh, rather than a page they scrape. Market-intelligence and lead-generation teams use it to size a market
and track who is active in it; analysts and journalists use it to build a longitudinal series that the source's
own portal does not expose; data engineers use it as a clean upstream feed into a warehouse, keyed so it upserts
without duplication. The common thread is that the record grain and the stable key are chosen so the output is a
building block, not a one-off export: you run it on a schedule, keep the delta, and join it to your other
sources on the identifiers it preserves verbatim.

### Field-by-field, and why the grain is what it is

Every field in the record is either a source-native identifier, a source-native attribute, or one of the six
provenance fields (`source`, `source_dataset`, `licence`, `attribution`, `caveat`, `observed_at`) the fleet
attaches to every record. Nothing is derived or inferred beyond the small, documented transforms noted above,
and nothing is dropped silently: the person-handling section spells out exactly which fields are excluded and
why. The grain - one record per the natural unit of the source - is deliberate: it keeps each row independently
meaningful, keeps the key stable across runs so re-running is a cheap upsert rather than a re-import, and lets
you aggregate up to whatever unit you need without having to unpick a pre-joined table. If you need a different
grain, you compose it downstream from these rows; the cell's job is to deliver the atomic, person-safe,
licence-clean records that everything else is built from.

### Reconciling counts honestly

Where the live count differs from any previously published figure, the live measure is the honest one and is what
this listing quotes; sources re-issue and consolidate their data over time, so a figure drifts. The run receipt
always states what was actually delivered and charged and confirms the two agree, so every run is auditable
against itself regardless of what any external index expected.

### Sibling Actors

### Provenance and compliance

Every run reads the door host's `robots.txt` at runtime; the gate result (URL, status, byte length and, where a
policy is served, its SHA-256) is written to the run's `RUN_RECEIPT`. Where the host serves no applicable
robots rule, or redirects its policy to another host, the gate records that (flagged) and proceeds on the
licence, which grants re-use. The endpoint is keyless. The Actor never bypasses a block or fetches through a
mirror, and it reads only the public listing endpoint - never a per-record detail page.

### Data quality and freshness

Numeric columns are delivered as real numbers and every other column as a string or null, so the dataset loads
without a cleaning pass. Delivery is keyed on a stable source identifier, so the data is safe to diff,
deduplicate or upsert. Every run re-reads the live door, so the data is as fresh as the source publishes, and
each record's `observed_at` stamp dates the snapshot. The run's `RUN_RECEIPT` records the source URL and how
many records were delivered and charged, and confirms `charge_equals_delivered`.

### Billing, delivery and joins

Pricing is per record: you are billed only for records the Actor actually delivers, with the charge raised after
each record is pushed (push-then-charge), so a failed or empty run costs nothing. The **Maximum records** cap
bounds every run, so you control spend precisely - sample cheaply, then raise it. Every record is a flat, typed
object keyed on a stable id, so the data loads without a cleaning pass, diffs cleanly between runs, and upserts
into a table you maintain over time; re-running keeps that table current without re-paying for rows you already
hold, and each receipt reconciles delivered against charged. Because the source's own identifiers are preserved
verbatim, the dataset joins cleanly onto other sources keyed on the same identifier.

### Scaling and scheduling

Set **Maximum records** low to sample the shape of the data cheaply, then raise it once the cell fits your use.
The Actor delivers incrementally and streams its source, so memory stays flat regardless of how many records you
request, and you are billed only for what is delivered. Because the source republishes on its own cadence, a
scheduled run keeps a downstream table current: new and changed records upsert over the old ones on the stable
key, and the `observed_at` stamp on every record tells you when each was last seen live. There is no
subscription and no minimum - the per-record price and the record cap together mean the spend on any run is
known in advance and matched exactly to the data you receive.

# Actor input Schema

## `year` (type: `integer`):

Calendar year of contract signature (fecha\_de\_firma). The SECOP II master holds ~6 million rows; this required partition scopes a run to one year of organisation (NIT) contracts.

## `maxRecords` (type: `integer`):

Maximum records delivered and billed. You are billed only for records actually delivered.

## Actor input object example

```json
{
  "year": 2026,
  "maxRecords": 500
}
```

# Actor output Schema

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

The delivered Colombia SECOP II contract record.

# 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 = {
    "year": 2026,
    "maxRecords": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexgensignal/co-secop-contract-records").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 = {
    "year": 2026,
    "maxRecords": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("nexgensignal/co-secop-contract-records").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 '{
  "year": 2026,
  "maxRecords": 500
}' |
apify call nexgensignal/co-secop-contract-records --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nexgensignal/co-secop-contract-records"
        }
    }
}

```

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/8qrbsin2Cv1MdHMIr/builds/PbUgfBSmdDkGggxJm/openapi.json
