AusTender Contract Expiry & Recompete Monitor avatar

AusTender Contract Expiry & Recompete Monitor

Pricing

from $15.00 / 1,000 recompete signal or contract changes

Go to Apify Store
AusTender Contract Expiry & Recompete Monitor

AusTender Contract Expiry & Recompete Monitor

Track AusTender contract expiry, extensions and supplier changes. Monitor Australian Government contracts by CN ID, agency or supplier, with persistent baselines and 365/180/90/60/30-day recompete alerts. Export changes as JSON, CSV or an API feed.

Pricing

from $15.00 / 1,000 recompete signal or contract changes

Rating

0.0

(0)

Developer

Vadim Bezrukov

Vadim Bezrukov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Monitor Australian Government contract expiry on AusTender and get verified alerts for renewal windows, extensions, value changes and supplier changes. Use exact Contract Notice IDs or discover a portfolio by supplier, agency, UNSPSC category and value. Export baseline records and subsequent changes as JSON, CSV or an API feed for a CRM, bid pipeline or procurement dashboard.

A persistent baseline makes daily monitoring useful: changesOnly returns the changes since your previous run. A quiet check costs $0.10 and writes no rows. Market discovery additionally requires a $1.49 setup per monitor key and filter; exact-ID watches have no setup fee. See pricing for result charges.

What a recompete window is

A recompete window is a countdown, not a prediction.

days_to_expiry = contract_end_date - today (Australian calendar)

You choose the thresholds - 365, 180, 90, 60 and 30 days by default. The first time a watched contract crosses from outside a threshold to inside it, you get one signal:

previous run: days_to_expiry = 186
this run: days_to_expiry = 179 -> ENTERED_180_DAY_RECOMPETE_WINDOW

The end date itself is the last threshold: when a watched contract passes it you get one CONTRACT_EXPIRED signal, on the same once-only rule.

And then never again for that threshold, for as long as the end date stands. The standing position is always in days_to_expiry and recompete_windows, so nothing is hidden - the event is reserved for real movement. If an extension pushes the contract back outside a window, that window can fire again when the calendar catches up, and the extension is linked in the same change record.

A recompete window says a contract is approaching its published end date. It is not a statement that a replacement tender will be run, or when.

What it detects

SignalMeaning
ENTERED_365_DAY_RECOMPETE_WINDOWENTERED_30_DAY_RECOMPETE_WINDOWThe contract crossed into one of your windows. One event name per configured threshold.
NEW_MATCHA contract entered the watched portfolio - a new award, or an amendment that brought it into your filter or your horizon.
CONTRACT_END_CHANGED + CONTRACT_EXTENDED / CONTRACT_SHORTENEDThe published end date moved, and which way.
VALUE_CHANGEDThe published contract value or currency moved.
SUPPLIER_CHANGEDThe awarded supplier, their ABN or their country changed.
AGENCY_CHANGEDThe procuring entity or its ABN changed.
STATUS_CHANGEDactive, cancelled, pending or terminated.
UNSPSC_CHANGEDThe category codes changed.
PROCUREMENT_METHOD_CHANGEDOpen or limited tender, or the limited-tender exemption flag.
TITLE_CHANGED, DESCRIPTION_CHANGED, CONTRACT_START_CHANGEDThe remaining published fields.
CONTRACT_EXPIREDThe contract reached its published end date. Fires once, and re-arms if an extension puts it back in the future.
NOT_FOUND / FOUNDA watched Contract Notice left, or returned to, AusTender.

Every signal is confirmed against the live source before it is emitted. A threshold crossing computed from a stale stored end date is exactly the alert a capture team must not receive.

Who uses it

  • Suppliers to the Australian Government watching their own contracts, so a renewal conversation starts before the incumbent advantage evaporates.
  • Capture and bid teams watching a competitor's or an agency's expiring portfolio, for pre-tender engagement.
  • Procurement-intelligence and market-research teams building an Australian government contract monitor without maintaining state themselves.
  • Developers who want an Australian Government procurement API feed that emits deltas rather than snapshots.

30-second quickstart

The Console prefill checks two exact Contract Notice IDs in snapshotAndChanges mode under the demo monitor key. It reads no market windows and returns verified records on both the first and repeat runs. For a scheduled changes-only watch, use your own monitor key and changesOnly mode.

Watch two contracts you care about:

{ "contracts": ["CN4258099", "CN4265960"] }

Or watch a market:

{
"agencyNames": ["Department of Defence"],
"unspscPrefixes": ["43"],
"minValueAud": 1000000,
"lookaheadDays": 365,
"monitorKey": "defence-it"
}

Run it once to establish the baseline, then schedule it daily. From the second run on, the dataset contains only signals and changes.

Choose your monitoring workflow

WorkflowStarting inputFirst run and later runs
Contract watchlistReplace CN4258099 and CN4265960 with your CN IDs; use snapshotAndChanges.First baseline, then current records and verified changes on every run. No market setup fee.
Defence ICT expiryagencyNames: ["Department of Defence"], unspscPrefixes: ["43"], minValueAud: 1000000, lookaheadDays: 31, recompeteWindowsDays: [30].Near-term baseline, then changes and 30-day crossings in changesOnly mode.
Supplier expiry by ABNReplace Dell Australia's supplierAbns: ["46003855561"] with your supplier's ABN; use lookaheadDays: 90, recompeteWindowsDays: [90, 60, 30].Baseline across agencies, then changes and window crossings in changesOnly mode.

Start each portfolio with its own monitorKey, retain it on later runs, and leave unrelated filter arrays empty. Keep baselineMode: "emitSnapshot" for a visible first result. Market workflows incur setup; see pricing. On quiet market checks the dataset may be empty. Check RUN_SUMMARY for verified coverage and status / error for failed or skipped work before interpreting an empty result. Discovery samples roll forward with the run date; a supplier with no upcoming contracts can legitimately return no matches.

Sample recompete signal

{
"record_type": "CHANGE",
"status": "SUCCESS",
"monitor_key": "defence-it",
"cn_id": "CN4258099",
"title": "3000657415",
"description": "Ammunitions",
"supplier": "RWM ITALIA SPA",
"agency": "Department of Defence",
"contract_end": "2027-03-30T13:00:00Z",
"contract_end_date": "2027-03-31",
"days_to_expiry": 179,
"recompete_windows": [365, 180],
"value_aud": 4337124.0,
"currency": "AUD",
"change_types": ["ENTERED_180_DAY_RECOMPETE_WINDOW"],
"changes": { "recompete_windows_entered": [180] },
"source_url": "https://api.tenders.gov.au/ocds/findById/CN4258099",
"observed_at": "2026-10-03T02:00:00Z"
}

An extension looks like this:

{
"record_type": "CHANGE",
"cn_id": "CN4263346",
"amendment_id": "CN4263346-A2",
"change_types": ["CONTRACT_END_CHANGED", "CONTRACT_EXTENDED", "VALUE_CHANGED"],
"changes": {
"contract_end": { "previous": "2026-10-01T14:00:00Z", "current": "2026-10-07T13:00:00Z" },
"value_aud": { "previous": "32004", "current": "61722" }
}
}

Watch exact contracts

The safest monitoring primitive. Every listed Contract Notice is checked on every run, whatever the market filters say.

{
"contracts": ["CN4258099", "cn4265960", "CN4220252-A1"],
"monitorKey": "our-contracts",
"mode": "changesOnly"
}

Case, spaces, a leading # and a trailing amendment suffix are normalized away, so CN4220252, cn4220252 and CN4220252-A1 are all the same contract - an amendment ID resolves to its parent's whole release chain. A repeated ID is fetched, charged and compared once. Up to 1,000 per monitor key.

Watch a supplier, agency or category

{
"supplierNames": ["Accenture"],
"agencyNames": ["Department of Defence", "Services Australia"],
"unspscPrefixes": ["43", "8111"],
"keywords": ["cloud", "software"],
"minValueAud": 250000,
"lookaheadDays": 365,
"recompeteWindowsDays": [365, 180, 90, 60, 30],
"monitorKey": "defence-cloud"
}

The filter groups combine like this, and this is the only rule to learn:

(supplierNames OR supplierAbns)
AND (agencyNames OR agencyAbns)
AND (any unspscPrefixes)
AND (any keywords)
AND value >= minValueAud

An empty group is not a condition. Names match case-insensitively as substrings; ABNs match exactly.

How the first run differs from every run after it

A market watch has to discover its portfolio before it can monitor it, and the AusTender API only filters by date - so discovery means reading the expiry horizon. That is not cheap. Measured against the live API:

Expiry horizon readRequestsContracts seenDownloadedWall time
next 90 days1488,60327 MB8 min
next 180 days31016,87456 MB18 min
next 365 days68737,229124 MB40 min

So the horizon is walked soonest expiry first, and one run covers at most 180 days of it. A first run aims to deliver the contracts inside every default recompete window (30, 60, 90 and 180 days) in about twenty minutes in the source-only benchmark, and the rest of a 365-day horizon arrives over the next two runs. The summary says so (horizon_capped), and coverage is recorded only for the range that was actually read - so a capped run is a shorter range, never a skipped one.

Every run after discovery reads only the newly uncovered tail of the horizon plus the contracts AusTender says changed since the stored cursor. Runtime depends on source volume and latency. Measured against the live API, a 31-day Defence market watch cost 71 requests and 11 MB to establish, and 13 requests and 2 MB to run again. New awards and newly matching contracts arrive through that second, cheap path: a contract that enters your filter between horizon scans is picked up from the changed-contracts feed, not by re-reading the year. That is what makes a daily schedule cheap, and it is the reason this Actor keeps state.

The same bound protects a monitor that has been paused: reconciliation catches up two weeks at a time, with the regular $0.10 fee on each catch-up run, and the cursor still advances only over what was actually read.

Changing the filter under the same monitor key re-discovers the horizon (the previously covered range was read with the old filter). The contracts already in the portfolio stay in it; use a new monitorKey for a clean portfolio.

Schedule it, and get a webhook when something moves

Schedule the Actor daily or weekly in the Apify console (Actor → Schedules), then add a webhook on Run succeeded pointed at your own endpoint:

{
"eventTypes": ["ACTOR.RUN.SUCCEEDED"],
"requestUrl": "https://hooks.example.com/austender-recompete",
"payloadTemplate": "{\"runId\":{{resource.id}},\"datasetId\":{{resource.defaultDatasetId}}}"
}

Then read only the signals:

GET https://api.apify.com/v2/datasets/{datasetId}/items?view=recompete&clean=true

In changesOnly mode that dataset is empty on a quiet day, so an empty response is itself the "nothing to do" answer.

Pricing

All prices are USD, pay per event, with the same rates on every Apify plan. Actor compute and source transfer are included in these event prices.

EventPriceCharged when
monitor-run$0.100Once after at least one conclusive contract lookup or date window, including a verified quiet check.
market-setup$1.490Once per monitor key and market filter after its first healthy discovery stage. Includes continuation through up to 730 days and later rolling discovery. Exact-ID-only watches have no setup fee.
contract-observed$0.003Each complete baseline or snapshot written to the dataset.
contract-signal$0.015Each verified change record written, even if it contains several change types.
Demo with two exact contracts: $0.10 + 2 x $0.003 = $0.106
100-contract exact baseline: $0.10 + 100 x $0.003 = $0.40
Quiet daily run: $0.10 (30 days = $3.00)
A day with 10 change records: $0.10 + 10 x $0.015 = $0.25
Market setup with 100 baselines: $1.49 + $0.10 + 100 x $0.003 = $1.89
365-day discovery over three runs,
50 first-run baselines and
50 later NEW_MATCH records: $1.49 + 3 x $0.10 + 50 x $0.003 + 50 x $0.015 = $2.69

Setup is charged even when a healthy date range contains no matches for your filter. It is not a guarantee that qualifying contracts exist. Changing market filters or choosing a new monitorKey requires a new setup; increasing only lookaheadDays continues the purchased discovery. Additional runs have the regular run fee. A failed or suspicious discovery stage does not incur setup. Only the first successful run establishes the monitor's initial baseline. Contracts first verified on later runs are NEW_MATCH changes, including those found in a continuation stage or recovered after an unverified first lookup. They use the $0.015 signal price. baselineMode: storeOnly suppresses only the initial baseline; it does not suppress later NEW_MATCH records.

Set the run charge limit to at least $0.115 for an exact watch or $1.605 for a new market watch, plus enough for the records you expect. The Console default is $5.00. Budget is checked before source work. A limit that cannot cover the required fees and one signal produces budget_preflight_blocked: true in RUN_SUMMARY, no source requests and no charges. Individual unfunded records become SKIPPED; their baselines and discovery positions are preserved. At the 1,000-contract ceiling, the maximum record bill is $15.00 when every record is a change: $15.10 including the check, or $16.59 if setup is also due. Apify may lower the requested limit to the account's remaining credits.

Never charged as records: retries, failed or malformed responses, duplicate CN IDs, suppressed unchanged comparisons, rejected input, discarded anomalies, or unfunded rows. A standing NOT_FOUND has no record fee; disappearance of a previously found contract is a verified change and has the signal fee. Source failure for the entire run incurs none of the four events. Internal pages, requests and state writes have no separate customer charge.

Source-only measurements above are development observations. Private hosted verification at 512 MB returned 100 exact records in 35 seconds and built a 31-day filtered portfolio in about three minutes. These are individual runs, not service guarantees or paid-customer revenue measurements.

Failure semantics

These four states are different, and this Actor never collapses them:

NO_MATCH != NO_CHANGE != NOT_FOUND != SOURCE_FAILED
SituationstatusWhat happens to your baseline
A bounded date search with no matching contractswindow reported emptyuntouched
The contract exists and nothing meaningful changedSUCCESS, no change_typesupdated
AusTender confirms there is no such Contract NoticeNOT_FOUNDupdated (and reported as a change if it used to exist)
429 or 5xx after the retry budget, a timeout, malformed JSON, an HTML body behind HTTP 200, or a response missing OCDS identitySOURCE_FAILEDuntouched
Most of the portfolio appears to vanish or lose its dates at oncePARTIALuntouched - treated as a source incident, not a mass removal
An amendment's position in the chain cannot be readSOURCE_FAILEDuntouched - publishing the superseded parent as current would understate an amended contract

AusTender itself reports "no records found" as HTTP 400 with errorCode: 100 rather than as an empty list. This Actor reads that error code: code 100 is an answer, every other 400 is a fault. Getting that backwards is how a monitor reports an empty market as an outage, or a broken request as an empty market.

One failed contract never denies the others their answer, and a failed date slice never advances the incremental cursor past the data it could not read - the next run re-reads it. If a window succeeds but a contract lookup fails, its CN ID is saved for a direct recheck on the next market-watch run, even after the release leaves the last-modified overlap. Pending CN IDs reserve portfolio capacity and are counted as pending_rechecks in RUN_SUMMARY. Invalid contract dates are source failures, and failed checks retain their baseline even after its normal retention period. A run with only invalid CN IDs leaves monitor state untouched. The same cursor rule covers the run charge limit: a run that discovers contracts it cannot fund rows for keeps its discovery position where it was, so those contracts are found again rather than silently skipped.

Data and source limitations

  • Contract Notices only. Not open tenders, not Approaches to Market, not planned procurements, not standing offers, not state or territory portals.
  • Only what AusTender publishes. No company enrichment, no contacts, no AI scoring, no summaries, no predictions about future tenders.
  • Dates are Australian. AusTender publishes contract periods as the UTC instant of Australian local midnight, so contract_end keeps the source instant verbatim and contract_end_date is the Australian calendar date it means. Reading the UTC date of the instant would be one day early.
  • A contract's current state is its highest-numbered amendment. Some amendments are superseded by later ones, including amendments marked cancelled, so this Actor never reads a chain by date order.
  • No modification timestamp exists. The API stamps a release with its own publication date, so incremental reconciliation re-reads a 24-hour overlap and deduplicates by release id.
  • One monitor key watches at most 1,000 contracts. A market watch that matches more says so in an explicit overflow row rather than silently monitoring a different sample each run. Once the portfolio is full the Actor stops scanning the horizon for new contracts it could not admit anyway; everything already watched keeps being monitored.
  • No personal contact data is collected. AusTender publishes agency officer names, emails and phone numbers in parties[].contactPoint; this Actor never reads that object.

Use it from the API or MCP

For a short exact watch, run and read records in one request:

curl -X POST "https://api.apify.com/v2/acts/automa-flow~austender-contract-expiry-monitor/run-sync-get-dataset-items?maxTotalChargeUsd=0.20&timeout=120&clean=true" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"contracts":["CN4258099","CN4265960"],"monitorKey":"api-demo","mode":"snapshotAndChanges"}'

Use asynchronous POST /v2/acts/automa-flow~austender-contract-expiry-monitor/runs for market discovery, which can exceed the synchronous API timeout. Keep the same monitor key on scheduled runs and read RUN_SUMMARY before acting on data.

The run summary - source requests, windows read, failed windows, cursor position, signal tallies and what was charged - is in the key-value store:

GET https://api.apify.com/v2/key-value-stores/{storeId}/records/RUN_SUMMARY

Connect an MCP client using Streamable HTTP and your Apify account (OAuth or a Bearer token). The direct tool endpoint is:

https://mcp.apify.com?tools=automa-flow/austender-contract-expiry-monitor

Example agent request:

Run automa-flow/austender-contract-expiry-monitor for CN4258099 and CN4265960, monitorKey capture-team, mode changesOnly. Establish a baseline if none exists; otherwise show verified changes and cite source_url. Check RUN_SUMMARY and report source failures separately. Do not infer a future tender from an expiry.

The first changesOnly run writes baselines; a quiet repeat may have an empty dataset. AI clients should inspect record_type, status and change_types, then fetch the summary using the returned run/storage identifiers.

Output surfaces: the recompete dataset view for expiry alerts, changes for what moved and by how much, observations for the current record of every watched contract, diagnostics for anything unverified, and RUN_SUMMARY in the key-value store for run health.

Frequently asked questions

How does this differ from a general AusTender scraper?

It stores a comparison baseline for AusTender contracts. After the first run, your dataset can contain only verified changes and threshold crossings, so an Australian government contract monitor does not need to reconstruct history from repeated exports.

Can it find contract renewal opportunities?

It highlights published expiry dates and government contract recompete windows. These are research signals for a bid pipeline, not proof that an agency will issue a replacement tender.

Can I use it as an AusTender supplier monitor?

Yes. Select supplier names or ABNs, optionally combined with agency, category and value filters. Exact CN IDs are always checked independently of filters. The official Australian Government procurement API provides the source records; the Actor adds persistent comparison state and verified change records.

Attribution and status

Data comes from the official AusTender OCDS API (https://api.tenders.gov.au), published by the Australian Government Department of Finance under a Creative Commons Attribution 3.0 Australia (CC BY 3.0 AU) licence. Every dataset row carries source, source_url, source_publisher and source_licence so attribution travels with the data.

This Actor is not affiliated with, endorsed by, or connected to AusTender, the Department of Finance, or the Australian Government. It reads a public API. Nothing it returns is legal, procurement-compliance or financial advice, and a recompete window is not a statement that any tender will be run.

Technical source details

  • Endpoints used: GET /ocds/findById/{cn} for the authoritative amendment chain, GET /ocds/findByDates/contractEnd/{from}/{to} for expiry discovery, and GET /ocds/findByDates/contractLastModified/{from}/{to} for incremental reconciliation. All anonymous, all plain HTTPS.
  • No API key, no login, no cookies, no browser, no proxy, no CAPTCHA solving.
  • Pagination is cursor-based at 100 releases per page; the absence of the links key is the only end-of-results signal, and off-host next links are refused.
  • Retries: at most 3 attempts for transport errors, 429 and retryable 5xx, with exponential backoff, jitter, and Retry-After honoured up to 60 seconds. Deterministic 4xx is never retried. Three requests in flight at most.
  • State: one compact record per monitor key in the Apify key-value store, holding each watched contract's last successful observation, its fingerprint, the recompete windows already emitted, how far the expiry horizon has been discovered, the last-modified cursor and pending CN rechecks. Two runs on one monitor key cannot overlap. Hard crashes keep a fail-closed lock. Stop all runs for that key and reconcile the interrupted dataset and charges before removing its specific lock marker. Automatic restart is disabled; do not blindly resurrect a run that already delivered records.
  • Every row carries a SHA-256 fingerprint over the semantic fields only, plus observed_at and schema_version, so rows are append-only observations that join to their own history.