AusTender Contract Expiry & Recompete Monitor
Pricing
from $15.00 / 1,000 recompete signal or contract changes
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
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 = 186this 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
| Signal | Meaning |
|---|---|
ENTERED_365_DAY_RECOMPETE_WINDOW … ENTERED_30_DAY_RECOMPETE_WINDOW | The contract crossed into one of your windows. One event name per configured threshold. |
NEW_MATCH | A 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_SHORTENED | The published end date moved, and which way. |
VALUE_CHANGED | The published contract value or currency moved. |
SUPPLIER_CHANGED | The awarded supplier, their ABN or their country changed. |
AGENCY_CHANGED | The procuring entity or its ABN changed. |
STATUS_CHANGED | active, cancelled, pending or terminated. |
UNSPSC_CHANGED | The category codes changed. |
PROCUREMENT_METHOD_CHANGED | Open or limited tender, or the limited-tender exemption flag. |
TITLE_CHANGED, DESCRIPTION_CHANGED, CONTRACT_START_CHANGED | The remaining published fields. |
CONTRACT_EXPIRED | The contract reached its published end date. Fires once, and re-arms if an extension puts it back in the future. |
NOT_FOUND / FOUND | A 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
| Workflow | Starting input | First run and later runs |
|---|---|---|
| Contract watchlist | Replace 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 expiry | agencyNames: ["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 ABN | Replace 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 read | Requests | Contracts seen | Downloaded | Wall time |
|---|---|---|---|---|
| next 90 days | 148 | 8,603 | 27 MB | 8 min |
| next 180 days | 310 | 16,874 | 56 MB | 18 min |
| next 365 days | 687 | 37,229 | 124 MB | 40 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.
| Event | Price | Charged when |
|---|---|---|
monitor-run | $0.100 | Once after at least one conclusive contract lookup or date window, including a verified quiet check. |
market-setup | $1.490 | Once 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.003 | Each complete baseline or snapshot written to the dataset. |
contract-signal | $0.015 | Each verified change record written, even if it contains several change types. |
Demo with two exact contracts: $0.10 + 2 x $0.003 = $0.106100-contract exact baseline: $0.10 + 100 x $0.003 = $0.40Quiet daily run: $0.10 (30 days = $3.00)A day with 10 change records: $0.10 + 10 x $0.015 = $0.25Market setup with 100 baselines: $1.49 + $0.10 + 100 x $0.003 = $1.89365-day discovery over three runs,50 first-run baselines and50 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
| Situation | status | What happens to your baseline |
|---|---|---|
| A bounded date search with no matching contracts | window reported empty | untouched |
| The contract exists and nothing meaningful changed | SUCCESS, no change_types | updated |
| AusTender confirms there is no such Contract Notice | NOT_FOUND | updated (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 identity | SOURCE_FAILED | untouched |
| Most of the portfolio appears to vanish or lose its dates at once | PARTIAL | untouched - treated as a source incident, not a mass removal |
| An amendment's position in the chain cannot be read | SOURCE_FAILED | untouched - 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_endkeeps the source instant verbatim andcontract_end_dateis 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, andGET /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
linkskey is the only end-of-results signal, and off-hostnextlinks are refused. - Retries: at most 3 attempts for transport errors, 429 and retryable 5xx, with
exponential backoff, jitter, and
Retry-Afterhonoured 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
fingerprintover the semantic fields only, plusobserved_atandschema_version, so rows are append-only observations that join to their own history.