# Africa Business Directory Scraper — 12 Countries (`piquno/businesslist-africa-scraper`) Actor

Scrape business listings across 12 African countries from the BusinessList / Yello network. Registration codes, VAT numbers, employee bands and named managers that Google Maps does not carry.

- **URL**: https://apify.com/piquno/businesslist-africa-scraper.md
- **Developed by:** [Piquno](https://apify.com/piquno) (community)
- **Categories:** Lead generation, Other
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 businesses

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?

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

## Africa Business Directory Scraper — 12 Countries

Scrape business listings across **twelve African national directories** in one actor: Nigeria, Kenya, Ghana, Uganda, Zimbabwe, Egypt, South Africa, Tanzania, Zambia, Ethiopia, Cameroon and Rwanda.

HTTP-only, no browser, no proxy. 32 flat fields per business, ready for CSV or Excel.

**$2.50 per 1,000 businesses.** No charge for empty rows.

***

### Why this one, and not Google Maps

Google Maps Scraper covers local businesses everywhere at a lower price, so a directory scraper only earns its place by carrying data Maps does not have. This one does:

| Field | Google Maps | Here |
|---|---|---|
| Company registration number | ✗ | ✓ |
| VAT registration | ✗ | ✓ |
| Employee band (e.g. `1-5`, `51-100`) | ✗ | ✓ |
| Named company manager | ✗ | ✓ |
| Establishment year | ✗ | ✓ |
| Products with local-currency prices | ✗ | ✓ |

Those turn a list of places into a list of *companies* — which is what matters for B2B prospecting, supplier discovery, KYB screening and market-entry research.

**Coverage that barely exists elsewhere.** Only two actors on the Store touch this network, both Ghana-only, both index-page-only, and both with zero monthly users. Nigeria (131k listings), Kenya (124k), Egypt (108k), South Africa (621k), Zimbabwe, Uganda, Ethiopia, Zambia, Tanzania, Cameroon and Rwanda have **no coverage at all**.

***

### Two modes, and the speed trade-off you should understand

**Read this before your first run.** The directory publishes `Crawl-delay: 40` in its robots.txt, and this actor honours it by default. That is a deliberate choice — these are small national directories, and hammering them is both discourteous and the quickest way to get blocked.

| Mode | Requests | Throughput at the default 40s delay |
|---|---|---|
| **Index only** (`fetchDetails: false`, the default) | 1 per **20** businesses | ~1,800 businesses/hour |
| **Full profiles** (`fetchDetails: true`) | 1 per business + index pages | ~90 businesses/hour |

Index pages already carry **name, address, city, phone, verified status, logo and photo count** — a complete lead-generation row with no profile fetch at all. Only turn on full profiles when you specifically need registration numbers, VAT, employee bands or managers, and keep `maxResults` small when you do.

`requestDelaySecs` is adjustable. Lowering it departs from what the site asks for, and that is your call rather than the default.

***

### Input

```json
{
  "country": "kenya",
  "indexType": "category",
  "slugs": ["construction-services", "logistics"],
  "maxResults": 500,
  "fetchDetails": false
}
```

**Defaults:** `maxResults` is **20** — a single index page, which returns in seconds. Raise it for a bulk crawl and budget the time: 20 businesses per request at 40s each is roughly 1,800 per hour. Rows are written as each index page is parsed, so a run you stop early still keeps everything collected up to that point.

Find category and city slugs on each site's own browse pages. **Slug style differs per country** — Nigeria and Kenya use hyphens (`construction-services`), Ghana uses underscores (`construction_services`) — so take them from the host you are crawling rather than reusing another country's.

The site does not support combining a category with a city; pick one axis.

***

### Output

```json
{
  "companyId": "260568",
  "name": "The Grills Corner",
  "country": "nigeria",
  "countryCode": "NG",
  "streetAddress": "Shop 20, King Jaja Complex Unilag",
  "city": "Lagos",
  "region": "Lagos",
  "latitude": 6.516142,
  "longitude": 3.397178,
  "phone": "+234 806 264 9941",
  "employeesBand": "1-5",
  "companyManager": "Chisom Okoye",
  "establishmentYear": 2018,
  "workingHours": "Monday: 9:00am - 10:00pm; Tuesday: 9:00am - 10:00pm; …",
  "listedInCategories": "Food Manufacturing | Restaurants | Seafood Restaurants",
  "ratingValue": 4.6,
  "reviewCount": 5,
  "productCount": 3,
  "verified": true,
  "fieldsPopulated": 27
}
```

#### Field fill rates

| Field | Fill |
|---|---|
| `companyId`, `name`, `city`, `detailUrl` | 100% |
| `phone` | ~100% from index pages alone |
| `latitude` / `longitude`, `workingHours`, `listedInCategories` | high, with full profiles |
| `employeesBand`, `companyManager`, `establishmentYear` | common, but self-reported by the business |
| `registrationCode`, `vatRegistration` | **a minority of listings** — use `withRegistrationOnly` if you need them |

Nothing is inferred. A listing that did not publish its VAT number returns `null`.

**Email addresses are not available.** The directory hides them behind a sign-in wall, and this actor does not attempt to get around that.

***

### Pricing

| Event | Price |
|---|---|
| Business | **$0.0025** ($2.50 / 1,000) |
| Actor start | $0.00005 |

A row is billed only if it has a company id, and it is billed **before** it is pushed — so you are never charged for a row you did not receive. Rows removed by your filters are never billed.

***

### Limitations — read before you buy

- **Speed is capped by politeness**, not capability. See the throughput table above. Plan bulk pulls as scheduled index-only runs rather than one enormous job.
- **Registration and VAT numbers are patchy.** They are the most valuable fields here and the least consistently published. Filter on them rather than assuming them.
- **Self-reported data.** Employee bands, founding years and managers come from the business's own listing and are not independently verified. The `verified` flag reflects only the directory's own check.
- **One axis at a time.** Category and city cannot be combined; the site ignores the extra path segment.
- **Apify Proxy must stay enabled.** The directory returns 403 to Apify's bare container egress but serves normally through the Apify Proxy datacenter pool — measured 0/4 without it, 20/20 with it, against 3/3 from an ordinary home connection. Datacenter proxy is included on every Apify plan, so this costs you nothing; residential is not required. It is on by default and turning it off will produce an empty run.
- **Format risk.** Listings are parsed from server-rendered HTML and profiles from a mix of structured data and labelled fields. A failed profile fetch degrades to the index row rather than dropping the business.

***

### Running locally

```bash
npm install
npm test
```

Tests run against captured live pages in `test/fixtures/` — no network required. They assert the things that actually break this scraper: 20 records per index page, page 2 disjoint from page 1, profile fields keyed by label rather than position (the first label changes with the business category), and index-only rows never inventing profile data.

# Actor input Schema

## `country` (type: `string`):

Which national directory to crawl. Each country is a separate site sharing the same structure.

## `indexType` (type: `string`):

Crawl an industry category or a city. The site does not support combining the two.

## `slugs` (type: `array`):

Slugs to crawl, e.g. restaurants, construction-services, lagos. Find them on the site's own browse pages — slug style differs per country (Nigeria and Kenya use hyphens, Ghana uses underscores).

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

Optional. Paste directory index URLs directly. Overrides country, browse-by and slugs.

## `maxResults` (type: `integer`):

Hard limit. Index pages return 20 businesses each, so this divided by 20 is your request count — and at the default 40s crawl delay each of those costs 40 seconds. The default of 20 is a single index page, which returns in seconds; raise it for a bulk crawl and expect roughly 1,800 businesses per hour.

## `fetchDetails` (type: `boolean`):

Visit each company page for registration code, VAT number, employee band, manager, establishment year, opening hours, coordinates and products. SLOW — it costs one extra request per business. Leave off for bulk name/address/phone extraction, which the index pages already provide.

## `requestDelaySecs` (type: `integer`):

The directory publishes Crawl-delay: 40 in its robots.txt and this actor honours it by default. Lowering it departs from what the site asks for and raises your risk of being blocked. At 40s an index page yields 20 businesses, so bulk mode collects roughly 1,800 businesses per hour.

## `incremental` (type: `boolean`):

Remember company ids between runs and emit only listings not seen before.

## `stateKey` (type: `string`):

Namespace for incremental state. Use a different key per saved crawl.

## `verifiedOnly` (type: `boolean`):

Keep only businesses the directory marks as verified.

## `withPhoneOnly` (type: `boolean`):

Drop listings that published no contact number. Index pages carry the phone, so this works without full profiles.

## `withRegistrationOnly` (type: `boolean`):

The strongest signal that a business is formally incorporated. Requires full profiles to be fetched.

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

Keep Apify Proxy enabled. The directory returns 403 to Apify's bare container egress but serves normally through the Apify Proxy datacenter pool — measured 0/4 without it and 20/20 with it. Datacenter proxy is included on every plan; residential is not needed.

## Actor input object example

```json
{
  "country": "nigeria",
  "indexType": "category",
  "slugs": [
    "restaurants"
  ],
  "maxResults": 20,
  "fetchDetails": false,
  "requestDelaySecs": 40,
  "incremental": false,
  "stateKey": "default",
  "verifiedOnly": false,
  "withPhoneOnly": false,
  "withRegistrationOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `businesses` (type: `string`):

One row per business: name, address, phone, coordinates, plus registration code, VAT number, employee band and manager where the listing carries them.

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

Rows pushed, businesses billed, rows filtered out, and the crawl delay used.

# 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 = {
    "slugs": [
        "restaurants"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("piquno/businesslist-africa-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 = {
    "slugs": ["restaurants"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("piquno/businesslist-africa-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 '{
  "slugs": [
    "restaurants"
  ],
  "maxResults": 20
}' |
apify call piquno/businesslist-africa-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,piquno/businesslist-africa-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/A60JMfIyiy8DUbnWq/builds/AZC6xFN5Nq4AXRFyM/openapi.json
