# GoodFirms Companies & Reviews Scraper (`automation-lab/goodfirms-companies-reviews-scraper`) Actor

🏢 Extract GoodFirms company profiles, agency leads, public contacts, services, locations, portfolios, ratings, and detailed client reviews into separate datasets.

- **URL**: https://apify.com/automation-lab/goodfirms-companies-reviews-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## GoodFirms Companies & Reviews Scraper

Turn public GoodFirms agency directories and company profiles into structured company and review data.

Use the scraper to build agency lead lists, enrich vendor shortlists, compare service providers, or monitor client feedback over time.

It accepts GoodFirms category pages, listing pages, and individual company profile URLs.

It returns two clean datasets:

- 🏢 **Companies** — identity, website, description, services, pricing band, team size, founding year, locations, public contacts, social links, rating totals, and portfolio items.
- ⭐ **Reviews** — reviewer and project context, review text, dates, overall rating, and detailed quality, schedule, communication, and value ratings.

No GoodFirms login, cookies, or account credentials are required.

### What does the GoodFirms scraper do?

The Actor opens public pages on `goodfirms.co` and extracts structured business information.

You can start from a broad service category such as web scraping companies.

You can also target one or more company profile URLs directly.

For category inputs, the Actor discovers unique company profiles and follows pagination until it reaches your company limit.

When profile enrichment is enabled, it visits each company profile and captures fields not available in the listing.

When review extraction is enabled, it follows public review pagination and saves individual reviews separately.

### Who is it for?

#### Sales and lead-generation teams

Build prospect lists of agencies in a service category or location.

Export company websites, public contacts, services, size bands, and locations into a CRM.

#### Procurement and vendor research teams

Enrich a shortlist with verified company details, portfolio examples, ratings, and client feedback.

Compare agencies without copying profile pages manually.

#### Agencies and competitive-intelligence teams

Track competitor ratings, review volume, service positioning, and portfolio changes.

Schedule recurring runs and compare snapshots in your own database.

#### Reputation and customer-success teams

Collect recent public reviews with project context and detailed ratings.

Feed the Reviews dataset into dashboards, alerts, or sentiment-analysis workflows.

### Why use this Actor?

- ✅ **Two purpose-built datasets** instead of mixed company and review rows.
- ✅ **Listing and profile modes** for cheap discovery or deeper enrichment.
- ✅ **Public contacts and services** exposed by GoodFirms profiles.
- ✅ **Detailed review context** including project questions and rating breakdowns when available.
- ✅ **Canonical deduplication** by company profile URL and review ID.
- ✅ **Location filtering** that fails closed when source location data is missing.
- ✅ **Date filtering** for repeat review-monitoring jobs.
- ✅ **Residential proxy sessions** with blocked media resources to reduce bandwidth.
- ✅ **JSON, CSV, Excel, XML, and RSS exports** through Apify datasets.

### What GoodFirms data can you extract?

| Dataset | Field | Description |
|---|---|---|
| Companies | `name` | Company or agency name |
| Companies | `profileUrl` | Canonical GoodFirms company profile URL |
| Companies | `website` | Public company website |
| Companies | `description` | Profile description or positioning statement |
| Companies | `rating` | Aggregate GoodFirms rating |
| Companies | `reviewCount` | Number of public reviews shown by GoodFirms |
| Companies | `hourlyRate` | Published hourly price range |
| Companies | `teamSize` | Employee band shown on the profile |
| Companies | `foundedYear` | Published founding year |
| Companies | `phone`, `email` | Public profile contact details when available |
| Companies | `addresses` | Structured office locations |
| Companies | `services` | Service catalog from the profile |
| Companies | `socialLinks` | Public LinkedIn, Facebook, X, Instagram, and similar links |
| Companies | `portfolioItems` | Portfolio titles and image URLs |
| Reviews | `title`, `body` | Review headline and public review text |
| Reviews | `publishedAt` | Review publication timestamp |
| Reviews | `reviewerName`, `reviewerRole` | Public reviewer identity and role context |
| Reviews | `projectName` | Project name when provided |
| Reviews | `serviceProvided` | Service associated with the project |
| Reviews | `projectSummary` | Public project description |
| Reviews | `positiveFeedback` | What the reviewer appreciated most |
| Reviews | `improvementFeedback` | What the reviewer said could improve |
| Reviews | detailed ratings | Quality, schedule, communication, and value scores when exposed |

### How to scrape GoodFirms companies in 5 steps

1. Open the Actor input page.
2. Add a GoodFirms category, listing, or company profile URL.
3. Choose whether to enrich company profiles and collect reviews.
4. Set conservative company and review limits for your first run.
5. Click **Start** and export the Companies or Reviews dataset.

For a quick test, use one company profile, one company, and two reviews.

That input normally finishes well within the default timeout.

### Input options

#### GoodFirms URLs

Add one or more URLs from `goodfirms.co`.

Supported routes include:

- service category pages;
- ranked company listing pages;
- location-specific listing pages;
- individual `/company/...` profiles.

Other domains are rejected before browser extraction starts.

#### Category slug

Instead of a full URL, enter a path such as:

```text
it-services/web-scraping
````

The Actor converts it to a GoodFirms URL and combines it with any supplied start URLs.

#### Location filter

Enter a city, region, or country such as `India`, `California`, or `London`.

A company is kept only when that text appears in a public address.

Profiles with no matching location do not pass the filter.

#### Company enrichment

Set `enrichProfiles` to `false` for listing-only collection.

Set it to `true` for website, description, team size, founding year, contacts, services, social links, multiple addresses, and portfolios.

Review extraction automatically enables profile enrichment.

#### Review controls

Use `includeReviews` to create review records.

Use `maxReviewsPerCompany` to cap pagination for each company.

Use `reviewsDateFrom` with an ISO date such as `2025-01-01` to skip older reviews. When a cutoff is set, reviews without a parseable publication date are excluded.

#### Proxy configuration

GoodFirms challenges ordinary datacenter traffic.

The default input therefore uses a US residential Apify Proxy session.

Keep the default unless you have already validated another proxy configuration.

### Example input: company profile and reviews

```json
{
  "startUrls": [
    { "url": "https://www.goodfirms.co/company/infograins" }
  ],
  "enrichProfiles": true,
  "includeReviews": true,
  "maxItems": 1,
  "maxReviewsPerCompany": 10,
  "reviewsDateFrom": "2025-01-01",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

### Example input: category lead list

```json
{
  "startUrls": [
    { "url": "https://www.goodfirms.co/it-services/web-scraping?sort_by=rating" }
  ],
  "locationFilter": "India",
  "enrichProfiles": false,
  "includeReviews": false,
  "maxItems": 25,
  "maxReviewsPerCompany": 0
}
```

Use listing-only mode when company identity, price range, rating, review count, and location are enough.

### Example company output

```json
{
  "name": "Infograins",
  "profileUrl": "https://www.goodfirms.co/company/infograins",
  "website": "https://infograins.com",
  "rating": 4.9,
  "reviewCount": 100,
  "hourlyRate": "$25 - $49",
  "teamSize": "50 - 249",
  "foundedYear": 2011,
  "city": "Indore",
  "region": "Madhya Pradesh",
  "country": "in",
  "services": ["Mobile App Development", "Web Development", "Software Development"],
  "profileEnriched": true,
  "sourceUrl": "https://www.goodfirms.co/company/infograins",
  "scrapedAt": "2026-07-22T00:00:00.000Z"
}
```

Fields not published on a particular profile are returned as empty strings, empty arrays, or `null` for nullable numeric values.

### Example review output

```json
{
  "reviewId": "58684",
  "companyName": "Infograins",
  "companyProfileUrl": "https://www.goodfirms.co/company/infograins",
  "title": "Infograins Group was very professional and had the highest standards.",
  "rating": 5,
  "reviewerName": "Michele Bruno",
  "reviewerRole": "Founder at Hospitality foods",
  "projectName": "IT Staff Augmentation for IT services company",
  "serviceProvided": "IT Services",
  "qualityRating": 5,
  "scheduleRating": 5,
  "permalink": "https://www.goodfirms.co/company/infograins#review-58684"
}
```

The exact fields available depend on what the reviewer chose to publish.

### Separate Companies and Reviews datasets

Company profiles and reviews have different shapes and different export use cases.

The Actor stores companies in the default **Companies** dataset.

It stores individual reviews in the separate **Reviews** dataset.

Open the run page and select the output you need.

Each dataset has its own table view and validation schema.

This avoids a `type` discriminator and makes CSV exports easier to use.

### How much does it cost to scrape GoodFirms?

The Actor uses pay-per-event pricing.

A small one-time start event covers run setup.

Company records and review records are charged separately.

Current BRONZE prices are:

- start: **$0.0075 per run**;
- company: **$0.0078892 per saved company**;
- review: **$0.016044 per saved detailed review**.

Higher Apify plans receive tiered per-record discounts. At DIAMOND, the company price is $0.002209 and the review price is $0.0044923.

Residential proxy transfer and platform usage are covered by Actor pricing rather than requiring a separate GoodFirms account.

For the cheapest discovery run, disable profile enrichment and reviews.

For due diligence, enrich only a small shortlist and cap reviews per company.

### Tips for reliable GoodFirms extraction

- 🎯 Start with one company and two reviews before scaling.
- 📉 Use listing-only mode for broad lead discovery.
- 🏢 Enrich only the companies that need contact and service details.
- ⭐ Use a date cutoff for scheduled review monitoring.
- 🌍 Keep the residential proxy default when GoodFirms blocks other traffic.
- 🔗 Prefer canonical GoodFirms category and company URLs.
- ⏱️ Increase limits gradually because profile and review pages require browser navigation.
- 📊 Store snapshots in your own dataset or warehouse for change tracking.

### Integrations and automation workflows

#### CRM enrichment

Send the Companies dataset to HubSpot, Salesforce, Airtable, or a spreadsheet.

Use `profileUrl` as a stable external key and `website` as a domain-matching field.

#### Vendor shortlist research

Start with a category listing in listing-only mode.

Filter candidates by location, rating, review count, and hourly rate.

Run a second enrichment job on the selected profile URLs.

#### Reputation monitoring

Schedule a profile run weekly.

Set `reviewsDateFrom` to your monitoring window.

Compare `reviewId` values with your stored review table and alert on unseen IDs.

#### BI and data warehouses

Connect the Apify dataset API to BigQuery, Snowflake, PostgreSQL, or a custom ETL job.

Keep Companies and Reviews as related tables joined by `companyProfileUrl`.

#### AI review analysis

Send review titles, bodies, project summaries, and detailed ratings to an LLM workflow.

Classify themes, summarize praise, or identify recurring improvement requests.

### Use the API with Node.js

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/goodfirms-companies-reviews-scraper').call({
  startUrls: [{ url: 'https://www.goodfirms.co/company/infograins' }],
  maxItems: 1,
  includeReviews: true,
  maxReviewsPerCompany: 10
});

const companies = await client.dataset(run.defaultDatasetId).listItems();
console.log(companies.items);
```

The Reviews dataset is exposed separately on the run output page and in the run storage metadata.

### Use the API with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/goodfirms-companies-reviews-scraper').call(run_input={
    'startUrls': [{'url': 'https://www.goodfirms.co/company/infograins'}],
    'maxItems': 1,
    'includeReviews': True,
    'maxReviewsPerCompany': 10,
})

for item in client.dataset(run['defaultDatasetId']).iterate_items():
    print(item)
```

### Use the API with cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~goodfirms-companies-reviews-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [{"url":"https://www.goodfirms.co/company/infograins"}],
    "maxItems": 1,
    "includeReviews": true,
    "maxReviewsPerCompany": 10
  }'
```

Poll the returned run ID, then read the datasets from the run output links.

### Use GoodFirms data with MCP

Apify MCP lets Claude Code, Claude Desktop, Cursor, and VS Code run the Actor as a tool.

#### Claude Code

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/goodfirms-companies-reviews-scraper"
```

#### Claude Desktop, Cursor, or VS Code

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/goodfirms-companies-reviews-scraper"
    }
  }
}
```

Example prompts:

- “Find 20 highly rated web scraping agencies on GoodFirms and return their locations and hourly rates.”
- “Extract the latest 25 reviews for this GoodFirms company and summarize the strongest positive themes.”
- “Build a vendor shortlist with websites, team sizes, services, ratings, and portfolio titles.”

### Data quality and deduplication

Companies are deduplicated by canonical GoodFirms profile URL.

Review records are deduplicated by company profile URL plus public review ID.

Structured JSON-LD is preferred for stable company and rating fields.

Rendered page content is used only for fields not present in structured data, including team size, portfolios, reviewer roles, project answers, and rating breakdowns.

Every record includes `sourceUrl` and `scrapedAt` for provenance.

### Limitations

GoodFirms can change its page structure or anti-bot controls without notice.

Public profile completeness varies by company.

Some profiles do not publish an email, phone number, portfolio, reviewer role, or detailed rating category.

Listing-only mode does not include all profile fields.

Review date cutoffs fail closed: reviews without a parseable source publication date are excluded when `reviewsDateFrom` is set.

The Actor collects public GoodFirms data only and does not visit agency websites to discover extra contacts.

Residential browser extraction is slower than direct API collection.

### Is scraping GoodFirms legal?

This Actor extracts information visible on public GoodFirms pages without login.

Scraping legality depends on your jurisdiction, purpose, contractual obligations, and how you use personal data.

Use the data responsibly.

Do not use it for harassment, discrimination, spam, or unlawful profiling.

Review GoodFirms terms, robots guidance, applicable privacy laws, and your organization’s compliance requirements.

If you process reviewer information, collect only what you need and apply appropriate retention and access controls.

### Troubleshooting

#### The run says no matching companies were extracted

Check that every input URL is on `goodfirms.co` and points to a category, listing, or `/company/...` profile.

If you set `locationFilter`, confirm the exact city, region, or country appears on the public profile.

The filter intentionally fails closed when no matching location is published.

#### The run receives a security challenge

Use the default Apify residential proxy configuration.

Avoid replacing it with a datacenter-only group unless you have tested that route recently.

The Actor rotates sessions after blocked responses, but persistent target-side challenges can still exhaust retries.

#### Review output is smaller than expected

Increase `maxReviewsPerCompany`.

Remove or move `reviewsDateFrom` earlier.

Confirm the profile’s public review count and remember that some reviews may not expose a parseable date.

#### A field is empty

GoodFirms profiles vary in completeness.

An empty value means the field was not publicly exposed in the supported profile surfaces at extraction time.

### Frequently asked questions

#### Can I scrape multiple GoodFirms categories in one run?

Yes. Add multiple listing URLs and set a shared `maxItems` cap.

Companies appearing in several categories are saved once.

#### Can I scrape only company listings without opening profiles?

Yes. Set `enrichProfiles` and `includeReviews` to `false`.

This is the fastest option for broad agency discovery.

#### Can I collect every review for a company?

Set `maxReviewsPerCompany` high enough for the profile’s public review count.

Start with a smaller value to confirm runtime and cost first.

#### Does the Actor need GoodFirms credentials?

No. It uses anonymous public pages.

#### Can I filter reviews by date?

Yes. Use an ISO date in `reviewsDateFrom`.

#### Can I use my own proxies?

Yes, through `proxyConfiguration`.

Residential Apify Proxy is the tested default.

### Related scrapers

- [Clutch Reviews Scraper](https://apify.com/automation-lab/clutch-reviews-scraper) — extract detailed agency reviews from Clutch.
- [Clutch.co Listings Scraper](https://apify.com/automation-lab/clutch-co-listings-scraper) — build agency lead lists from Clutch categories.
- [TopDevelopers Agency Directory Scraper](https://apify.com/automation-lab/topdevelopers-agency-directory-scraper) — compare agencies on another B2B directory.
- [DesignRush Agency Directory Scraper](https://apify.com/automation-lab/designrush-agency-directory-scraper) — collect agency profiles and service-category listings.

Use source-specific Actors when provenance and source coverage matter.

Combine their datasets in your own warehouse for broader vendor intelligence.

### Support

If a valid public GoodFirms page fails, open an Actor issue with:

- the input URL;
- the run ID;
- the expected company or review;
- the relevant log excerpt.

Do not include private credentials or unrelated personal information.

We use reproducible examples to investigate page changes and extraction gaps.

# Actor input Schema

## `startUrls` (type: `array`):

Add GoodFirms category/listing URLs or company profile URLs. Only goodfirms.co URLs are accepted.

## `categorySlug` (type: `string`):

Optional GoodFirms path such as it-services/web-scraping. It is combined with any URLs above.

## `locationFilter` (type: `string`):

Keep only companies whose public address contains this city, region, or country. Matching fails closed when location data is missing.

## `enrichProfiles` (type: `boolean`):

Visit each profile to collect descriptions, website, public phone/email, services, social links, addresses, and portfolios.

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

Stop after this many unique GoodFirms companies across all inputs.

## `includeReviews` (type: `boolean`):

Save public client reviews to the separate Reviews dataset. This automatically enables profile enrichment.

## `maxReviewsPerCompany` (type: `integer`):

Maximum paginated review records saved for each company. Use 0 to save no reviews.

## `reviewsDateFrom` (type: `string`):

Optional ISO date cutoff such as 2025-01-01. Older reviews and reviews without a parseable publication date are skipped.

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

Residential Apify Proxy is recommended because GoodFirms blocks ordinary datacenter traffic.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.goodfirms.co/company/infograins"
    }
  ],
  "categorySlug": "",
  "locationFilter": "",
  "enrichProfiles": true,
  "maxItems": 1,
  "includeReviews": true,
  "maxReviewsPerCompany": 2,
  "reviewsDateFrom": "",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `companies` (type: `string`):

Company listings and enriched profiles with contacts, services, ratings, locations, and portfolios.

## `reviews` (type: `string`):

Individual GoodFirms client reviews with reviewer, project, feedback, and detailed rating context.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.goodfirms.co/company/infograins"
        }
    ],
    "categorySlug": "",
    "locationFilter": "",
    "enrichProfiles": true,
    "maxItems": 1,
    "includeReviews": true,
    "maxReviewsPerCompany": 2,
    "reviewsDateFrom": "",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/goodfirms-companies-reviews-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 = {
    "startUrls": [{ "url": "https://www.goodfirms.co/company/infograins" }],
    "categorySlug": "",
    "locationFilter": "",
    "enrichProfiles": True,
    "maxItems": 1,
    "includeReviews": True,
    "maxReviewsPerCompany": 2,
    "reviewsDateFrom": "",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/goodfirms-companies-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.goodfirms.co/company/infograins"
    }
  ],
  "categorySlug": "",
  "locationFilter": "",
  "enrichProfiles": true,
  "maxItems": 1,
  "includeReviews": true,
  "maxReviewsPerCompany": 2,
  "reviewsDateFrom": "",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call automation-lab/goodfirms-companies-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=automation-lab/goodfirms-companies-reviews-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "GoodFirms Companies & Reviews Scraper",
        "description": "🏢 Extract GoodFirms company profiles, agency leads, public contacts, services, locations, portfolios, ratings, and detailed client reviews into separate datasets.",
        "version": "0.1",
        "x-build-id": "djnbAuUuqVywkndnT"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/automation-lab~goodfirms-companies-reviews-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-automation-lab-goodfirms-companies-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/automation-lab~goodfirms-companies-reviews-scraper/runs": {
            "post": {
                "operationId": "runs-sync-automation-lab-goodfirms-companies-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/automation-lab~goodfirms-companies-reviews-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-automation-lab-goodfirms-companies-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "startUrls": {
                        "title": "🔗 GoodFirms URLs",
                        "type": "array",
                        "description": "Add GoodFirms category/listing URLs or company profile URLs. Only goodfirms.co URLs are accepted.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "categorySlug": {
                        "title": "Category slug",
                        "type": "string",
                        "description": "Optional GoodFirms path such as it-services/web-scraping. It is combined with any URLs above.",
                        "default": ""
                    },
                    "locationFilter": {
                        "title": "Location filter",
                        "type": "string",
                        "description": "Keep only companies whose public address contains this city, region, or country. Matching fails closed when location data is missing.",
                        "default": ""
                    },
                    "enrichProfiles": {
                        "title": "Enrich company profiles",
                        "type": "boolean",
                        "description": "Visit each profile to collect descriptions, website, public phone/email, services, social links, addresses, and portfolios.",
                        "default": true
                    },
                    "maxItems": {
                        "title": "Maximum companies",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Stop after this many unique GoodFirms companies across all inputs.",
                        "default": 10
                    },
                    "includeReviews": {
                        "title": "Extract individual reviews",
                        "type": "boolean",
                        "description": "Save public client reviews to the separate Reviews dataset. This automatically enables profile enrichment.",
                        "default": true
                    },
                    "maxReviewsPerCompany": {
                        "title": "Maximum reviews per company",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Maximum paginated review records saved for each company. Use 0 to save no reviews.",
                        "default": 10
                    },
                    "reviewsDateFrom": {
                        "title": "Reviews published from",
                        "type": "string",
                        "description": "Optional ISO date cutoff such as 2025-01-01. Older reviews and reviews without a parseable publication date are skipped.",
                        "default": ""
                    },
                    "proxyConfiguration": {
                        "title": "🌐 Proxy configuration",
                        "type": "object",
                        "description": "Residential Apify Proxy is recommended because GoodFirms blocks ordinary datacenter traffic.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "US"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
