# Thomasnet Industrial Manufacturers Suppliers Scraper (`arezdev/thomasnet-industrial-manufacturers-suppliers-scraper`) Actor

Find industrial suppliers on ThomasNet and extract company websites, phones, certifications, revenue and employee ranges. Optionally enrich supplier websites with public emails. Export manufacturer and distributor records for sourcing and B2B research.

- **URL**: https://apify.com/arezdev/thomasnet-industrial-manufacturers-suppliers-scraper.md
- **Developed by:** [Arezdev](https://apify.com/arezdev) (community)
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 supplier extracteds

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

**Turn ThomasNet supplier searches into industrial sourcing lists with direct phones, certifications, revenue ranges, and public website contacts.**

Find US and Canadian manufacturers, CNC machine shops, injection molders, packaging suppliers, and distributors. Export one flat record per supplier to JSON, CSV, Excel, or your automation platform. Optional homepage enrichment finds published email addresses and LinkedIn links without discarding suppliers when their websites are unavailable.

Best for industrial sourcing and supplier qualification. Each successful row is a ThomasNet company; certifications and company ranges are source-reported. Kompass is outside this Actor's coverage. Test three suppliers before enabling website enrichment at scale.

### Ready-to-run example

The Input form includes public example values. A fresh `apify run` uses the input schema defaults. To explicitly select the small sample, including when input was saved by an earlier local run, use:

```sh
apify run --input-file .actor/input.example.json
```

#### Run results and source access

A blocked HTML request can recover through the public supplier-data endpoint. Optional company-website contact enrichment may remain incomplete when the company blocks access. Repeated identical source warnings are logged once with a final occurrence count. The final status counts successful suppliers separately from uncharged ERROR diagnostics.

You can also paste [.actor/input.example.json](.actor/input.example.json) into the JSON input editor in Apify Console. Replace the example sources with your own targets and increase the limits when you are ready.

### How to use

1. Paste ThomasNet supplier search, category, or company profile URLs into **ThomasNet search or profile URLs**.
2. Optionally require a certification such as `ISO 9001`, `AS9100`, or `ITAR`.
3. Choose a maximum number of saved records. Start with three to verify the results.
4. Run the Actor, then download the dataset or connect your automation.

Working CNC machining example:

```json
{
  "searchUrls": ["https://www.thomasnet.com/nsearch.html?cov=NA&heading=45330503"],
  "enrichEmailsFromWebsite": true,
  "maxItems": 3,
  "timeoutSecs": 15
}
```

Pagination is automatic. Repeated supplier IDs are deduplicated across pages and searches. Certification matching ignores case, spaces, and hyphens; `ISO 9001` matches `ISO 9001:2015`. The filter uses published qualifications rather than claims inferred from descriptions. Nonmatching suppliers are skipped without supplier event fees.

### What data can you extract?

| Data | Output fields |
| --- | --- |
| Identity | `companyId`, `companyName`, `supplierType`, `thomasnetProfileUrl` |
| Public contacts | `phone`, `websiteUrl`, `primaryEmail`, `allEmails`, `linkedinUrl` |
| Location | `city`, `state`, `zipCode`, `country` |
| Firmographics | `annualSalesRange`, `employeeCountRange`, `yearFounded` |
| Qualifications | `certifications`, `diversityBadges` |
| Context | `descriptionSnippet`, `status`, `scrapedAt`, `errorMessage` |

Unavailable scalar values are `null`; unavailable lists are empty arrays. Sales and employee counts retain source ranges. Certifications reflect source listings and are not independent compliance verification. Descriptions are limited to 1,000 characters.

### Input settings

| Setting | Default | Behavior |
| --- | --- | --- |
| `searchUrls` | CNC Machining category `45330503` | Up to 100 ThomasNet search, category, or profile URLs |
| `requiredCertification` | Empty | Optional published certification or diversity badge filter |
| `enrichEmailsFromWebsite` | `true` | Scan each external supplier homepage for contacts |
| `maxItems` | `3` | 1–2,000 total SUCCESS and ERROR records |
| `timeoutSecs` | `15` | 3–60 seconds per URL, including redirects and fallback |
| `maxConcurrency` | `25` | Upper bound of 1–100; memory adaptation allows 10 workers at 256 MB |
| `proxyConfiguration` | Direct-first cascade | Optional Apify or custom HTTP/HTTPS proxies |

The Console prefill and runtime default use CNC Machining category `45330503`, as shown above. Source availability and website enrichment vary, so successful completion in under five seconds cannot be guaranteed.

The public API fallback supports category IDs, text queries, geographic category paths, ZIP/distance parameters, and pagination. Unsupported URL filters produce an explicit error rather than silently widening the search. Use `requiredCertification` for certification filtering.

### Output example

Illustrative supplier record:

```json
{
  "companyId": "101",
  "status": "SUCCESS",
  "companyName": "Atlas Precision Manufacturing",
  "supplierType": "Custom Manufacturer",
  "primaryEmail": "sales@atlas-industrial.example",
  "allEmails": ["sales@atlas-industrial.example"],
  "phone": "+1 (212) 555-0100",
  "websiteUrl": "https://atlas-industrial.example/",
  "linkedinUrl": null,
  "city": "Toronto",
  "state": "ON",
  "zipCode": "M5V 2T6",
  "country": "Canada",
  "annualSalesRange": "$1 - 4.9 Mil",
  "employeeCountRange": "10-49",
  "yearFounded": 1998,
  "certifications": ["ISO 9001:2015", "ITAR Registered"],
  "diversityBadges": ["Woman-Owned"],
  "descriptionSnippet": "Precision CNC machining and fabrication.",
  "thomasnetProfileUrl": "https://www.thomasnet.com/company/101/profile",
  "scrapedAt": "2026-10-20T12:00:00.000Z",
  "errorMessage": null
}
```

An unreachable source produces an ERROR row with a safe public hostname and code. Failed website enrichment leaves the ThomasNet supplier as SUCCESS with existing contacts. The Actor never invents emails or substitutes sample suppliers for failed requests.

### Use the results in Clay, n8n, Make, or your API

- **Clay:** import the dataset CSV and map company, website, phone, revenue, and certifications.
- **n8n:** run the Actor through Apify or an HTTP Request node, read its dataset, and select SUCCESS rows.
- **Make:** run the Actor and iterate its dataset into a CRM, spreadsheet, or sourcing workflow.
- **Your API:** start a run through Apify's Actor API. Read its `defaultDatasetId`, then use `GET https://api.apify.com/v2/datasets/{datasetId}/items?clean=true&format=json`. Supply authentication through your client's secure configuration.

Use `companyId` as a merge key and `scrapedAt` to track observation dates.

### Pricing and limits

The custom PPE event is **`supplier-scraped`**. Only saved SUCCESS records receive that event. **ERROR rows cost $0 in supplier event fees.** Missing emails do not make an otherwise successful supplier free of event fees. Check the current Store price; this repository does not publish or set one.

For example, **if the price is $1 per 1,000 successful suppliers**, 1,000 SUCCESS records cost $1 in supplier event fees. A run with 900 SUCCESS and 100 ERROR records costs $0.90 in those fees. Platform compute, storage, and proxy costs may apply separately. This is an illustrative price, not a configured Store rate.

Hosted free Apify accounts have a hard cap of **25 saved records per run**, including both statuses. Paid accounts and local runs use the requested limit, up to 2,000. Exhausted event budgets stop scheduling, flush eligible results, and exit successfully.

Configure the Store with `supplier-scraped` and disable automatic `apify-default-dataset-item` fees. The Actor detects incompatible automatic item fees and stops before writing results so ERROR rows remain free of event charges.

### FAQ and troubleshooting

**Why did a search return no suppliers?**

Check that the URL points to a current ThomasNet category and that the certification filter matches published qualifications. The default CNC Machining category is `45330503`. Searches with no results can produce an ERROR diagnostic; certification filters with no matches produce an empty dataset.

**Why are emails or other fields missing?**

Not every company publishes every field. Enrichment visits the external homepage only. It does not crawl all contact pages, infer addresses, submit forms, or sign in. Script-generated and image-only emails may remain unavailable.

**How do proxies work?**

Direct requests run first. HTTP 403, 429, or 503 can trigger SHADER datacenter fallback, then the account's default datacenter proxy. A configured final proxy tier requires explicit `useApifyProxy: true`. Set it to `false` for direct-only requests. Custom proxy URLs force the custom tier. Automatic proxies require platform-provided proxy access configuration.

**Does the Actor support LIMITED_PERMISSIONS?**

Yes. It uses the run environment and charging manager, skips account access checks, and never requests `/v2/users/me`.

**How are pages extracted?**

The Actor streams HTML and reads `__NEXT_DATA__`, JSON-LD, and supplier cards. It follows profiles and next links. Client-rendered searches fall back to ThomasNet's public GraphQL search endpoint. Public company queries can recover blocked or incomplete profiles. No browser engine is included.

**How are external requests protected?**

Every target and redirect is validated. All resolved IPs must be public unicast and are pinned without changing the HTTP hostname. Proxy tunnels connect to validated target IPs. TLS verification stays enabled. Decompressed HTML is limited to 4 MB, JSON to 2 MB, and XML to 15 MB. Nesting is capped at 128. Useful HTML prefixes can survive interrupted streams. Logs contain safe identifiers and codes. Dataset strings receive CSV formula protection, preserving valid phone numbers.

### Local development and deployment

Use Node.js 22 or newer:

```sh
npm ci
npm run build
npm test
npm run benchmark
npm run smoke
npm run smoke -- --enrich
npm start
```

Provide input through Apify's local storage input mechanism. Never commit credential files. The test suite uses `node:test`. Launchers set `UV_THREADPOOL_SIZE=64`, `NODE_ENV=production`, and enforce TLS verification. The benchmark is offline. The live smoke test uses the current CNC Machining category and expects three successful suppliers; it can fail if the source changes or blocks requests.

The multi-stage Dockerfile uses `node:22-alpine`, installs from the lockfile, removes development dependencies, and runs as the non-root `node` user. Deploy with Apify CLI or connect the repository in Console, then configure the `supplier-scraped` Store event. Default memory is 256 MB and the run timeout is 3,600 seconds.

# Actor input Schema

## `searchUrls` (type: `array`):

Paste ThomasNet supplier search, category, or company profile URLs. Pagination is followed automatically. Start with a small sample.

## `requiredCertification` (type: `string`):

Optional certification filter, such as ISO 9001, AS9100, or ITAR. ISO 9001 also matches ISO 9001:2015. Suppliers without a published matching certification are excluded.

## `enrichEmailsFromWebsite` (type: `boolean`):

Visit each external supplier homepage to extract public email addresses and a LinkedIn link. Missing or unreachable websites do not discard supplier data.

## `maxItems` (type: `integer`):

Maximum saved SUCCESS and ERROR records combined. Free Apify accounts are limited to 25 saved records per run.

## `timeoutSecs` (type: `integer`):

Total timeout for each URL, including redirects and proxy fallback attempts.

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

Upper bound on concurrent supplier requests. The Actor reduces this automatically to fit the available memory.

## `proxyConfiguration` (type: `object`):

Optional. Direct requests run first, followed by datacenter fallback on 403, 429, or 503. Configured residential proxies are used only when explicitly enabled. Set useApifyProxy to false to force direct requests, or supply custom proxy URLs.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.thomasnet.com/nsearch.html?cov=NA&heading=45330503"
  ],
  "enrichEmailsFromWebsite": true,
  "maxItems": 3,
  "timeoutSecs": 15,
  "maxConcurrency": 25
}
```

# Actor output Schema

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

Default dataset items. Review each record status before using extracted data downstream.

# 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 = {
    "searchUrls": [
        "https://www.thomasnet.com/nsearch.html?cov=NA&heading=45330503"
    ],
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("arezdev/thomasnet-industrial-manufacturers-suppliers-scraper").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 = {
    "searchUrls": ["https://www.thomasnet.com/nsearch.html?cov=NA&heading=45330503"],
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("arezdev/thomasnet-industrial-manufacturers-suppliers-scraper").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 '{
  "searchUrls": [
    "https://www.thomasnet.com/nsearch.html?cov=NA&heading=45330503"
  ],
  "maxItems": 3
}' |
apify call arezdev/thomasnet-industrial-manufacturers-suppliers-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arezdev/thomasnet-industrial-manufacturers-suppliers-scraper"
        }
    }
}
```

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/zw9ruOO8pmuKhFMYt/builds/J66w8xpPHOWLzDYwT/openapi.json
