# SaaS Domain to LinkedIn Company URL Resolver (`sourcerowlabs/company-link-resolver`) Actor

Find LinkedIn company links declared in public software-company website HTML. Up to 20 JSON rows with source evidence and ordered results. Source declarations are not independently verified company identity.

- **URL**: https://apify.com/sourcerowlabs/company-link-resolver.md
- **Developed by:** [SourceRow Labs](https://apify.com/sourcerowlabs) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 successful company 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

## SaaS Domain to LinkedIn Company URL Resolver

Find website-declared LinkedIn company links for up to **20 software-company domains**. Use it for small SaaS/vendor lists when you need the exact link published by each website, with your row IDs, source evidence, observation time and an explicit status for every row.

A returned link means the checked website published it. It is **not independently verified company identity**, and the LinkedIn destination is not visited. A link can refer to a parent, subsidiary, regional brand, partner or outdated page.

### Observed pilot results

On 2 November 2026, build 0.4.1 at 1 GB returned one qualifying source-declared link for **15 of 20 preselected software-company domains**. An earlier, separate mixed-enterprise sample on the same build returned qualifying links for **2 of 20 domains**. The software sample's other outcomes were one blocked redirect, one missing link, one byte-limit failure, one parser failure and one ambiguous result.

These are small, deliberately selected development samples, not representative coverage estimates or a success-rate guarantee for your list. The samples contain different websites and are not a controlled comparison of industries. Their historical counts have not been changed to credit subsequent fixes.

### Input

Submit JSON rows containing a bare domain or an HTTPS homepage URL. An optional `rowId` lets you join results back to your records.

```json
{
  "rows": [
    { "rowId": "crm-001", "domain": "apify.com" },
    { "rowId": "crm-002", "domain": "gitlab.com" }
  ],
  "maxPagesPerDomain": 3,
  "maxTotalSecondsPerDomain": 15
}
```

The maximum is 20 rows and 1 MB of input. Page paths, query strings, credentials and IP-literal inputs are unsupported. Do not submit confidential notes or personal contact lists. There is no CSV upload interface.

### What is checked

The tool reads the public homepage HTML. If no company-link candidate appears, it can check up to two linked contact/about pages. It stops at the first page containing candidates. It reads explicit LinkedIn `/company/` anchor links and qualifying Organization JSON-LD declarations; it does not guess missing URLs.

Multiple distinct candidates produce an ambiguous result. Obvious third-party references are filtered, but this does not guarantee the right corporate entity. Static HTML, robots restrictions and website blocking can reduce coverage. No LinkedIn login, employee search, email collection, search-engine fallback, proxy bypass or browser rendering is used.

Numeric company IDs and older company slugs are retained as declared. A redirect to a different domain can be blocked; that does not mean the company has no LinkedIn page.

### Price and spending limit

The success-event price is **USD 0.005 per qualifying source-declared result for a distinct normalized domain per run**: USD 5 per 1,000 successful results. A full 20-row batch has at most USD 0.10 in success-event charges. Standard pay-per-event pricing includes platform usage in the event price; applicable taxes depend on the platform's billing rules.

Missing, ambiguous, unverified, blocked, invalid, timed-out and failed observations have no success-event charge. Duplicate domains stay in the output without additional success events within that run. Starting a separate new run requests a new observation and can incur a new charge.

Set a finite maximum run charge, such as USD 0.10 for a full batch. When less than one success event remains, new domains return `budget_exhausted` without a fetch. Lower limits can leave part of the batch unprocessed.

### Results

Open **Dataset table and exports** for the normal tabular result. After processing, the actual rows, including failures and duplicates, are copied to the default dataset. Apify's dataset export controls provide CSV and JSON downloads; use JSON for full nested source evidence.

**Authoritative ordered JSON** remains the primary result: the `OUTPUT` key-value record contains one JSON snapshot with a `rows` array. If the run or dataset export is interrupted, use this snapshot and check every row's status. The dataset can be missing, partial, or stale after an uncertain export or a resumed run; CSV then has the same limitation.

Rows preserve input order and include `inputIndex`, `rowId`, `inputDomain`, `resolvedWebsite`, `companyLinkedInUrlOrNull`, `sourceUrlOrNull`, `observedAtUTC`, `method`, `status` and bounded source `evidence`. `companyIdentityIndependentlyVerified` is always false.

| Status | Meaning |
| --- | --- |
| `source_declared_link` | One qualifying link was observed with source evidence. |
| `ambiguous` | Multiple company-link candidates; no link selected. |
| `unverified_reference` | A reference was found but did not satisfy declaration checks. |
| `not_found` | No qualifying link appeared within this crawl; this does not prove that no LinkedIn page exists. |
| `blocked`, `invalid_input`, `timeout`, `fetch_failed`, `limit_exceeded` | The check could not produce an eligible result. |
| `budget_exhausted` | The remaining event budget was too small for another success. |
| `billing_uncertain` | Payment bookkeeping is incomplete; an already delivered result remains available. |
| `billing_halted`, `not_processed` | Work stopped, or this row has not yet been processed. |

`billable` denotes eligibility, not a receipt. `serviceChargeApplied: true` means a platform event was confirmed; false means no event was confirmed for that row, and null means the outcome is uncertain. Duplicate rows carry `duplicateOfInputIndex` and do not add a charge. A confirmed event does not establish that money has been paid out to the developer.

### Runtime and interruptions

Domains are processed sequentially, with up to 15 seconds of elapsed time per distinct domain, including worker startup, three HTML pages, 12 requests and a 3 MB accepted HTTP payload budget. Header checks occur after reading headers; this is not a strict wire-traffic limit. Redirects stay within the submitted hostname, after removing an initial `www.`, and its subdomains.

Use a **360-second run timeout** for a full batch. This bounds the run; it does not guarantee completion of all 20 rows when network, storage or payment operations are slow. Each charge call is limited to 30 seconds. An interrupted run can leave a partial snapshot; check every row's status.

Results are saved before charging. Repeating delivery within the same run can reuse recorded observations without another charge. An uncertain payment stops new work, preserves delivered data and is not submitted again by this application. One result may therefore be delivered without a confirmed charge. Exactly-once settlement and automatic recovery from every interruption are not guaranteed.

The secondary dataset export adds no success-event charge. It uses a durable run marker and one append attempt with API retries disabled. An uncertain attempt is not repeated automatically, including after a restart, so available results remain in `OUTPUT` even if the dataset export is incomplete. Do not delete or edit export markers to force another append. Exactly-once dataset delivery is not promised.

### Support

Use the Actor's Issues tab with the run ID and affected row IDs. Report missing promised evidence, invalid company-link formats, unexpected duplicate charges or uncertain payment status. Do not include credentials or confidential data.

Payment reconciliation or compensation may require Apify support; instant or automatic refunds are not promised. Do not delete or edit a run's payment ledger to retry it. Review the source page when exact corporate identity matters.

# Actor input Schema

## `rows` (type: `array`):

Up to 20 objects, each containing domain and optional rowId. Use bare public domains or HTTPS homepages, not page paths. Do not submit credentials or confidential notes.

## `maxPagesPerDomain` (type: `integer`):

Homepage first; if no candidates appear, optionally follow bounded contact/about links. Stops at the first page containing candidate declarations.

## `maxTotalSecondsPerDomain` (type: `number`):

Includes process startup and network work. A timed-out subprocess is terminated; process cleanup can add a small overhead.

## Actor input object example

```json
{
  "rows": [
    {
      "rowId": "sample-001",
      "domain": "apify.com"
    }
  ],
  "maxPagesPerDomain": 3,
  "maxTotalSecondsPerDomain": 15
}
```

# Actor output Schema

## `table` (type: `string`):

Secondary row export. Use the authoritative JSON if export is missing, partial, or stale.

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

The primary OUTPUT snapshot, saved before success-event charging.

# 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 = {
    "rows": [
        {
            "rowId": "sample-001",
            "domain": "apify.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sourcerowlabs/company-link-resolver").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 = { "rows": [{
            "rowId": "sample-001",
            "domain": "apify.com",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("sourcerowlabs/company-link-resolver").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 '{
  "rows": [
    {
      "rowId": "sample-001",
      "domain": "apify.com"
    }
  ]
}' |
apify call sourcerowlabs/company-link-resolver --silent --output-dataset

```

## MCP server setup

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

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/iSn6bpVbVLFKhaTdk/builds/McmX2AxSCsbzfOOhn/openapi.json
