# CORDIS Consortium Funding & Participant Change Monitor (`thatmike1/cordis-consortium-changes`) Actor

Check nominated EU research projects for organization-level CORDIS contributions, roles and termination flags. Save a baseline and compare future observations. Bills successful organization checks, including unchanged checks.

- **URL**: https://apify.com/thatmike1/cordis-consortium-changes.md
- **Developed by:** [Michal Pšenčík](https://apify.com/thatmike1) (community)
- **Categories:** Education, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 organization checks

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

## CORDIS Consortium Funding & Participant Change Monitor

Check nominated EU research projects for the organizations CORDIS currently lists, with published gross and net EC contributions, country, role and termination flag. Optionally save a watch and compare later observations. Every successfully stored organization check is billable, including baseline and unchanged checks.

This HTTP-only Actor uses the European Commission's public [CORDIS project XML](https://cordis.europa.eu/project/id/101334246/en?format=xml). It covers only direct project organizations, including terminated participants. There is no search, historical revision retrieval, linked-party expansion, contact harvesting, browser, proxy, login or account API.

```json
{
    "projectIds": ["101334246", "604102"],
    "watchName": "research-portfolio",
    "maxRows": 1000,
    "requestIntervalMs": 2000
}
```

`projectIds` contains 1–50 numeric strings (1–12 digits, no leading zeros); repeated IDs are collapsed in input order. Default: `["101334246"]`. Omit `watchName` for untracked checks. Watch names are case-sensitive ASCII letters, digits, `_` or `-`, 1–64 characters. `maxRows` is the stored-row cap, default 1000, range 1–5000. `requestIntervalMs` is idle after each HTTP attempt, default/minimum 1500, maximum 5000. There are no filters or changes-only mode. Schedule repeated checks externally.

Each dataset row includes:

- `organizationId`, `organizationKey`, contextual `project` ID/acronym/status/date-only dates and published project maximum contribution.
- `changeType`: `untracked`, `baseline`, `added`, `updated`, `unchanged` or `no_longer_listed`; `changedFields` plus nullable six-field `before` and `after` objects.
- Organization legal name, country as published (including UK/EL), role, exact EUR decimal strings for gross/net contribution, and nullable termination flag. Missing amounts stay null; zero remains `"0"`. Net is never inferred from gross. Termination does not force amounts to zero.
- `recordId`, nullable `transitionId`, previous delivered observation metadata, CORDIS attribution/source URLs, observation time, complete payload SHA-256, raw source update timestamp and diagnostic warnings.

Published contributions are grant allocation figures, **not verified cash payments**. The source update timestamp has no established timezone and is returned raw with `contentUpdateTimezone: null`. Organization fields alone drive material comparisons; XML formatting, project context and timestamp changes do not. Older/equal timestamps do not suppress corrected values. `no_longer_listed` means absence in a completely parsed current document; it does not establish legal withdrawal, deletion or reclaimed funding.

The default dataset contains useful checks only. Free `SUMMARY` and `OUTPUT` records in the run's default key-value store report failures, delivery counts, remaining projects and partially delivered organization IDs. Remaining IDs are diagnostics, not an immutable pagination cursor. A retry re-fetches the entire nominated project. Complete-document coverage describes parsing, not complete row delivery or global membership coverage. SDK event counts are reported separately from stored rows; settled platform item counts remain null pending platform reconciliation. Unknown stored-row and SDK counts remain null while a push acknowledgement is unresolved.

### Watch storage and delivery

A named KV store `cordis-v1-` followed by 32 SHA-256 hex characters identifies `[Actor identity, watchName]`, scoped to the storage-owning account. It retains up to 50 distinct projects and 200 organizations per project with no automatic eviction. Omitted projects remain stored. Capacity, checksum, namespace or normalization failures stop before source work. Explicitly delete that named store to reset a watch; there is no automatic migration.

First successful tracked observations are a baseline. Comparisons use the last delivered organization values. Missing IDs are delivered first, sorted lexicographically; current IDs follow lexicographically. State advances only after confirmed dataset storage. Partial delivery retains old values for undelivered rows and does not complete the project receipt. Absence observations require a complete valid nonempty document. Failures never clear a consortium.

Delivery is **at least once** across runs. A dataset push followed by failed state persistence may produce another billed comparison in a later run. Same-run recovery retains the original run ID and reconciles actual dataset records before continuing. `recordId` identifies a run/project/organization check. `transitionId` is a content fingerprint: legitimate A→B→A→B and removal/reappearance cycles repeat fingerprints. **Never deduplicate records across runs using transitionId.** No cross-run occurrence identity or guaranteed duplicate suppression is provided.

If a newer run has advanced the watch baseline, an older resumed run discards its plan and stops with `stale_plan`, even if none of its rows were stored. Further resumes cannot revive it. Start a fresh run to fetch and compare against the current baseline. The runner checks the stored watch revision before delivery and immediately before every baseline write; an exact previously committed transaction can be acknowledged without rewriting it.

Serialize runs on each watch. The best-effort read/write/read lease detects some overlaps; ordinary KV storage does not provide a race-safe lock. Different watches are independent.

### Pricing

The two built-in event prices are **$0.00005 per Actor start unit** and **$0.001 per stored organization check**, including unchanged, baseline and absence checks. At one start unit and five organizations, the calculated event total is $0.00505. Fifty projects with five checks each cost $0.25005 under those assumptions. These are calculated event fees, not measured invoices. An all-failed run produces no item fees; the start fee can still apply. Platform pricing and costs require verification before public release. No custom charge events or additional usage pass-through are proposed.

Budget admission uses integer micro-dollars, including a reservation for an unreported platform start and confirmed rows not yet reflected in SDK counters. Exact budgets admit the final affordable item. Limits below a whole micro-dollar round down, and fractional micro-dollar prices round up conservatively.

### Limits and access

One source request at a time; each attempt uses at least 1.5 seconds idle after the preceding response. Limits: 2 MiB decoded bytes/document, 200 direct organizations, XML depth 64, 100,000 elements, 100 attempts/run, 16 MiB decoded bytes/run and 300 seconds plus up to 30 seconds for shutdown persistence. Request admission reserves a full capped document plus sentinel, so a run may stop before consuming the final partial byte allowance. Interrupted requests conservatively consume the outstanding reservation on recovery. All counters, host stops and the original deadline survive a same-run restart.

Attempts time out after at most 25 seconds, including DNS and body reads. At most two retries apply to transport/timeout/5xx failures, with 2 then 5 seconds or longer Retry-After. 403, 429 and detected challenges stop the host immediately; 401/402 remain access failures. Stop after three exhausted project failures. Retry-After diagnostics use milliseconds for scheduling. Redirects are limited to two, same-origin same-project or `/error`; error routes are always failures. DTDs, external entities and XInclude are rejected.

Project **785907** exceeds the body cap and is unsupported in v1. Malformed, empty, oversized, blocked or incomplete source documents produce free failure diagnostics and no participant-removal observations. Public-source availability, completeness and freshness have no guaranteed SLA. The default recent project produces checks even when unchanged; outages can still produce empty runs.

CORDIS robots.txt includes `Disallow: *format=xml`. This Actor uses the disclosed bounded public route under the commissioning policy; it does not bypass challenges or obtain credentials. The account/key-based DET API is excluded. Native [CORDIS exports](https://cordis.europa.eu/about/dataextractions/en), Datalab and free scripts are alternatives.

Output excludes personal contacts, addresses, VAT and embedded publications/objectives. Published legal organization names can still contain personal data; the Actor user is controller for downstream use and must apply appropriate retention and access controls. Research fixtures in the source repository are unaltered public source captures, not sanitized production exports.

# Actor input Schema

## `projectIds` (type: `array`):

1–50 explicit numeric project IDs. Duplicates are checked once; no search or URLs.

## `watchName` (type: `string`):

Omit for untracked checks. Case-sensitive ASCII letters, digits, underscore or hyphen (1–64). Serialize repeat runs on a watch.

## `maxRows` (type: `integer`):

Stored rows, including unchanged checks and absence observations. Partial projects retain undelivered state.

## `requestIntervalMs` (type: `integer`):

One request at a time; idle after each response, including retries and redirects.

## Actor input object example

```json
{
  "projectIds": [
    "101334246"
  ],
  "maxRows": 1000,
  "requestIntervalMs": 1500
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `output` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("thatmike1/cordis-consortium-changes").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("thatmike1/cordis-consortium-changes").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 '{}' |
apify call thatmike1/cordis-consortium-changes --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thatmike1/cordis-consortium-changes"
        }
    }
}
```

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/hJPfgx5nI2vrgzT0g/builds/0fuX5OvmjAkU9AkxB/openapi.json
