# Company Lookup — Website & GLEIF Evidence (`zinin/company-lookup`) Actor

Turn domains, company names, or exact LEIs into evidence-linked website and GLEIF observations with confidence, gaps, billing semantics, and a manual review action. Name matches never claim domain ownership, KYC, a complete tech stack, or buying intent.

- **URL**: https://apify.com/zinin/company-lookup.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.25 / 1,000 company evidence cards

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Company Lookup — Website & GLEIF Evidence

Turn a domain, legal company name, or exact LEI into a review-ready public evidence row. The Actor observes the submitted website, searches or retrieves GLEIF legal-entity records, keeps fuzzy candidates separate from accepted matches, and returns direct sources, freshness, confidence, identity gaps, billing semantics, and a recommended next action.

This is intentionally narrower and more useful than a generic “one true company profile.” A website, a brand name, and a legal entity are related concepts, but they are not interchangeable identities. A strong GLEIF name match does **not** prove that the legal entity owns the submitted domain. A detected script does **not** prove a vendor contract, budget, intent, or a complete technology stack.

Use this Actor when you need evidence that a person, spreadsheet, CRM workflow, or AI agent can inspect before enrichment, segmentation, outreach, compliance review, or research.

![Company Lookup Evidence: buyer input to a review-ready company card](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/3899241f75b77c2056d0f213388aab15a9c63a69/commercial115/company-lookup/readme-hero.webp)

### What you get

Every unique requested input produces one outcome row:

- a reachable public-domain observation with final URL, homepage description, and detected technology signatures;
- an exact GLEIF record when the buyer supplied an LEI;
- a scored GLEIF legal-name match when exactly one entity clears the acceptance rule;
- a free candidate shortlist when a name is ambiguous;
- or a free, structured failure row when no billable company card was established.

Successful rows add a decision layer designed for downstream use:

- stable `entityId` and `observationId`;
- `evidence[]` with direct website and GLEIF URLs;
- `freshness` and single-observation semantics;
- overall `confidence` plus separate website, registry-entity, and domain-to-registry confidence;
- `domainRegistryLinkStatus` so a name match cannot silently become ownership proof;
- explicit `dataGaps`;
- `recommendedAction`, priority, and reason;
- `safeToAutomate: false` for identity-sensitive downstream actions;
- exact `billing` meaning on every paid or free row.

Run-level completeness is written to the `OUTPUT` Key-Value Store record: requested and unique items, duplicate suppression, source attempts, delivered/paid/free/withheld rows, linked receipt count, partial state, fatal error, and replay safety.

### What this Actor is — and is not

The Actor supports three evidence modes:

1. **Domain or public URL.** It fetches the public homepage with redirect-by-redirect SSRF checks and DNS pinning, observes initial HTML and response headers, extracts public page metadata, and detects known technology signatures. It may also search GLEIF using public website-name candidates.
2. **Legal company name.** It searches GLEIF, scores returned legal names against the submitted name, applies an optional two-letter country constraint, and accepts only one entity that clears the threshold.
3. **Exact LEI.** It retrieves that record directly and verifies that the returned LEI equals the requested identifier.

It is not:

- a KYC, AML, sanctions, litigation, credit, beneficial-ownership, or investment report;
- proof that a domain belongs to a matched legal entity;
- proof that a technology is currently contracted, paid for, or used across the whole organization;
- proof of company size, revenue, budget, intent, growth, or willingness to buy;
- a browser-rendered crawl of every page or application route;
- a historical change detector unless you compare separate observations yourself.

The safest reusable statement is:

> At the recorded observation time, the submitted public website returned these page-level signals and/or GLEIF returned this legal-entity record or candidate set. The evidence does not, by itself, prove domain ownership, commercial intent, KYC status, or a complete technology stack.

### Why the identity boundary matters

GLEIF legal-name search is relevance-based. A brand, subsidiary, parent, holding company, and same-named entity in another country can all appear close to one another. Taking the first result creates a confident-looking error: the address and LEI are official, but they may belong to the wrong entity.

This Actor therefore separates three questions:

- **Did the submitted hostname respond?** `websiteIdentityConfidence` addresses only that public-site observation.
- **Did one GLEIF entity clear the legal-name rule?** `registryEntityConfidence` addresses only that registry match.
- **Is the website proven to belong to that legal entity?** `domainRegistryAssociationConfidence` remains low for a name-based association, and `domainRegistryLinkStatus` says it is unverified.

For high-stakes joins, verify the link with first-party legal pages, regulatory disclosures, a known LEI, a registry identifier published by the company, or another authoritative source.

### Who buys this data

#### Sales operations and RevOps

Normalize messy account inputs before routing them into a CRM. Keep website observations, legal-entity candidates, and uncertainty in separate columns. Use tech evidence for manual segmentation, not as an automatic purchase-intent score.

#### Lead-generation and research agencies

Deliver auditable enrichment instead of a black-box “company matched” flag. Include the input, direct source URL, matched legal name, confidence, gaps, and recommended review action in the client export.

#### Founders and small B2B teams

Check a small account list without buying another monthly seat. Use exact LEIs where available; otherwise add a country hint and review the identity boundary before outreach.

#### Market and investment researchers

Build a reproducible first-pass company sheet from public evidence. Treat it as collection and triage, not as investment, ownership, or credit analysis.

#### Compliance and procurement support teams

Use the row to prepare a human review queue or discover the LEI that needs deeper screening. Do not treat it as completed KYC, AML, sanctions, beneficial-ownership, or counterparty approval.

#### AI-agent and automation builders

Give an agent structured evidence and a forced review boundary. Require the agent to cite `evidence[].sourceUrl`, preserve `dataGaps`, and obey `recommendedAction`; never allow a fuzzy candidate or name-based domain association to become an asserted fact.

### High-value use cases

#### 1. CRM account normalization

Input domains and legal names from a spreadsheet. Store `entityId`, `registry.lei`, `registryMatch`, `domainRegistryLinkStatus`, confidence, and evidence URLs. Route ambiguous or domain-linked matches to review instead of silently merging accounts.

#### 2. Territory and market segmentation

Use `registry.jurisdiction`, `registry.status`, and observed technologies as review inputs. Preserve the source and observation time, and do not infer revenue, employee count, or buying intent.

#### 3. Agency enrichment deliverables

Export one row per unique requested input to CSV or Excel. Include candidate lists for unresolved names so the client can choose the correct entity rather than receiving a fabricated exact match.

#### 4. Website technology research

Use the domain mode as a bounded homepage observation. `tech.technologies` is useful for research and manual targeting, while `dataGaps` makes clear that client-rendered, subpage-only, server-side, and unrecognized tools can be missed.

#### 5. Exact LEI retrieval

Pass a 20-character LEI when legal identity is already known. The Actor verifies the returned identifier and provides the current GLEIF record without fuzzy name selection.

#### 6. Human-in-the-loop company resolution

For ambiguous names, display `registryCandidates` with legal name, jurisdiction, LEI, and score. Ask the reviewer for an exact LEI or stronger legal name before continuing.

#### 7. Evidence-grounded AI research

Let an agent summarize rows but force it to distinguish website evidence, registry evidence, and the unverified relationship between them. This reduces hallucinated ownership and false certainty.

### How it works

![Company Lookup Evidence: evidence-to-action workflow](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/3899241f75b77c2056d0f213388aab15a9c63a69/commercial115/company-lookup/readme-workflow.webp)

1. Runtime validates the JSON object without coercing strings, integers, arrays, or malformed country hints.
2. Domain, URL, name, and LEI variants are canonicalized before duplicate suppression. `stripe.com` and `https://STRIPE.com/pricing` with the same country hint are one requested domain identity.
3. Website inputs resolve to public IP addresses. Private, loopback, link-local, multicast, CGNAT, metadata, mixed public/private DNS, unsafe schemes, and unsafe redirect hops are refused.
4. The connection is pinned to the verified address set, reducing DNS-rebinding risk.
5. HTML reads are bounded to 600,000 bytes and record truncation. Non-HTML responses and unsuccessful HTTP status codes are not sold as website evidence.
6. Fixed-origin GLEIF responses must be successful JSON with the expected root shape and a hard byte limit.
7. Name matches are scored. One accepted entity becomes `registry`; unresolved results remain `registryCandidates` and free.
8. Source work may run concurrently, but delivery and billing happen sequentially in normalized input order.
9. A paid row is delivered through one linked Dataset/PPE operation. The receipt must prove both linked operations; ambiguity is fatal and `replaySafe` becomes false.
10. Dataset rows and run-level `OUTPUT` preserve what succeeded, what failed, what was free, what was withheld, and what still needs review.

### Input

| Field | Required | Type | Limits | Meaning |
|---|---:|---|---|---|
| `companies` | yes | array of strings | 1–100 | Domain, public HTTP(S) URL, legal company name, or exact 20-character LEI. Add `| US`, `| GB`, or another two-letter country hint. |
| `maxConcurrency` | no | integer | 1–30, default 10 | Parallel source lookups. Delivery and billing remain sequential. |

Example:

```json
{
  "companies": [
    "stripe.com | US",
    "Monzo Bank Limited | GB",
    "549300CLHGIPTCYHQ143"
  ],
  "maxConcurrency": 3
}
```

Input notes:

- Use the full legal name rather than a short brand when searching the registry.
- Use a country hint when the same name may exist in several jurisdictions.
- Use an LEI when you already know the entity and need exact retrieval.
- URL paths are accepted for convenience, but domain identity is canonicalized to the hostname.
- Duplicate domain/URL variants and case-only name or LEI variants are searched and billed once.
- Non-string items, control characters, malformed hints, oversized items, and numeric strings in integer fields are rejected instead of silently coerced.

### Output example

```json
{
  "input": "stripe.com | US",
  "found": true,
  "companyName": "STRIPE, LLC",
  "domain": "stripe.com",
  "websiteUrl": "https://stripe.com/",
  "websiteResponseTruncated": false,
  "description": "Financial infrastructure for the internet.",
  "registry": {
    "lei": "549300CLHGIPTCYHQ143",
    "jurisdiction": "US-DE",
    "status": "ACTIVE",
    "legalForm": "HZEH",
    "address": "Wilmington, US-DE, US"
  },
  "registryMatch": {
    "confidence": 1,
    "basis": "name+country",
    "matchedName": "STRIPE, LLC",
    "matchedFrom": "Stripe"
  },
  "registryCandidates": [],
  "tech": {
    "cms": null,
    "ecommerce": null,
    "technologies": ["Next.js", "Nginx"]
  },
  "schemaVersion": "1.0.0",
  "recordType": "company_lookup_observation",
  "entityId": "legal-entity:549300CLHGIPTCYHQ143",
  "observationId": "company-lookup-observation:…",
  "observedAt": "2026-08-11T00:00:00.000Z",
  "freshness": {
    "status": "fresh",
    "ageSeconds": 0,
    "basis": "source_retrieval_time",
    "cacheReused": false
  },
  "evidenceCoverage": 80,
  "websiteIdentityConfidence": 95,
  "registryEntityConfidence": 100,
  "domainRegistryAssociationConfidence": 40,
  "domainRegistryLinkStatus": "unverified_name_based_association",
  "confidence": {
    "score": 95,
    "level": "high",
    "reasons": ["The submitted public hostname resolved and returned a successful homepage response."],
    "risks": ["A high legal-name similarity does not prove that the observed domain is owned or operated by the matched legal entity."]
  },
  "dataGaps": [
    "Only the submitted homepage initial HTML and response headers were observed; client-rendered and subpage technologies may be missing.",
    "A high legal-name similarity does not prove that the observed domain is owned or operated by the matched legal entity."
  ],
  "recommendedAction": "VERIFY_DOMAIN_TO_LEGAL_ENTITY_LINK",
  "actionPriority": "high",
  "safeToAutomate": false,
  "billing": {
    "event": "result-found",
    "billable": true,
    "unit": "one_unique_requested_company_card_delivered",
    "reason": "A unique requested website or registry observation with source evidence is delivered."
  }
}
```

### Field dictionary

#### Compatibility fields

| Field | Meaning |
|---|---|
| `input` | Normalized buyer-visible input retained for traceability. |
| `found` | Whether a billable website or registry observation was established. |
| `error` | Free outcome reason when `found` is false. |
| `companyName` | GLEIF legal name only after an accepted match; otherwise a public site name or title-derived label. |
| `domain` | Final observed hostname for domain input. |
| `description` | Public homepage meta description, bounded to 300 characters. |
| `registry` | Accepted GLEIF card, or `null`. |
| `registryMatch` | Match confidence and basis: exact LEI, name, name+country, ambiguous, or none. |
| `registryCandidates` | Up to five scored candidates when no single entity is accepted. |
| `tech` | Page-level observed CMS, ecommerce, and technology signatures. |
| `summary` | Human-readable one-line synopsis; not a substitute for structured fields. |
| `checkedAt` | Source retrieval time for compatibility. |

#### Website and registry evidence

| Field | Meaning |
|---|---|
| `websiteUrl` | Final public URL that produced the homepage observation. |
| `websiteResponseTruncated` | Whether the HTML hit the 600,000-byte cap. |
| `registrySearchUrl` | Bounded GLEIF query URL for a name-search observation. |
| `evidence[]` | Direct source observations with type, URL, scope, and timestamp. |
| `evidenceScope` | Compact list of source scopes represented by the row. |
| `evidenceCoverage` | Coverage of this row’s narrow evidence contract, not overall company-data completeness. |

#### Identity and decision fields

| Field | Meaning |
|---|---|
| `entityId` | Stable legal-entity, website, or company-query identity. |
| `observationId` | Stable identity for this source state at this observation time. |
| `inputKind` | `domain`, `name`, or `lei`. |
| `countryHint` | Optional normalized two-letter constraint. |
| `confidence` | Overall confidence in the row’s narrow identity claim, with reasons and risks. |
| `websiteIdentityConfidence` | Confidence that the submitted hostname produced the public response—not business ownership. |
| `registryEntityConfidence` | Confidence in the GLEIF entity selection. |
| `domainRegistryAssociationConfidence` | Separate confidence in joining a domain to a legal entity; intentionally low for name-only association. |
| `domainRegistryLinkStatus` | Whether the domain/legal-entity link is unverified, absent, or not applicable. |
| `dataGaps` | Facts the observed sources do not establish. |
| `recommendedAction` | Review step appropriate to the evidence state. |
| `actionPriority` / `actionReason` | Why the next step matters. |
| `safeToAutomate` | Always false for identity-sensitive downstream action without workflow-specific review. |
| `failureDiagnostics` | Failure type, retryability, and partial status. |

#### Observation, change, and billing fields

| Field | Meaning |
|---|---|
| `observedAt` / `firstSeenAt` / `lastSeenAt` | Timestamps for this single run observation. |
| `freshness` | Fresh, unknown, cache state, and age semantics. |
| `observationSemantics` | States that one run is an observation, not a historical change claim. |
| `change` | `not_computed` unless prior accepted evidence is supplied by another workflow. |
| `billing.event` | `result-found` only for paid rows. |
| `billing.billable` | Whether this row consumes the result event. |
| `billing.unit` | Exact unit delivered. |
| `billing.reason` | Why the row is paid or free. |

### Confidence model

Confidence is layered because one number cannot honestly answer three different identity questions.

#### Exact LEI input

If GLEIF returns the same LEI requested by the buyer, `registryEntityConfidence` is 100. This proves which GLEIF record was retrieved. It does not prove website ownership, beneficial ownership, creditworthiness, or compliance approval.

#### Legal-name input

The Actor normalizes punctuation, case, common legal forms, accents, and word order, then combines token overlap and character similarity. One entity must clear the 0.85 rule. A country hint is a hard source filter. Multiple accepted entities remain ambiguous rather than becoming a first-hit guess.

#### Domain input

A successful pinned public response supports `websiteIdentityConfidence`. If a scraped public site name also clears the registry match rule, the GLEIF record is shown—but the domain-to-entity association stays explicitly unverified and receives separate low confidence.

### Pricing and billing

The Actor uses pay per event:

- one `apify-actor-start` event when the run starts;
- one `result-found` event for each unique requested company card successfully delivered.

At the current public rate, the free-tier price is $0.005 per start and $0.005 per successful result, with lower account-tier rates where Apify applies them.

Free outcomes include:

- invalid or unreachable website observations after input validation;
- GLEIF source failures;
- name searches with no accepted entity;
- ambiguous candidate shortlists;
- a budget-stop explanation row.

Delivery and billing use one linked Dataset/PPE operation. On a monetized run, the receipt must report the expected two linked operations. If the platform response is ambiguous, the run fails and `OUTPUT.replaySafe` becomes false so an operator does not blindly retry and risk duplication.

The Actor checks money before each paid delivery. When the run’s charge cap cannot cover another result, remaining paid rows are withheld, not given away and not charged. `OUTPUT` records the exact state.

### Run-level OUTPUT

Read `OUTPUT` from the default Key-Value Store when completeness matters:

```json
{
  "schemaVersion": "1.0.0",
  "status": "COMPLETE",
  "input": {
    "requestedItems": 3,
    "uniqueItems": 2,
    "duplicateItems": 1,
    "maxConcurrency": 2
  },
  "source": {
    "websiteAttempts": 1,
    "registryAttempts": 1,
    "successfulItems": 2,
    "failedItems": 0
  },
  "delivery": {
    "deliveredRows": 2,
    "paidRows": 2,
    "localNonMonetizedRows": 0,
    "freeRows": 0,
    "withheldRows": 0,
    "linkedChargedCount": 4
  },
  "partial": false,
  "budgetStopped": false,
  "fatalError": null,
  "replaySafe": true,
  "safeToAutomate": false
}
```

`COMPLETE` means every unique input reached a successful billable observation. `PARTIAL` can mean one or more free source outcomes or budget withholding. `FAILED` means a fatal budget or ambiguous-delivery condition. Always inspect `replaySafe` before retrying a failed run.

### API

Start a run:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/zinin~company-lookup/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "companies": ["stripe.com | US", "Monzo Bank Limited | GB"],
    "maxConcurrency": 2
  }'
```

For a synchronous integration, use the Apify run-sync endpoint within your own timeout budget. For larger lists, start asynchronously, wait for terminal status, then read both Dataset items and the `OUTPUT` record.

### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zinin/company-lookup').call({
  companies: ['stripe.com | US', 'Monzo Bank Limited | GB'],
  maxConcurrency: 2,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const output = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('OUTPUT');

for (const row of items) {
  console.log(row.input, row.registry?.lei, row.domainRegistryLinkStatus, row.recommendedAction);
}
console.log(output?.value?.status, output?.value?.replaySafe);
```

### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("zinin/company-lookup").call(run_input={
    "companies": ["stripe.com | US", "Monzo Bank Limited | GB"],
    "maxConcurrency": 2,
})

rows = client.dataset(run["defaultDatasetId"]).list_items().items
output = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT")["value"]

for row in rows:
    print(row["input"], row.get("entityId"), row.get("recommendedAction"))
print(output["status"], output["replaySafe"])
```

### Google Sheets and Excel

Run the Actor from Apify Console, open the Dataset, and export JSON, CSV, or Excel. For review queues, keep these columns visible:

- `input`
- `companyName`
- `domain`
- `registry.lei`
- `registryMatch`
- `registryCandidates`
- `domainRegistryLinkStatus`
- `confidence`
- `dataGaps`
- `recommendedAction`
- `evidence`

Flatten nested fields in your integration only after preserving the source URLs and identity boundary.

### n8n

A practical workflow:

1. Trigger from a CRM export, form, or scheduled spreadsheet read.
2. Split the list into Actor inputs of at most 100 items.
3. Run `zinin/company-lookup`.
4. Wait for terminal run status.
5. Read Dataset rows and `OUTPUT`.
6. Route `registryMatch.basis = ambiguous` or `domainRegistryLinkStatus = unverified_name_based_association` to a review queue.
7. Write accepted reviewed identifiers back to the CRM.

Do not auto-merge companies merely because `registryMatch.confidence` is high. That score is about the legal-name match, not domain ownership.

### Make

Use an HTTP module to start the Actor, poll the run endpoint, and retrieve Dataset items. Add a filter:

- continue automatically only for your workflow’s explicitly reviewed evidence state;
- send candidate lists and domain/legal-entity joins to manual review;
- stop and alert when `OUTPUT.replaySafe` is false.

### MCP and AI agents

Expose the Actor through the Apify MCP server when an agent needs company evidence. A safe agent policy is:

1. cite at least one `evidence[].sourceUrl` for every company statement;
2. never convert `registryCandidates` into an accepted entity;
3. never describe `unverified_name_based_association` as ownership;
4. preserve `dataGaps` in the final answer;
5. ask for an LEI or authoritative legal page when `recommendedAction` requires verification;
6. never interpret observed technologies as purchase intent.

### Scheduling and monitoring

This Actor is stateless: each run observes the current public sources. Schedule it when you need refreshed evidence, then compare rows in your own database or use dedicated change-monitor Actors.

When scheduling:

- use stable, canonical inputs;
- store `entityId`, `observationId`, and `observedAt`;
- compare source-specific fields separately;
- do not call a difference a change unless both observations are accepted and comparable;
- alert on `PARTIAL`, `FAILED`, or `replaySafe:false` OUTPUT.

### Decision recipes

#### Safe CRM preparation

```text
IF inputKind = lei AND registryEntityConfidence = 100
THEN prepare the legal-entity record for reviewer approval
ELSE IF registryCandidates is not empty
THEN ask for an exact LEI or reviewer choice
ELSE preserve the website observation without adding a legal entity
```

#### Website-tech research

```text
IF found = true AND domain is present
THEN use tech.technologies as observed homepage evidence
AND preserve websiteUrl, observedAt, confidence.risks, and dataGaps
NEVER convert the observation into vendor spend or buying intent
```

#### Domain-to-entity join

```text
IF domainRegistryLinkStatus = unverified_name_based_association
THEN verify a first-party legal page, exact LEI, or authoritative registry link
BEFORE merging accounts, screening, outreach, or risk decisions
```

### Privacy, security, and responsible use

The Actor processes buyer-supplied domains, company names, country hints, and LEIs. It reads public website responses and public GLEIF data. It does not require a website login, proxy, LLM key, or GLEIF key.

Security controls include:

- fixed-origin GLEIF requests;
- redirect-by-redirect scheme and address checks for buyer-supplied websites;
- rejection of private, loopback, metadata, link-local, multicast, CGNAT, and mixed DNS answers;
- DNS pinning for the actual connection;
- bounded source bodies;
- content-type and HTTP-status validation;
- strict input without hidden coercion;
- exact linked delivery receipts and fail-closed billing.

Public availability does not remove your obligations around privacy, lawful basis, outreach rules, suppression lists, discrimination, financial promotions, data retention, and jurisdiction-specific regulation. Minimize stored inputs and outputs, restrict access, define retention, and keep a human review step for identity-sensitive actions.

### Troubleshooting

#### `hostIsBlocked is not defined`

That was a historical domain-runtime defect. The current Commercial115 build imports the pinned network client and contains a regression test for the executable domain path. If you see this exact message, verify that your run uses the current production build and share the run ID in an Actor issue.

#### I received `registryCandidates` and `found: false`

No single legal entity cleared the acceptance rule. Use the full legal name, add a two-letter country hint, pass the exact LEI, or ask a reviewer to select a candidate. The row is free.

#### A domain row has `registry: null`

The website observation can still be valid and billable. It means no GLEIF entity cleared the name rule. Use the tech and website evidence without attaching a legal identity.

#### A domain row has a registry match, but `domainRegistryAssociationConfidence` is low

That is intentional. The legal name may be a strong match while the domain-to-entity relationship remains unproven. Verify the association before joining records.

#### Technology list is empty

The homepage responded, but no catalog signature was observed. This is not proof that the company uses no technology. Client rendering, subpages, server-side tools, blocked scripts, and unknown signatures can all be missing.

#### The website returned an error or unexpected content type

The Actor does not sell an HTTP error or binary response as evidence. Check whether the site is publicly reachable and returns HTML without login or anti-bot interstitials.

#### The run is `PARTIAL`

At least one unique input produced a free source outcome or a budget stop. Inspect Dataset rows and `OUTPUT.source`, `OUTPUT.delivery`, and `OUTPUT.fatalError`.

#### The run says `replaySafe: false`

Do not retry blindly. A linked Dataset/PPE delivery response was ambiguous and the row may already exist. Inspect the Dataset and charged events first.

#### Why was a duplicate not run twice?

The Actor canonicalizes domain/URL variants, LEI case, legal-name case, and country-hint case before source work. `OUTPUT.input` shows requested, unique, and duplicate counts.

### FAQ

#### Does a GLEIF name match prove the website belongs to that company?

No. The Actor exposes the candidate record and separate association confidence, but a name match alone does not prove ownership or operation of a domain.

#### Is an LEI exact?

The identifier lookup is exact: the Actor verifies that GLEIF returned the requested LEI. Broader business, ownership, compliance, and website claims still require their own evidence.

#### Is this a company database?

It is a bounded public-evidence lookup for inputs you already have. It does not enumerate every company or replace a full commercial database.

#### Is this KYC or AML screening?

No. Use dedicated official and licensed sources, policies, and human review for regulated decisions.

#### Does it detect every technology?

No. It detects a maintained catalog of signatures in initial homepage HTML, headers, and the final URL. It does not render client-side applications or crawl every page.

#### Can I use it for lead scoring?

Use observed evidence as one reviewed feature. Do not equate technology, registry status, or domain activity with buying intent or eligibility.

#### Are ambiguous rows billed?

No. Candidate-only and failure outcomes are pushed as free rows when the active pricing contract confirms Dataset writes are unpriced.

#### Does it need a proxy or API key?

No website proxy, GLEIF key, login, or LLM key is required. Apify authentication is required to run through the API.

#### How many companies can I send?

Up to 100 strings per run. Duplicate variants are normalized before source work.

#### Can I preserve the old fields?

Yes. The original company-card fields remain. The evidence and decision layer is additive.

#### What should I store for reproducibility?

Store the full row, especially `input`, `entityId`, `observedAt`, `evidence`, confidence, gaps, decision fields, and `billing`. Store `OUTPUT` with the run ID.

### Related tools

| Actor | When to use it |
|---|---|
| [Company Registry Enricher](https://apify.com/zinin/company-registry-enricher) | Use a registry-focused workflow for legal names, LEIs, and supported company numbers. |
| [Website Tech Stack Detector](https://apify.com/zinin/tech-stack-detector) | Use a dedicated, richer page-level technology evidence workflow. |
| [B2B Lead Enricher](https://apify.com/zinin/b2b-lead-enricher) | Add broader website-based sales-research fields with explicit evidence limits. |
| [Tech Stack Change Detector](https://apify.com/zinin/tech-stack-change-detector) | Compare accepted current and previous website technology observations. |
| [Counterparty Risk Rollup](https://apify.com/zinin/counterparty-risk-rollup) | Combine dedicated public risk sources after legal identity is reviewed. |

### Support

Open an issue on this Actor page and include:

- the Apify run ID;
- whether the input was a domain, URL, legal name, or LEI;
- the relevant Dataset row;
- the `OUTPUT` record;
- the expected identity boundary;
- and whether retry safety is true or false.

Do not paste API tokens, private credentials, or non-public personal data into an issue.

***

Built by [zinin](https://apify.com/zinin). The product promise is evidence with boundaries: enough structure to act carefully, never a confident-looking guess presented as fact.

# Actor input Schema

## `companies` (type: `array`):

Company domains (e.g. "stripe.com"), legal names (e.g. "Monzo Bank Limited") or LEIs to look up. One row per entry. Add a country hint after a pipe — "stripe.com | US" — to keep the registry match inside one country; same-named companies exist in several. An LEI is used as-is, with no name search.

## `maxConcurrency` (type: `integer`):

How many source lookups to perform in parallel. Results are delivered and billed sequentially after duplicate normalization.

## Actor input object example

```json
{
  "companies": [
    "stripe.com | US",
    "Monzo Bank Limited | GB"
  ],
  "maxConcurrency": 10
}
```

# Actor output Schema

## `results` (type: `string`):

API URL for the default dataset items produced by this run.

## `runSummary` (type: `string`):

Run-level source, duplicate, delivery, billing, budget, fatal-error, and replay-safety state.

# 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 = {
    "companies": [
        "stripe.com | US",
        "Monzo Bank Limited | GB"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/company-lookup").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 = { "companies": [
        "stripe.com | US",
        "Monzo Bank Limited | GB",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("zinin/company-lookup").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 '{
  "companies": [
    "stripe.com | US",
    "Monzo Bank Limited | GB"
  ]
}' |
apify call zinin/company-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/company-lookup"
        }
    }
}

```

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/rzgNc6GSDOWg5Ixre/builds/vjVkdCgRS19p184zE/openapi.json
