# Phone Number Decoder API (`s-r/phone-number-decoder`) Actor

Find every business phone for any domain. Classifies by country (NANP CA/US disambiguated), line type (landline / mobile / toll-free), purpose label (sales / support / billing / press / office / fax), and confidence tier. 31 countries.

- **URL**: https://apify.com/s-r/phone-number-decoder.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Lead generation, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN 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.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.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/platform/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

## Phone Number Decoder API — find every business phone for any domain

A phone number lookup API that takes a domain and returns every business phone Google has indexed, classified by country (US vs CA disambiguated via NANP area code), line type (landline / mobile / toll-free), purpose label (sales / support / billing / press / office / fax / general), and confidence tier. Covers 31 countries (full EU + UK + US + CA) with country-specific local-format regex packs for the 17 most common.

This is the missing layer between scraping every company's contact page (which fails on the 80% of pages that render numbers as images or JS) and paying for a B2B contact-data vendor (CARFAX-style pricing, $$$). Three orthogonal Google queries per domain — directory listings, the brand's own contact page, and customer-service brand mentions — fan out in parallel, then a multi-pass regex pipeline extracts and normalizes every phone to E.164 form.

### What you get

- **E.164 normalization** — every phone in canonical `+CCNNNNNNNNN` form, regardless of how it was rendered in the source (`(0)20 224 2424`, `020-224-2424`, `+31202242424` all collapse to one record)
- **Country resolution** — ISO 2-letter country code, with NANP CA/US disambiguation via 52 known Canadian area codes (`+1 416 ...` correctly tagged `CA`, `+1 805 ...` stays `US`)
- **Line-type classification** — `landline`, `mobile`, `toll_free`, or `unknown` based on per-country mobile/toll-free prefix lookup
- **Purpose label inference** — `sales`, `support`, `billing`, `press`, `office`, `fax`, or `general` from a 200-char text-context window with multilingual keyword matching (EN/NL/DE/FR/ES/IT/SE/PL)
- **URL-path fallback** — `/support`, `/klantenservice`, `/kundenservice`, `/contact-us` paths automatically labelled when text context is empty
- **Source attribution** — every URL where the number was surfaced, deduplicated
- **Confidence tier** — `high` for multi-source confirmation, `medium` for single-source, `low` for likely-personal mobile (single-source mobile with `label=general`)
- **IT canonicalization** — `+39 0775 770011` and `+39 775 770011` correctly dedup to one record while preserving the leading 0 in the displayed form

### Why use a phone number lookup instead of scraping pages

Contact-page scraping is fragile. About 30% of business contact pages render phones as images (anti-scrape defence). Another 20% load phones via JavaScript that fires after a page-view event. About 10% fragment phones across regional subsidiary pages that don't link back to the parent domain. A naive scraper that just curls the homepage misses most of the signal.

Google has already indexed the same data from business directories (KvK, BBB, Trustpilot, Yelp, Google Business Profile), local newspapers, partner vendor pages, and the brand's own SEO-friendly pages. This actor harvests all of it in 3 parallel SERP queries per domain. The fan-out gives a 3-5× improvement over single-query approaches — empirically validated.

### Input

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `domain` | string | yes | `hema.nl` | Bare host like `coolblue.nl` or `patagonia.com`. No scheme, no path |
| `country` | string | no | `nl` | ISO 2-letter country hint. Drives which local-format regex pack fires for numbers without `+CC` |
| `language` | string | no | `nl` | Google UI language (hl) |
| `filter_personal` | boolean | no | `true` | When true, single-source mobile + label=general gets demoted to confidence=low and flagged |
| `hits` | boolean | no | `false` | Include raw SERP results per route in the output |

### Output

One dataset item per unique phone. Example for `domain=hema.nl, country=nl`:

```json
{
  "domain": "hema.nl",
  "country_hint": "nl",
  "phone": "+31203114411",
  "raw": "020 311 4411",
  "country": "NL",
  "type": "landline",
  "label": "support",
  "sources": [
    "https://www.hema.nl/klantenservice",
    "https://www.trustpilot.com/review/hema.nl"
  ],
  "source_count": 2,
  "confidence": "high",
  "warning": null,
  "phone_rank": 1,
  "total_results": 28,
  "duration_seconds": 0.34,
  "errors": []
}
````

Multi-phone domains return multiple items. A large brand can yield 7-14 phones across multiple countries when multi-country directory listings surface alongside the brand's own contact page.

### Use cases

**Outbound sales prospecting.** When the SDR has a TAM list of 5000 NL/BE/DE company domains, calling this actor per domain returns the canonical sales/support line plus the office HQ number. The `label=sales` keyword match across EN/NL/DE/FR helps target the right inbox; the `type=landline` filter excludes likely personal mobiles. Pair with `filter_personal=true` to auto-flag the inevitable founder-mobile leaks that appear in business directories.

**Compliance and KYC verification.** When an onboarding form has a "what's your business phone?" field, the actor lets you verify the customer-supplied number matches at least one Google-indexed source for their domain. A confidence-high match across 2+ public sources is a strong signal; a confidence-low single-source mobile is a flag for additional checks.

**Multi-country brand intelligence.** Querying `domain=ikea.com&country=nl` discovers NL, BE, LU, and US toll-free lines in a single call. The country dispatch table covers 31 countries including all EU + UK + NANP. For multinationals with regional subsidiary phones, the actor surfaces every country's office line as a separate dataset item with the resolved country code.

**Reputation monitoring and customer service routing.** For a brand monitoring tool that wants to know which support line customers actually call, the `label=support` filter combined with `source_count` ranking surfaces the line that appears across the most directory listings — usually the canonical customer-facing number, not the unstaffed corporate switchboard. The `confidence=high` tier means 2+ independent retailers confirmed it.

### How it compares

| Source | Coverage | Output | Auth |
|---|---|---|---|
| **Contact-page scraping** | ~30-40% of domains (fails on image/JS-rendered pages) | Single page only; no cross-source dedup | None |
| **B2B vendor (ZoomInfo, Lusha, Cognism)** | Comprehensive | Spec sheet per contact, with email | Paid subscription per credit |
| **Google Knowledge Panel** | Whatever Google chose to surface | One number per brand | None, but limited |
| **This actor** | 31-country regex; 3 parallel SERP routes | 4-14 phones per brand with country/type/label/source attribution | None |

This actor is for when you need MULTIPLE phones per domain with classification, not for when you need a single "official" number (use the brand's own contact page directly for that). For email + LinkedIn enrichment, a paid B2B vendor still wins.

### Pricing

Pay-per-event — one charge per phone successfully extracted. A multi-phone brand that returns 8 dataset items charges 8 events. A run with no matches charges nothing. The actor-start fee is a token charge to cover Apify compute overhead.

### Limits and gotchas

- **APAC, ME, LATAM, AF not covered.** The 31-country dispatch covers EU + UK + US + CA. Phones with `+CC` outside that set still parse via Pass 1 (international regex) but the `country` field comes back as `?`. Add to `COUNTRY_INFO` when business need arises.
- **Personal mobiles in directories.** Setting `filter_personal=false` keeps them at `medium` confidence instead of demoting to `low`. Default behavior flags single-source mobile + label=general as likely personal.
- **NANP CA/US disambiguation is area-code based.** All 52 known Canadian area codes (as of the 2026 NANPA registry) are checked. New area codes added by Canada are rare (a few per decade); update the `CANADIAN_AREA_CODES` set when they appear.
- **Captcha retries are per-route, not global.** Each of the 3 SERP routes retries once independently. If 2 routes succeed and 1 captcha-blocks, you still get most of the signal — the `errors` array reports the failing route.
- **IT leading-0 canonicalization is asymmetric.** `+39 0775 770011` and `+39 775 770011` collapse to one dedup key, but the displayed `phone` field always retains the leading 0 (correct for IT fixed-line). Every other EU country drops the national-prefix 0 in international form.
- **Toll-free without `+1`.** US toll-free numbers shown as `8XX-NNN-NNNN` (no country code) only catch via the local-format US regex when `country=us` is passed. Without the hint, they parse but get tagged generically.
- **`?` country signals an unknown CC.** If you see `?` in production, the international regex matched a `+CC` not in the dispatch table. Add the country code to `COUNTRY_INFO` to enable resolution.

### FAQ

**Can I look up phone numbers without an API key?** Yes. This actor calls Google's public index through a private SERP gateway. You don't pass any directory credentials, no B2B vendor subscription, no Google Business Profile access. Just the domain.

**How does the actor handle Canadian vs US numbers?** Both use NANP (`+1`), so the country code alone doesn't disambiguate. The actor checks the first 3 digits of the local portion against a 52-entry set of known Canadian area codes (NANPA 2026 registry). Match → `country=CA`. No match → defaults to `US`. Caribbean NANP states (Bahamas, Jamaica) currently fall through as US by default; extend the lookup if granular disambiguation matters.

**What's the difference between confidence tiers?** `high` = 2+ independent SERP results surfaced the same phone. `medium` = 1 source surfaced it. `low` = filtered down from medium because it's a mobile number, single-source, with no purpose context — almost always a personal number leaked into a business directory. Pass `filter_personal=false` to keep those at `medium`.

**Why are there 3 parallel SERP queries?** Each query has a distinct purpose: Route 1 (`-site:domain`) finds third-party directory listings that index the brand. Route 2 (`site:domain`) finds the brand's own contact page. Route 3 (broad brand query) catches Knowledge Panel and GMB-surfaced numbers. Empirically the 3-route fan-out yields 3-5× more phones per domain than a single query.

**Can the actor find phones for any domain?** NL/BE/DE/FR/UK domains hit ~95% of the time with 3-10 phones each. Nordics (SE/NO/DK/FI) ~80%. CA/US ~90% — requires the leading `+1` or canonical 10-digit format. APAC/MENA/LATAM coverage is limited; without `+CC` the international regex passes but `country` may resolve to `?`.

### Related Actors

- [VIN Decoder](https://apify.com/s-r/vin-decoder) — decode any 17-character VIN into year/make/model + recalls + safety
- [ISBN Decoder](https://apify.com/s-r/isbn-decoder) — find every edition of a book from title + author
- [Backlinks Checker](https://apify.com/s-r/backlinks-checker) — domain link profile and per-link records

# Actor input Schema

## `domain` (type: `string`):

Bare host like 'coolblue.nl' or 'patagonia.com'. No scheme, no path.

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

ISO 2-letter country hint. Drives which local-format regex pack fires for numbers without a country code.

## `language` (type: `string`):

Google UI language.

## `filter_personal` (type: `boolean`):

When true (default), single-source mobile numbers with label=general are demoted to confidence=low and flagged as likely personal.

## `hits` (type: `boolean`):

Include raw SERP results per route in the output for debugging.

## Actor input object example

```json
{
  "domain": "hema.nl",
  "country": "nl",
  "language": "nl",
  "filter_personal": true,
  "hits": false
}
```

# 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 = {
    "domain": "hema.nl"
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/phone-number-decoder").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 = { "domain": "hema.nl" }

# Run the Actor and wait for it to finish
run = client.actor("s-r/phone-number-decoder").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 '{
  "domain": "hema.nl"
}' |
apify call s-r/phone-number-decoder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=s-r/phone-number-decoder",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Phone Number Decoder API",
        "description": "Find every business phone for any domain. Classifies by country (NANP CA/US disambiguated), line type (landline / mobile / toll-free), purpose label (sales / support / billing / press / office / fax), and confidence tier. 31 countries.",
        "version": "0.1",
        "x-build-id": "K39OK2FF0O2TU59cl"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/s-r~phone-number-decoder/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-s-r-phone-number-decoder",
                "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/s-r~phone-number-decoder/runs": {
            "post": {
                "operationId": "runs-sync-s-r-phone-number-decoder",
                "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/s-r~phone-number-decoder/run-sync": {
            "post": {
                "operationId": "run-sync-s-r-phone-number-decoder",
                "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",
                "required": [
                    "domain"
                ],
                "properties": {
                    "domain": {
                        "title": "Domain",
                        "pattern": "^[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
                        "type": "string",
                        "description": "Bare host like 'coolblue.nl' or 'patagonia.com'. No scheme, no path."
                    },
                    "country": {
                        "title": "Country hint (gl)",
                        "type": "string",
                        "description": "ISO 2-letter country hint. Drives which local-format regex pack fires for numbers without a country code.",
                        "default": "nl"
                    },
                    "language": {
                        "title": "Language (hl)",
                        "type": "string",
                        "description": "Google UI language.",
                        "default": "nl"
                    },
                    "filter_personal": {
                        "title": "Filter personal mobiles",
                        "type": "boolean",
                        "description": "When true (default), single-source mobile numbers with label=general are demoted to confidence=low and flagged as likely personal.",
                        "default": true
                    },
                    "hits": {
                        "title": "Include raw SERP hits",
                        "type": "boolean",
                        "description": "Include raw SERP results per route in the output for debugging.",
                        "default": false
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
