Cordoba Tenders Scraper & Monitor avatar

Cordoba Tenders Scraper & Monitor

Pricing

Pay per event

Go to Apify Store
Cordoba Tenders Scraper & Monitor

Cordoba Tenders Scraper & Monitor

Extracts active public tenders (licitaciones) from the Province of Cordoba, Argentina official procurement portal, with organism, dates, status, reference budget per item and contact phone.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Stefano Seggio

Stefano Seggio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 hours ago

Last modified

Share

Cordoba Tenders Scraper & Monitor — Apify Store Overview

Actor: stefano_seggio/cordoba-compras-monitor Actor ID: q9jhMgJRSGjyNbXKA Store URL: https://apify.com/stefano_seggio/cordoba-compras-monitor Version: 2.0


Executive Summary & Business Use Case

Cordoba Tenders Scraper & Monitor extracts every active public tender (licitacion) from the Province of Cordoba, Argentina's official e-procurement portal (webecommerce.cba.gov.ar), the government system where the province publishes its open contracting opportunities. For each tender the actor returns the process number, contracting type, issuing agency, jurisdiction, publication and closing dates, current status, whether the deadline has been extended, the full line-item list with quantity and reference/official budget per item, and the published contact phone — all pulled from a single request per listing page, with no secondary "detail page" fetch required. On top of the raw extraction, the actor ships a delta engine: run it on a schedule with delta mode on and it reports only what actually changed since the previous run — a brand-new tender, a status change, a content amendment (e.g. a deadline extension or a revised budget line), or a tender that has dropped off the active register entirely.

This is a genuinely difficult portal to work with in practice — the source is a stateful ASP.NET postback/ViewState session with a sliding pager window, it serves an incomplete TLS certificate chain that has to be explicitly patched around, and it blocks any non-Argentina, non-residential IP outright. All of that plumbing is solved inside the actor, so the integrator receives a plain structured feed and never has to touch a browser session, a ViewState token, or a proxy configuration edge case.

Three concrete buyers for this data, grounded directly in what the source actually contains: suppliers who sell to Cordoba provincial agencies use it to watch a shortlist of tenders and get notified the moment one is awarded/closed or its deadline is extended, so they stop chasing dead opportunities and never miss a live one; bid consultants and gestores who manage several clients' tender pipelines use the per-run event_type and previousEstado fields to know exactly what changed for which client since the last check, without manually re-reading the portal; and journalists, researchers and transparency/procurement-analytics groups use servicioAdministrativo (issuing agency), tipoContratacion and the per-item presupuestoOficial (official reference budget) to analyze which agencies run the most tenders and at what reference-budget scale. A monetary "deal value" use case beyond the published per-item reference/official budget is not supported — the portal does not publish an awarded contract value, only the pre-award reference budget per line item.

Technical Features & V2 Architecture Highlights

Cross-run delta persistence. When onlyNew is enabled, the actor persists which tender ids (nroCotizacion) it has already seen — together with each one's last-known estado and a content fingerprint — in its own named key-value store that survives between scheduled runs. Each new run diffs the freshly walked listing against that persisted state to decide, per tender, whether it is genuinely new, changed status, amended, or closed.

Real event types (source-specific, not the generic fleet default). This actor's event_type field takes one of five values, confirmed directly in dataset_schema.json: NEW_LISTING (never seen before), STATUS_CHANGE (seen before, estado changed — free to detect since estado is already in every walked row), UPDATED (seen before, same estado, but some other field changed — e.g. a prorroga deadline extension or an amended line-item budget, detected via the sha1 contentHash fingerprint), UNCHANGED (only ever emitted when delta mode is off, i.e. a full non-delta run), and CLOSED (a previously-tracked tender is absent from a complete walk of the active register). Unlike some sibling actors in this fleet, this schema includes all four change categories plus CLOSED; there is no domain-specific event like SANCTION here because Cordoba's portal has no such concept — the four/five-way split above is the complete real set for this actor.

What onlyNew actually does, per its own schema description. onlyNew's input_schema.json description states plainly: when enabled, the actor "persists which tender ids (nroCotizacion) this actor has already returned — and their last-known estado + content fingerprint — in its own named key-value store that survives between scheduled runs, and returns only tenders that are new, changed estado, amended, or closed since a prior run." Critically, this actor's implementation of onlyNew deliberately does not stop pagination early the way some of this fleet's other delta-enabled monitors do. The schema and README both document why: Cordoba's listing is not reliably sorted newest-first end to end — a real, live-verified tender (2026/000033) was found sitting out of both fechaInicio order and nroCotizacion order within the very first page, both in a captured fixture and in a fresh live re-pull. Short-circuiting pagination on a source like that risks silently missing a genuinely new tender. So onlyNew here fetches up to maxItems in full, exactly like a normal run, and only filters the complete result afterward — a correctness-first design choice, explicitly not a pagination-cost optimization.

CLOSED requires a complete walk. A previously-tracked tender absent from this run's fetch is only reported CLOSED when the walk was not truncated by maxItems — a partial walk cannot prove a tender is actually gone rather than simply past where the walk stopped. For a recurring monitor that needs reliable CLOSED detection, maxItems should be set above the real count of active tenders.

Output envelope field count. The dataset schema defines 17 top-level record fields: 10 domain fields (nroCotizacion, tipoContratacion, servicioAdministrativo, jurisdiccion, fechaInicio, fechaFinalizacion, estado, prorroga, items, telefonoContacto) plus 7 standardized integration/envelope fields (record_id, event_type, previousEstado, contentHash, scraped_at, is_new, source_url) shared with this actor's sibling actors in the same portfolio. No generic "18-field" figure is claimed here — this actor's own dataset_schema.json confirms exactly 17 top-level fields, and that is the number worth quoting.

Input Schema & JSON Configuration Example

FieldTypeDefaultDescription
maxItemsinteger200Hard cap on the number of active tenders (licitaciones) returned this run. The source only covers active/open tenders (Licitaciones) - awarded or historical tenders are out of scope for this actor.
proxyConfigurationobject{ "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AR" }Required: webecommerce.cba.gov.ar blocks non-residential-Argentina traffic from the cloud (verified live - ConnectTimeoutError from Apify's datacenter IPs). Defaults to Residential + Argentina; do not switch to datacenter.
onlyNewbooleanfalseWhen enabled, persists which tender ids (nroCotizacion) this actor has already returned - and their last-known estado + content fingerprint - in its own named key-value store that survives between scheduled runs, and returns only tenders that are new, changed estado, amended, or closed since a prior run. IMPORTANT: this source's listing is NOT reliably sorted newest-first end to end (verified live - a real tender can appear out of order relative to both its own Fecha Inicio and its own nroCotizacion, within the very first page), so onlyNew filters AFTER a full fetch (up to maxItems) rather than stopping pagination early like this fleet's other delta-enabled actors. It is correct, just not a pagination-cost optimization. Independent of dateRange. Recommended for recurring monitoring; leave off for a full one-off extraction.
eventTypesarray["NEW_LISTING", "STATUS_CHANGE", "UPDATED", "CLOSED"]Which kinds of change to deliver when 'Delta mode' is on (ignored, everything delivered, when it is off). NEW_LISTING = never seen before. STATUS_CHANGE = seen before, estado changed - free to detect. UPDATED = seen before, same estado, a field changed (a prorroga granted, an amended item/budget). CLOSED = a previously-active tender is absent from this run's fetch - only computed when maxItems does not truncate the walk (a partial walk cannot prove absence).
dateRangestring(none)Optionally restrict results to tenders whose Fecha Inicio (publication date) falls within this window. Independent of onlyNew - this is the source's own declared publication timestamp, not independently cross-verified against when a tender actually first appeared on the portal. Enum: "24h", "7d", "30d".

JSON configuration example (a recurring delta-monitoring setup, narrowed to only new tenders and status changes, filtered to the last 7 days):

{
"maxItems": 300,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "AR"
},
"onlyNew": true,
"eventTypes": ["NEW_LISTING", "STATUS_CHANGE"],
"dateRange": "7d"
}

A minimal, valid full-extraction example (all fields except maxItems fall back to their schema defaults):

{ "maxItems": 200 }

Output Dataset Sample & Data Dictionary

FieldTypeDescription
nroCotizacionstringTender process number, e.g. 2026/000091
tipoContratacionstringContract type
servicioAdministrativostringIssuing agency
jurisdiccionstringJurisdiction
fechaIniciostringPublication date
fechaFinalizacionstringClosing/opening date and time
estadostringStatus
prorrogabooleanWhether the deadline has been extended
itemsarray of objectsLine items with quantity and reference/official budget, extracted inline - no extra request needed. Each item: renglon (string), cantidad (string), precioReferencia (string), presupuestoOficial (string)
telefonoContactostring | nullContact phone
record_idstringSame value as nroCotizacion - the natural unique id for this tender
event_typestringNEW_LISTING (never seen before), STATUS_CHANGE (estado changed since last seen), UPDATED (a field changed, same estado), UNCHANGED (only when Delta mode is off) or CLOSED (no longer active)
previousEstadostring | nullSet only when event_type=STATUS_CHANGE: the estado this record_id was last seen under
scraped_atstringISO-8601 timestamp of this run's extraction, same value for every record from one run
is_newbooleanTrue if this tender's id was not in the persisted seen-set when this run started
contentHashstringsha1 fingerprint of this record's changeable fields - used to detect UPDATED between runs
source_urlstringThe general listing page - this portal has no stable per-tender deep link (session/ViewState-bound postback UI)

Sample dataset record:

{
"nroCotizacion": "2026/000142",
"tipoContratacion": "Licitacion - Soporte Digital",
"servicioAdministrativo": "Ministerio de Salud",
"jurisdiccion": "Provincia de Cordoba",
"fechaInicio": "01/09/2026 10:15",
"fechaFinalizacion": "22/09/2026 12:00",
"estado": "EN PROCESO",
"prorroga": false,
"items": [
{
"renglon": "1",
"cantidad": "500",
"precioReferencia": "1250.00",
"presupuestoOficial": "625000.00"
},
{
"renglon": "2",
"cantidad": "120",
"precioReferencia": "3400.00",
"presupuestoOficial": "408000.00"
}
],
"telefonoContacto": "0351-4341300",
"record_id": "2026/000142",
"event_type": "NEW_LISTING",
"previousEstado": null,
"scraped_at": "2026-09-08T14:32:07.000Z",
"is_new": true,
"contentHash": "8f2a6c1e0d4b7a9c3e5f1a2b4c6d8e0f12345678",
"source_url": "https://webecommerce.cba.gov.ar/Compras/Compras.aspx"
}

Multi-language Integration Snippets

cURL

curl "https://api.apify.com/v2/acts/stefano_seggio~cordoba-compras-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"maxItems": 300,
"onlyNew": true,
"eventTypes": ["NEW_LISTING", "STATUS_CHANGE", "UPDATED", "CLOSED"],
"dateRange": "7d"
}'

Python (apify-client)

from apify_client import ApifyClient
client = ApifyClient(token="YOUR_APIFY_TOKEN")
run_input = {
"maxItems": 300,
"onlyNew": True,
"eventTypes": ["NEW_LISTING", "STATUS_CHANGE", "UPDATED", "CLOSED"],
"dateRange": "7d",
}
run = client.actor("stefano_seggio/cordoba-compras-monitor").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(f"[{item['event_type']}] {item['nroCotizacion']} - {item['servicioAdministrativo']} - {item['estado']}")

Node.js (apify-client)

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const runInput = {
maxItems: 300,
onlyNew: true,
eventTypes: ['NEW_LISTING', 'STATUS_CHANGE', 'UPDATED', 'CLOSED'],
dateRange: '7d',
};
const run = await client.actor('stefano_seggio/cordoba-compras-monitor').call(runInput);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
console.log(`[${item.event_type}] ${item.nroCotizacion} - ${item.servicioAdministrativo} - ${item.estado}`);
}

(Equivalent direct API call for all three: POST https://api.apify.com/v2/acts/stefano_seggio~cordoba-compras-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN)

Pricing Model Explanation

This actor uses Apify's Pay-Per-Event model with three named events:

EventPriceWhen it fires, for this actor
result$0.003 per recordCharged for NEW_LISTING, STATUS_CHANGE, or UPDATED records — every one of these already carries the full tender detail (organism, dates, status, per-item budgets, contact phone) inline, pulled from the listing walk with no extra request needed.
result-summary$0.001 per recordCharged only for CLOSED records — a CLOSED event is a derived absence signal (a previously-tracked tender no longer appears in a complete walk), so there is no fresh tender content to fetch or deliver, and it bills at the lower tier accordingly.
apify-actor-start$0.00005Charged once per run, regardless of how many tenders are returned.

The two-tier split exists specifically because CLOSED records carry materially less new information than the other three event types: NEW_LISTING/STATUS_CHANGE/UPDATED all deliver a freshly walked, fully detailed tender row, while CLOSED is inferred purely from a tender's absence and produces no new content beyond the fact of closure — hence result vs. the cheaper result-summary.

Because onlyNew (delta mode) is a filter on what gets created as a dataset record, not a discount on records that are created: an unchanged tender (estado and content identical to the last run) is simply never emitted as a NEW_LISTING/STATUS_CHANGE/UPDATED/CLOSED record when delta mode is on, so it is never billed at all — not billed at $0, it is not turned into a chargeable event in the first place. Only genuine changes generate cost. On a full non-delta run, every fetched tender is returned as UNCHANGED or another applicable event type and billed at the result rate.

A daily recurring monitor that finds around 5 changes a day across the active register costs roughly $0.02/day (about $0.45/month), plus the negligible per-run apify-actor-start charge.


Sources: .actor/actor.json, .actor/input_schema.json, .actor/dataset_schema.json, .actor/output_schema.json, README.md, CHANGELOG.md, AGENTS.md — all read directly from the actor's repository at C:\Users\Stef\apify-portfolio\cordoba-compras-monitor.