# WhatsApp Profile Photo Finder (`enrich-crm/whatsapp-profile-photo`) Actor

Get the WhatsApp profile photo URL for phone numbers. Pay only when a photo is found. Bulk input, first 3 photos free.

- **URL**: https://apify.com/enrich-crm/whatsapp-profile-photo.md
- **Developed by:** [Enrich-CRM](https://apify.com/enrich-crm) (community)
- **Categories:** Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.40 / 1,000 photo delivereds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## WhatsApp Profile Photo Finder

**Retrieve the WhatsApp profile photo of a phone number when one is visible. You are charged only when a photo is actually returned.**

Powered by [Enrich-CRM](https://enrich-crm.com) · Every result timestamped (`checked_at`) · EU servers (Paris) · GDPR-minded

***

### What does this Actor do?

Give it a list of phone numbers. For each one, the Actor looks up the WhatsApp profile photo and, when the photo is visible, returns a link to the image. When there is no photo to return, the row says why (the number is not on WhatsApp, no photo is set, the photo is hidden by privacy settings, or the lookup was temporarily unavailable), and that row is free.

Numbers are normalised to international format first, duplicates are removed, and every unique number produces exactly one row in the dataset.

### Use cases

- Put a recognisable face on your own customers' records in a CRM or support tool
- Check that a contact's WhatsApp identity looks consistent with the person you expect before you reach out
- Spot which numbers in a list have no usable profile photo, at no cost

***

### Input parameters

| Parameter | Description | Example |
|-----------|-------------|---------|
| `phones` | List of phone numbers to look up. Required unless `phone` is given. | `["+33612345678", "+14155552671"]` |
| `phone` | A single phone number (alternative to `phones`). | `+33612345678` |
| `apiId` | *(Optional)* Your Enrich-CRM API key, for existing Enrich-CRM customers. Leave empty to use the built-in key (Apify charges apply). | `YOUR_KEY` |

#### Phone number format (E.164)

Numbers must be in international [E.164](https://en.wikipedia.org/wiki/E.164) format: a `+`, the country code, then the national number without the leading zero, for example `+33612345678` (France) or `+14155552671` (USA). Between 7 and 15 digits in total.

The Actor tidies your input before the lookup: spaces, dots, dashes and parentheses are removed, and a leading `00` is read as `+`. So `00 33 6 12 34 56 78` and `+33 6.12.34.56.78` both become `+33612345678`. Anything that still does not look like a phone number is reported as an error row and is never charged.

Identical numbers (after tidying) are looked up once per run.

#### Input example

```json
{
  "phones": ["+33612345678", "+14155552671"]
}
```

#### Output example

One row per unique number, saved to the default dataset. A photo was found for the first number, none for the second:

```json
[
  {
    "phone": "+33612345678",
    "photo_status": "ok",
    "photo_url": "https://api.enrich-crm.com/wa-photo/3f2a9c0d5b7e4a1f8c6d2e9b0a4f7c13d5e8b1a2c4f6e8d0b2a4c6e8f0a1b3c5.jpg",
    "captured_at": "2026-10-27T09:59:58Z",
    "checked_at": "2026-10-27T10:00:00Z",
    "correlationId": "5b1f0c2e-0000-0000-0000-000000000000",
    "_success": true,
    "_input": "+33612345678"
  },
  {
    "phone": "+14155552671",
    "photo_status": "not_set",
    "photo_url": null,
    "captured_at": null,
    "checked_at": "2026-10-27T10:00:01Z",
    "correlationId": "5b1f0c2e-0000-0000-0000-000000000001",
    "_success": true,
    "_input": "+14155552671"
  }
]
```

A number that cannot be processed produces an error row instead:

```json
{
  "_success": false,
  "_input": "12ab",
  "phone": "12ab",
  "_error": "Invalid phone number: expected international E.164 format, for example +33612345678."
}
```

***

### Response fields

| Field | Type | Description |
|-------|------|-------------|
| `phone` | string | The number as looked up, normalised |
| `photo_status` | string | Outcome of the lookup, see the table below |
| `photo_url` | string or null | Link to the photo image when `photo_status` is `ok`, otherwise `null` |
| `captured_at` | string or null | When the photo was retrieved (RFC 3339, UTC), `null` without photo |
| `checked_at` | string | Time of the live lookup (RFC 3339, UTC) |
| `correlationId` | string | Reference of the lookup, useful for support requests |
| `_success` | boolean | `true` when the lookup was answered (with or without photo), `false` for an error row |
| `_input` | string | The value exactly as you provided it |
| `_error` | string | Only on error rows: what went wrong |

#### `photo_status` values

| Value | Meaning | Charged? |
|-------|---------|----------|
| `ok` | A photo is visible and `photo_url` links to it | Yes |
| `not_on_whatsapp` | The number is not registered on WhatsApp | No |
| `not_set` | The account has no profile photo | No |
| `unauthorized` | A photo may exist but is not visible to us (privacy settings) | No |
| `temporarily_unavailable` | The lookup could not be completed right now, try again later | No |

***

### Pricing

| Model | Cost |
|-------|------|
| Free trial | First **3 results free** on this Actor, per Apify account |
| Photo delivered | **$0.009 per photo** on the Apify Free plan, down to $0.0036 on higher Apify plans — charged only when a photo is returned; numbers without a visible photo are free |
| Run start | $0.00005 per run |
| Existing Enrich-CRM customers | Enter your `apiId`: lookups use your Enrich-CRM credits and are not charged through Apify |

> Only rows with `photo_status` equal to `ok` and a `photo_url` count as results. Rows without photo, invalid numbers, duplicates and technical errors are never charged and never use up the free trial.

***

### Use with Clay, Zapier, Make, n8n or the API

Every run is a standard Apify run, so it plugs into the usual tools through the Apify integrations:

- **Clay, Zapier, Make, n8n** — use the Apify integration of your tool, run this Actor with a `phones` list, then read the dataset rows (`phone`, `photo_status`, `photo_url`).
- **API and webhooks** — start a run with the Apify API, wait for it to finish (or use a webhook), then fetch the dataset items as JSON, CSV or Excel.
- **Schedules** — refresh photos periodically with an Apify schedule.

***

### Good to know

- `photo_url` points to an image file you can open or download. Download the images you need to keep.
- A photo that is hidden by the person's privacy settings cannot be retrieved. That is reported as `unauthorized`, at no cost.
- Large lists are processed one number at a time. If a run is about to hit its time limit, the Actor stops cleanly, keeps the rows already produced and tells you how many numbers were left, so you can run those separately.
- Temporary capacity errors are retried automatically.

### Responsible use

A profile photo linked to a phone number is personal data under the GDPR and similar laws. Use this Actor only where you have a lawful basis, for example verifying or completing the contact details of your own customers. Respect opt-outs and privacy choices: do not use photos to profile, harass or impersonate anyone, and do not republish them. You are responsible for complying with the laws and the terms of service that apply to you.

Not affiliated with WhatsApp or Meta.

***

### Links

- [Enrich-CRM website](https://enrich-crm.com)

# Actor input Schema

## `phones` (type: `array`):

List of phone numbers in international (E.164) format, for example +33612345678. Spaces, dots, dashes and parentheses are ignored, and a leading 00 is read as +. Duplicates are processed once.

## `phone` (type: `string`):

Alternative to the list above: one phone number in international format, for example +33612345678.

## `apiId` (type: `string`):

Optional. Your Enrich-CRM API key, for existing Enrich-CRM customers. Leave empty to use the built-in key (Apify charges apply).

## Actor input object example

```json
{
  "phones": [
    "+33612345678",
    "+14155552671"
  ]
}
```

# Actor output Schema

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

Every row produced by this run.

## `overview` (type: `string`):

The Results columns only.

# 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 = {
    "phones": [
        "+33612345678",
        "+14155552671"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("enrich-crm/whatsapp-profile-photo").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 = { "phones": [
        "+33612345678",
        "+14155552671",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("enrich-crm/whatsapp-profile-photo").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 '{
  "phones": [
    "+33612345678",
    "+14155552671"
  ]
}' |
apify call enrich-crm/whatsapp-profile-photo --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,enrich-crm/whatsapp-profile-photo"
        }
    }
}
```

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/cgYe4zvcGbHKT8Cii/builds/hOFKAdg3hNEY8YmFE/openapi.json
