# Supplier Corporate Group Concentration Analyzer (`afkdigital/api26-supplier-group-concentration`) Actor

Evidence-based GLEIF direct/ultimate accounting-consolidation group links and optional supplier-spend concentration, with explicit coverage gaps. Independent of GLEIF.

- **URL**: https://apify.com/afkdigital/api26-supplier-group-concentration.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 1,000 verified supplier-group links

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

## Supplier Corporate Group Concentration Analyzer

**API26 — independent Apify Actor** | Publisher: AFK Digital (independent software publisher; **not affiliated with, endorsed by, or authorized by GLEIF**).

### What this Actor does

Upload your own, authorized supplier-spend CSV or JSON file. For files held in a
private Apify Key-Value Store, choose the same supplierStoreId in the READ-only
storage picker so this limited-permissions Actor can access that store, or use
an authorized signed Apify read-only file URL. Never paste an account API token
into an upload URL. The Actor resolves **a supplier LEI only when provided and validated or when a unique, exact legal name is found**, then verifies the currently published GLEIF Level 2 **direct and ultimate accounting-consolidation** relationship records. It produces source links, exceptions, an auditable review queue, and (only if spend exists) **supplier-spend concentration at the evidenced parent-group level**.

It does **not** repeat CRM data enrichment. It is not a sanctions, credit, beneficial-ownership, procurement exclusion, or supplier risk-decision tool. A missing parent link **never proves independence**.

#### Input — choose exactly one source

- **Supplier file**: Apify input's **file upload** field (UTF-8 CSV or JSON file, up to 4 MB), uploaded to your own Apify Key-Value Store.
- **suppliers**: JSON array of supplier objects. The input form starts with one public Apple Inc. LEI example so the default Store test produces a real supplier record. A charge is possible if GLEIF later publishes a qualifying parent link. **Clear that example before using supplierFile or csvText**.
- **csvText**: CSV content string.

Each row uses these column names (some common snake_case aliases are supported):

| Field | Required | Meaning |
| --- | --- | --- |
| rowId | Recommended | Client row key; duplicate keys are explicitly excluded from spend and billable links |
| supplierName | Unless LEI present | Name of legal supplier; exact normalized GLEIF name required |
| lei | Optional | ISO 17442 LEI; checksum is validated before use |
| spend | Optional | Nonnegative amount, dot decimal, maximum 4 fractional digits; no FX conversion |
| currency | With spend (defaults USD) | Three-letter currency, used as a separate concentration bucket |
| period | With spend (defaults unspecified) | Spend reporting period; **never combined across periods** |
| country | Optional | Two-letter country narrowing exact-name matching |

Run limits: **250 input rows by default; 1–1000 configurable**. Input above limit fails before any GLEIF lookup. No silent truncation. No personal names, bank details, emails, addresses, employee data, vendor contracts, or customer PII needed. Unrecognized columns are not included in output.

#### Downloadable output

- **Dataset** — one record for every uploaded row: matched supplier LEI, parent LEI, relationship scope, verified source URL, reporting exception and reason, method/confidence, spend status, duplicate/ambiguity/error marker.
- **GROUPS** — evidenced ultimate parent clusters and separately flagged direct-parent-only clusters, with supplier membership and eligible spend per period/currency.
- **CONCENTRATION** — grouped spend, known total spend **including unmatched rows**, unresolved spend, coverage %, top 1/top 3 share, and descriptive observed HHI. Metrics do not represent all corporate relationships.
- **REVIEW_QUEUE** — ambiguous / invalid / technical-failure rows. Not charged.
- **SUMMARY** — counts, API usage, event eligibility, executed billings and gaps.

Output data are written only to **the requesting run's own Apify storage**. No shared vendor database; no cross-customer file caching. Apify's own data retention and privacy settings apply. Source GLEIF public records are fetched live per run, with a per-run in-memory cache. Do not publish the input file or run storage yourself if it contains confidential spend.

#### GLEIF relationship meaning & exceptions

Parent = a **published accounting consolidation relationship**, not proof of economic ownership or voting control. The Actor checks relationship status **ACTIVE**, registration status **PUBLISHED**, exact link type and child ID, parent LEI checksum, current validity window, and GLEIF-published **relationship-record URL**. A successfully verified **ultimate parent** is preferred; otherwise a verified direct parent forms a **partial, separately marked** group. Different confidence scopes never silently merge. Direct/ultimate reporting-exception reasons are shown from their official GLEIF record link. Absence of a GLEIF relationship, exceptions, LEI gaps, fuzzy/ambiguous names or an API outage must **not** be interpreted as proof of independence.

#### Pricing (PPE)

**Active Apify PPE event name:** `verified_supplier_group`

**Configured Apify rate:** **$0.015 USD per event** ($1.50 per 100 *distinct* source-proven supplier↔group links), configured through the authenticated Apify API; as shown in the Actor pricing section on Apify Store.

The Actor emits at most one billable event per **unique (supplier LEI, proven parent LEI, group basis)** per run. The same supplier repeated on separate spend rows is free after the first valid group link; duplicate row IDs are explicitly free and excluded from totals. Rows without a source-valid and active accounting-consolidation parent, ambiguous names, reporting exceptions without a verified link, absent or invalid LEIs, API errors and review rows are free. **Important nuance:** Missing spend alone does not prevent a proven *relationship* from being billable; a proven group link is useful even without spend. No run-start charge, no general lookups charge, no summary charge. Configured Apify maximum-charge budgets may reduce actual charges. Events are sent through Apify's official `Actor.charge` once, after the corresponding dataset row is pushed; failed charge calls are not blindly retried.

GLEIF data access is free (CC0). Costs for the publisher are Apify compute/storage/network and development; for a PPE Actor the publisher normally pays underlying platform run usage. **Estimate**, not a rate guarantee: a new supplier may require 1 name/LEI lookup plus up to 2 parent or exception calls, with 1.25 s pacing and retry/backoff. 100 distinct suppliers may take several minutes. Public API may rate limit or fail; no guaranteed latency or response volume. Actual cost/profit must be checked in Apify Billing after test runs. Apify's documented profit model: `80% × event revenue − platform costs` on eligible paid-tier runs.

#### Example input

```json
{
  "suppliers": [
    {"rowId":"sup-001","supplierName":"Bloomberg Finance L.P.","lei":"5493001KJTIIGC8Y1R12","spend":"25000.00","currency":"USD","period":"2026-Q3"},
    {"rowId":"sup-002","supplierName":"Apple Inc.","lei":"HWUPKR0MPOU8FGXBT394","spend":"12500","currency":"USD","period":"2026-Q3"}
  ],
  "maxSuppliers":250,
  "includeConcentration":true
}
```

Bloomberg Finance L.P. is a real **public-data test** for the relationship-record branch; Apple Inc. is a real public-data test of GLEIF's **reporting-exception** branch. Exact relationship contents may change over time.

### Security and limits

- File upload URLs must use `https://api.apify.com/v2/key-value-stores/{id}/records/{key}`; arbitrary addresses, redirects and non-Apify hosts are rejected (SSRF prevention). An Apify run token is sent only to that precise Apify host via an authorization header.
- Uploads are size-limited, UTF-8 checked, bounded row counts. The Actor never logs supplier spend, account tokens or signed URL secrets. For private file uploads, choose the same supplierStoreId with READ-only permission; all other storage remains inaccessible. Unknown input fields and extra CSV columns are discarded.
- GLEIF HTTPS only, no CAPTCHA, logins, bulk extraction or API circumvention, conservative self-throttle (≤48 requests/minute) and retries honoring 429.
- API/API Store is third-party service. Users must have authorization for any private supplier/spend files and comply with applicable privacy/commercial rules.
- GLEIF makes no warranty of completeness, timeliness or correctness; public entities not reporting a parent may still be part of a wider company group.

### Sources checked October 14, 2026

- [GLEIF API](https://www.gleif.org/en/lei-data/gleif-api)
- [GLEIF LEI Data Terms of Use (CC0)](https://www.gleif.org/en/meta/lei-data-terms-of-use)
- [GLEIF Level 2 reporting exceptions](https://www.gleif.org/en/lei-data/access-and-use-lei-data/level-2-data-reporting-exceptions-2-1-format)
- [GLEIF Level 2 relationship records](https://www.gleif.org/en/lei-data/access-and-use-lei-data/level-2-data-relationship-record-rr-cdf-2-1-format)
- [Apify PPE](https://docs.apify.com/actors/publishing/monetize/pay-per-event)

### Maintenance and tests

Run `python3 -m unittest discover -s tests -v`; actual public-GLEIF smoke tests: `python3 tests/live_checks.py` (uses public company data only). For Apify deployment, the Actor includes `.actor/actor.json`, input, output and dataset schemas, and a Dockerfile. For release verification see the project test reports; the current cloud build must pass sample data and billing checks.

#### Apify limited-permissions file access

Fileupload provides an Apify record URL, but limited-permissions Actors cannot
read arbitrary other storages. For private uploaded CSV/JSON, choose the **same
supplierStoreId** using the read-only resourcePicker input field. Alternatively
use a valid Apify-signed record URL containing only the official signature
parameter. The Actor validates the exact host/store identity, and never logs
the signature or access token. The 2026-10-14 live cloud test used a private KVS
selected via resourcePicker. No generic full-account permission requested.

#### Sample output (abbreviated)

A source-confirmed record includes a status, verified supplier LEI, parent-group LEI and the URL of the GLEIF relationship record. Unconfirmed or excluded records include an explicit reason and are not charged.

```json
{
  "recordType": "supplier",
  "rowId": "sup-001",
  "supplierName": "Bloomberg Finance L.P.",
  "supplierLEI": "5493001KJTIIGC8Y1R12",
  "status": "group_verified",
  "groupLEI": "549300RMUDWPHCUQNE66",
  "groupBasis": "ultimate_parent",
  "confidence": "high",
  "evidenceUrls": [
    "https://api.gleif.org/api/v1/lei-records/5493001KJTIIGC8Y1R12/direct-parent-relationship",
    "https://api.gleif.org/api/v1/lei-records/5493001KJTIIGC8Y1R12/ultimate-parent-relationship"
  ],
  "spend": "25000.00",
  "currency": "USD",
  "period": "2026-Q3",
  "billableEvent": true
}
```

**Billing notice:** The event charge is for a unique verified supplier-to-parent relationship, even when no spend is provided. No financial concentration is calculated without valid spend. The same verified supplier-to-parent link is charged once per run, not once per spend row.

**Data caveat:** Public LEI relations do not cover every supplier. The Actor does not make legal, compliance, credit, sanctions or investment decisions.

# Actor input Schema

## `supplierFile` (type: `string`):

Upload an authorized supplier-spend CSV/JSON via Apify file upload. Max 4 MB, UTF-8, max 1,000 rows. File remains in your own Apify storage. Supply this OR suppliers OR csvText, not multiple.

## `suppliers` (type: `array`):

Example Apple LEI is prefilled for a quick test. Clear this sample when uploading a file or entering csvText. Enter an array of {rowId, supplierName, lei?, spend?, period?, currency?, country?}.

## `csvText` (type: `string`):

Optional CSV text with headers rowId,supplierName,lei,spend,period,currency,country. Do not use with supplierFile or suppliers.

## `maxSuppliers` (type: `integer`):

Run fails if input exceeds this limit; never silently truncates rows. Maximum 1000.

## `defaultCurrency` (type: `string`):

Three-letter currency for rows with missing currency; currencies are never converted or merged.

## `defaultPeriod` (type: `string`):

Bucket for spend records without an explicit period. Different periods are never combined.

## `includeConcentration` (type: `boolean`):

Compute observed group spend share, top 1/top 3 and HHI only where valid spend exists; unmatched spend remains in denominator.

## `supplierStoreId` (type: `string`):

If the CSV/JSON upload is in your private Apify Key-Value Store, select that SAME store here. Grants this Actor READ access to only the chosen store. Leave unset for signed read-only file URLs.

## Actor input object example

```json
{
  "suppliers": [
    {
      "rowId": "demo-apple",
      "supplierName": "Apple Inc.",
      "lei": "HWUPKR0MPOU8FGXBT394"
    }
  ],
  "maxSuppliers": 250,
  "defaultCurrency": "USD",
  "defaultPeriod": "unspecified",
  "includeConcentration": true
}
```

# Actor output Schema

## `supplierRecords` (type: `string`):

No description

## `groupTotals` (type: `string`):

No description

## `concentration` (type: `string`):

No description

## `reviewQueue` (type: `string`):

No description

## `runSummary` (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 = {
    "suppliers": [
        {
            "rowId": "demo-apple",
            "supplierName": "Apple Inc.",
            "lei": "HWUPKR0MPOU8FGXBT394"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/api26-supplier-group-concentration").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 = { "suppliers": [{
            "rowId": "demo-apple",
            "supplierName": "Apple Inc.",
            "lei": "HWUPKR0MPOU8FGXBT394",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/api26-supplier-group-concentration").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 '{
  "suppliers": [
    {
      "rowId": "demo-apple",
      "supplierName": "Apple Inc.",
      "lei": "HWUPKR0MPOU8FGXBT394"
    }
  ]
}' |
apify call afkdigital/api26-supplier-group-concentration --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/api26-supplier-group-concentration"
        }
    }
}
```

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/7kgmbEofSDNOZuiyO/builds/lD53QjArlFzDZnOqS/openapi.json
