# CFP Board Scraper — Certified Financial Planner Leads (`scrapersdelight/cfp-board-planner-scraper`) Actor

Export CFP Board's Find a CFP Professional directory: planner name, firm, office addresses, phone, website, LinkedIn, year certified, planning services, client specialties, languages, CRD with BrokerCheck/IAPD links, plus optional discipline and bankruptcy flags. Filter by state, city, ZIP or firm.

- **URL**: https://apify.com/scrapersdelight/cfp-board-planner-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 per cfp® professional returneds

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

## CFP Board Scraper — Certified Financial Planner Leads

Export CFP Board's public [Find a CFP® Professional](https://www.letsmakeaplan.org/find-a-cfp-professional) directory as a clean lead list. **One row = one CERTIFIED FINANCIAL PLANNER™ professional**, carrying **fullName, firmName, phone, website, linkedinUrl, addressLine1, city, state, postalCode, latitude, longitude, certificationYear, yearsCertified, planningServices, specialties, languages, designations, minInvestableAssets** and the **crd** number that links straight to FINRA BrokerCheck and SEC IAPD.

**72,207 certified professionals live on 2026-08-12** — the whole directory, enumerable to the last row, not a top-N sample. Filter by state, city, ZIP, firm, planning service, client specialty, language, minimum-asset band or a lat/long radius — or look up one person by name.

**$0.004 per planner — $4 per 1,000.** No login. No API key. No cookies. No CAPTCHA solving.

```json
{ "states": ["CA"], "maxItems": 100 }
```

Click **Try for free** and hit **Start** — that is the input this Actor ships with. It returned **100 California planners in 5 seconds** on 2026-08-12, for $0.40.

***

### Quick start — data in about a minute

1. Click **Try for free** on this page (an Apify account is free; no card for the free tier).
2. Leave every field alone, or pick your states from the **States** drop-down — each one shows its live planner count, so you can size the run before you spend.
3. Press **Start**.
4. Open the **Dataset** tab when the run turns green.
5. Export **CSV / Excel / JSON** — or hit the API URL on the same tab.

No coding required. Every filter on the form is a drop-down read from CFP Board's own live vocabulary, so there is nothing to spell correctly.

#### From the API

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("scrapersdelight/cfp-board-planner-scraper").call(run_input={
    "states": ["CA", "NY"],
    "planningServices": ["Tax Planning"],
    "requirePhone": True,
    "maxItems": 500,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["fullName"], row["firmName"], row["phone"], row["crd"])
```

```javascript
// npm i apify-client
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('scrapersdelight/cfp-board-planner-scraper').call({
    states: ['MA'],
    cities: ['Boston, MA'],
    maxItems: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);
```

```bash
## One-person verification, synchronous — returns the row in the response body
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~cfp-board-planner-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"firstName":"John","lastName":"Miller","states":["CA"],"maxItems":5}'
```

***

### The wedge: the CRD number, on 97.6% of rows

A name-and-phone list of financial advisors is a commodity. What makes this row worth buying is that **97.6% of planners carry a `crd`** — the FINRA Central Registration Depository number — and the Actor emits the two URLs that number resolves to:

```
crd                  "6872220"
finraBrokerCheckUrl  "https://brokercheck.finra.org/individual/summary/6872220"
secAdviserInfoUrl    "https://adviserinfo.sec.gov/individual/summary/6872220"
```

That is a **join key into the regulated world**. With it you can attach employment history, exam record, state registrations, firm affiliation and disciplinary history from BrokerCheck and IAPD to 97.6% of a 72,207-person list, without ever running a name match. Google Maps cannot give you that. A generic "financial advisor" scrape cannot give you that. It is the difference between a mailing list and a dataset you can model on.

Two more things this export carries that the CFP Board profile page never shows in one place:

- **`offices[]` — every office, not just the headquarters.** 10.4% of planners list more than one office (measured on 2,500 contiguous rows), sometimes in different states. You get the full array with a per-office street address, city, state, ZIP, latitude and longitude, plus `officeStates[]` and `officeCount`. If you are assigning territory, the second office is the one that decides it.
- **`designations[]` — the letters after the CFP.** CFA, CPA, ChFC, CLU, AIF, CIMA, PFS, Licensed Attorney, resolved from CFP Board's GUIDs into readable labels (13.0% of rows carry at least one). A CFP® who is also a CPA is a different sales conversation than a CFP® who is not.

***

### Read this before you buy rows

1. **There are no email addresses here, and there is no `email` column.** CFP Board publishes none — the field exists in the source payload and came back empty on **0 of 2,500 rows** (re-measured 2026-08-12, and 0.0% across an earlier 6,000-row sweep). See *Honest limits* for what to do instead.
2. **`firmUrl` is effectively empty — 0.2%, five rows in 2,500.** It is kept in the schema because CFP Board populates it occasionally, but do not plan against it. The field you want is `website` (82.2%).
3. **A city or ZIP filter with no state walks the whole directory.** CFP Board's `cityExact` parameter is a *relevance boost*, not a filter, so this Actor filters city and ZIP on the rows themselves. Write cities as `"Boston, MA"` and the state narrows the server-side search for you — that is the difference between ~25 requests and ~723.
4. **`specialties` is the most aggressive filter on the form (47.4% fill).** Fewer than half of all planners declare a client segment at all. Use it to find a practice, not to size a market — a "Veterans" filter returning 2,236 planners is a floor, not a census.
5. **The top investable-asset band filters nothing.** The bands are cumulative and every planner states a minimum, so "Accepts $5,000,000 or less" returns all 72,207. Pick a *low* band to find planners who take smaller clients.

***

### What you get

One row per certified professional. All timestamps are ISO-8601 UTC; `latitude`/`longitude` are decimal degrees; `minInvestableAssets` is a whole number of US dollars.

**Identity**

| Field | Type | Notes |
|---|---|---|
| `cfpId` | string | CFP Board's profile GUID — the dedupe key |
| `profileUrl` | string | Canonical public profile page |
| `fullName` | string | First + middle + last, marks stripped |
| `fullNameDisplay` | string | As CFP Board renders it, e.g. `Mr. John T Miller` |
| `prefix` / `firstName` / `middleName` / `lastName` / `suffix` | string | Split name parts |
| `memberNumber` | string | CFP Board internal record number |
| `photoUrl` | string | Headshot URL |

**Credential**

| Field | Type | Notes |
|---|---|---|
| `certificationStatus` | string | `Certified` on every row — see limit 4 |
| `certificationYear` | integer | Year the CFP® marks were awarded |
| `yearsCertified` | integer | Current year minus `certificationYear` |
| `cfpMarks` | string | The marks string as published |
| `designations[]` | array | CFA, CPA, ChFC, CLU, AIF, CIMA, PFS, Licensed Attorney |
| `designationIds[]` | array | CFP Board's raw GUIDs, for any label we cannot resolve |

**Firm & contact**

| Field | Type | Notes |
|---|---|---|
| `firmName` | string | 99.6% fill |
| `phone` | string | Primary, formatted `(631) 592-2028 x1`; extensions preserved |
| `phones[]` | array | Every published number |
| `website` | string | 82.2% — the handle you enrich from |
| `linkedinUrl` | string | 42.3% |
| `firmUrl` | string | **0.2% — effectively empty, see limit 2** |

**Location**

| Field | Type | Notes |
|---|---|---|
| `addressLine1` / `addressLine2` / `city` / `state` / `postalCode` | string | Primary office |
| `latitude` / `longitude` | number | Primary office, decimal degrees |
| `offices[]` | array | Every office, each with its own address + geo |
| `officeCount` | integer | 10.4% of planners have more than 1 |
| `officeStates[]` | array | Every state this planner has an office in |

**Practice**

| Field | Type | Notes |
|---|---|---|
| `planningServices[]` | array | 21 possible values, 91.2% fill |
| `specialties[]` | array | 10 client segments, 47.4% fill |
| `languages[]` | array | 25 possible values, 94.6% fill |
| `minInvestableAssets` | integer | 100% fill — every planner states one |
| `paymentOptionIds[]` / `paymentOptionCount` | array / integer | Opaque GUIDs — see limit 2 in *Honest limits* |

**Regulatory**

| Field | Type | Notes |
|---|---|---|
| `crd` | string | 97.6% — the FINRA join key |
| `finraBrokerCheckUrl` / `secAdviserInfoUrl` | string | Resolved from `crd` |
| `hasDiscipline` / `hasBankruptcy` | boolean | `null` unless `includeDisclosures` is on |
| `disclosureUrl` | string | CFP Board background page for this planner |
| `disclosuresChecked` | boolean | Tells you whether the two flags above are meaningful |

**Provenance**

| Field | Type | Notes |
|---|---|---|
| `sourceUrl` | string | Where the row came from |
| `scrapedAt` | string | ISO-8601 UTC |

***

### Field fill — measured on 2,500 contiguous rows

Sample: one run of this Actor with no filters, walking contiguous pages 1–25 through the Apify datacenter proxy on **2026-08-12** — 2,500 rows, 2,500 unique. Sorted by fill, so the sparse fields are impossible to miss.

| Field | Fill |
|---|---|
| `cfpId`, `profileUrl`, `sourceUrl`, `scrapedAt` | **100%** |
| `fullName`, `fullNameDisplay`, `firstName`, `lastName` | **100%** |
| `addressLine1`, `city`, `state`, `postalCode` | **100%** |
| `certificationStatus`, `cfpMarks`, `certificationYear`, `yearsCertified` | **100%** |
| `offices[]`, `officeCount`, `officeStates[]` | **100%** |
| `minInvestableAssets`, `paymentOptionCount` | **100%** |
| `photoUrl`, `memberNumber` | **100%** |
| `latitude`, `longitude` | 99.8% |
| `firmName` | **99.6%** |
| **`crd` + `finraBrokerCheckUrl` + `secAdviserInfoUrl`** | **97.6%** |
| `languages[]` | **94.6%** |
| `planningServices[]` | **91.2%** |
| `prefix` | 86.3% |
| **`website`** | **82.2%** |
| **`phone`, `phones[]`** | **72.1%** |
| `middleName` | 69.0% |
| `specialties[]` | 47.4% |
| **`linkedinUrl`** | **42.3%** |
| `paymentOptionIds[]` | 25.4% |
| `designations[]`, `designationIds[]` | 13.0% |
| `suffix` | 3.2% |
| `addressLine2` | 0.7% |
| `firmUrl` | **0.2%** |
| `email` | **0.0% — no such column exists** |

A separate 6,000-row sweep measured `phone` at **73.3%** and `website` at **82.7%** — the two samples bracket each other, which is what a stable fill rate looks like.

`hasDiscipline`, `hasBankruptcy` and `disclosureUrl` are 0% in this table because `includeDisclosures` was off. With it on they fill on 100% of rows (50/50 on a New York run, same date).

**The headline that could mislead you: this is not an email product, and it is only a phone product 72.1% of the time.** It is a *credential and firmographic* product with a regulatory join key on 97.6% of rows. Buy it for the CRD, the certification year and the practice fields; treat the phone as a bonus and set `requirePhone` if you need it guaranteed.

Every run prints its own scope totals in the log.

***

### Sample row

A real row from the default run on 2026-08-12, unedited:

```jsonc
{
  "cfpId": "a1340a68-58d1-4410-bceb-30bf47b9696b",
  "profileUrl": "https://www.letsmakeaplan.org/find-a-cfp-professional/certified-professional-profile/a1340a68-58d1-4410-bceb-30bf47b9696b",

  "fullNameDisplay": "Mr. John T Miller",
  "prefix": "Mr.",
  "firstName": "John",
  "middleName": "T",
  "lastName": "Miller",
  "suffix": null,
  "fullName": "John T Miller",

  "cfpMarks": "CFP",
  "certificationStatus": "Certified",
  "certificationYear": 2022,
  "yearsCertified": 4,

  "firmName": "Empirical Wealth Management",
  "website": "https://www.empirical.net/",
  "firmUrl": null,
  "linkedinUrl": "https://www.linkedin.com/in/john-miller-cpa-cfp®-1773b0103",
  "phone": "(206) 518-9163",
  "phones": ["(206) 518-9163"],

  "addressLine1": "633 Menlo Ave Ste 100",
  "addressLine2": null,
  "city": "Menlo Park",
  "state": "CA",
  "postalCode": "94025-4711",
  "latitude": 37.547833,
  "longitude": -122.326471,
  "officeCount": 1,
  "offices": [
    {
      "addressLine1": "633 Menlo Ave Ste 100",
      "addressLine2": null,
      "city": "Menlo Park",
      "state": "CA",
      "postalCode": "94025-4711",
      "latitude": 37.547833,
      "longitude": -122.326471
    }
  ],
  "officeStates": ["CA"],

  "planningServices": [
    "Socially Responsible Investing", "Divorce Planning", "Investment Planning",
    "Inheritance", "Employee and Employer Plan Benefits", "Education Planning",
    "Social Security Planning", "Insurance Planning",
    "Comprehensive Financial Planning", "Tax Planning", "Estate Planning",
    "Retirement Planning"
  ],
  "specialties": ["Young Professionals", "Retirees"],
  "languages": ["English"],
  "designations": ["Certified Public Accountant"],
  "designationIds": ["e82676e9-7e47-4782-b36a-cc56c108e896"],

  "paymentOptionIds": ["8ecb3ee3-3f08-405c-bd1b-7781e254c79d"],
  "paymentOptionCount": 1,
  "minInvestableAssets": 1000000,

  "crd": "6872220",
  "finraBrokerCheckUrl": "https://brokercheck.finra.org/individual/summary/6872220",
  "secAdviserInfoUrl": "https://adviserinfo.sec.gov/individual/summary/6872220",

  "hasDiscipline": null,
  "hasBankruptcy": null,
  "disclosureUrl": null,
  "disclosuresChecked": false,

  "photoUrl": "https://login.cfp.net/eweb/fileviewer.aspx?itemkey=00000000-0000-0000-0000-000000000000&objectName=profile_images&file=449043",
  "memberNumber": "449043",
  "sourceUrl": "https://www.letsmakeaplan.org/find-a-cfp-professional/certified-professional-profile/a1340a68-58d1-4410-bceb-30bf47b9696b",
  "scrapedAt": "2026-08-13T02:02:10.320Z"
}
```

A **different** planner, run with `includeDisclosures: true` — the four fields that change (real row, New York run, 2026-08-12):

```jsonc
{
  "fullName": "Henry P. Cerruti",
  "firmName": "Northwestern Mutual",
  "hasDiscipline": false,
  "hasBankruptcy": false,
  "disclosureUrl": "https://www.cfp.net/verify-a-cfp-professional/certification-and-background/131c2d4e-2ee5-49a9-b50d-d51d57d4771f",
  "disclosuresChecked": true
}
```

**Fields people misread:**

- `hasDiscipline: false` means *checked and clean*. `hasDiscipline: null` means *not checked* — `disclosuresChecked` is the field that tells them apart. Never treat `null` as clean.
- `minInvestableAssets` is the **planner's stated minimum for a client**, in dollars. It is not their AUM. `1000000` means "I take clients with $1M+", not "I manage $1M".
- `yearsCertified` is derived (current year − `certificationYear`), not published by CFP Board.
- `phones[]` keeps extensions (`(631) 592-2028 x1`). `phone` is simply `phones[0]`.
- `designationIds[]` is always present when `designations[]` is; if CFP Board adds a credential we have no label for, the GUID still ships and the run logs a warning rather than dropping it.

***

### How to run it

#### Recipe 1 — the default: a cheap first look

```json
{ "states": ["CA"], "maxItems": 100 }
```

100 rows, 5 seconds, $0.40. This is what ships prefilled.

#### Recipe 2 — a whole state, end to end

```json
{ "states": ["MA"], "maxItems": 0 }
```

`maxItems: 0` means everything in scope. Massachusetts = 2,408 planners = $9.63.

#### Recipe 3 — one metro, cheaply

```json
{ "states": ["MA"], "cities": ["Boston, MA"], "maxItems": 0 }
```

Writing the city as `"Boston, MA"` narrows the server-side walk to Massachusetts (2,408 rows, ~25 requests) instead of the full 72,207 (~723 requests). Rows outside Boston are dropped before billing.

#### Recipe 4 — a firm roster (competitor or acquisition target)

```json
{ "firmName": "Edward Jones", "maxItems": 0 }
```

4,461 planners. Applied server-side, so you pay for rows, not for searching.

#### Recipe 5 — Spanish-speaking planners who take smaller clients

```json
{
  "languages": ["Spanish"],
  "clientInvestableAssets": "100000",
  "requirePhone": true,
  "maxItems": 0
}
```

Spanish is 2,053 planners nationally; the asset band and phone requirement cut it further. All three filters are free — the language and asset filters run server-side, and `requirePhone` drops rows before they are billed.

#### Recipe 6 — everyone within 25 miles of downtown Chicago

```json
{ "latitude": "41.8781", "longitude": "-87.6298", "radiusMiles": 25, "maxItems": 0 }
```

CFP Board's own distance filter. A planner is returned if *any* of their offices falls inside the circle.

#### Recipe 7 — verify one person

```json
{ "firstName": "John", "lastName": "Miller", "states": ["CA"], "maxItems": 5 }
```

Does this individual actually hold the CFP® marks, since when, and what is their CRD? Usually a one-row, $0.004 answer. Pair with the `run-sync-get-dataset-items` cURL call above to get it back in the HTTP response.

#### Recipe 8 — a compliance / due-diligence pull

```json
{ "states": ["NY"], "includeDisclosures": true, "maxItems": 0 }
```

Fills `hasDiscipline` and `hasBankruptcy` from CFP Board's own verification service. Measurably slower: 100 planners without disclosures took **5 seconds**; 50 planners with disclosures took **81 seconds** (measured 2026-08-12). The price per planner is identical either way.

#### Recipe 9 — the whole directory

```json
{ "maxItems": 0 }
```

72,207 planners, ~723 requests, about $289. Schedule it monthly from the **Schedules** tab to track new certifications.

***

### Input

Every field ships with a default or a prefill, so a zero-edit run works. Fields are listed in the order they appear on the form.

| Field | Type | Default | What it does |
|---|---|---|---|
| `states` | multi-select | `["CA"]` (prefill) | 54 states and territories, each labelled with its live planner count. Empty = the whole 72,207-row directory. Multi-state runs dedupe across scopes. |
| `cities` | string list | `[]` | `"Boston, MA"` (recommended) or `"Boston"`. With the state attached it also narrows the search server-side. Non-matching rows are dropped before billing. |
| `zipCodes` | string list | `[]` | 5-digit ZIPs, matched against *every* office on a profile. Combine with `states` or the run walks all 723 pages. |
| `latitude` | text | `""` | Radius-search centre, e.g. `40.7128`. Needs `longitude` too. |
| `longitude` | text | `""` | e.g. `-74.0060`. West is negative. |
| `radiusMiles` | number | `25` | 1–500. Ignored unless both coordinates are set. |
| `planningServices` | multi-select | `[]` | CFP Board's 21 live services, each with its national count. Multiple values = planner must offer **all** of them. Server-side. |
| `specialties` | multi-select | `[]` | 10 client segments with counts. Only 47.4% of planners declare any — the harshest filter here. Server-side. |
| `languages` | multi-select | `[]` | 25 languages with counts. English is 68,308 of 72,207, so it is not a filter. Server-side. |
| `clientInvestableAssets` | select | `"any"` | Cumulative bands: planners who accept a client at or below the level you pick. Server-side. |
| `firmName` | text | `""` | Partial match, server-side. `"Edward Jones"` = 4,461 planners. |
| `firstName` | text | `""` | Use with `lastName` to verify one person. |
| `lastName` | text | `""` | See above. |
| `requirePhone` | checkbox | `false` | Drop rows with no phone (~27% of any scope). Dropped rows are **not billed**. |
| `requireWebsite` | checkbox | `false` | Drop rows with no website (~17%). Dropped rows are **not billed**. |
| `includeDisclosures` | checkbox | `false` | Fill `hasDiscipline` / `hasBankruptcy`. Roughly 0.05 s/row becomes 1.6 s/row (measured). Same price. |
| `maxItems` | number | `100` | Hard cap and cost ceiling. `0` = everything in scope. Duplicates never count against it. |
| `proxyConfiguration` | proxy | Apify datacenter | See the transport ladder below. You should not need to change this. |

#### The drop-down vocabularies

Every enumerable filter is a drop-down populated from CFP Board's own live facet payload, read on **2026-08-12**. You cannot mistype a filter, and each option carries its national count so you can size a run before you spend.

**Planning services (21):** `Retirement Planning` 55,593 · `Comprehensive Financial Planning` 54,331 · `Investment Planning` 54,254 · `Insurance Planning` 36,485 · `Estate Planning` 35,993 · `Tax Planning` 25,876 · `Employee and Employer Plan Benefits` 21,192 · `Education Planning` 18,432 · `Retirement Income Management` 18,254 · `Business Succession Planning` 11,425 · `Long-term Care` 8,704 · `Social Security Planning` 8,502 · `Inheritance` 8,260 · `Small Business Planning` 7,780 · `Life Transitions` 7,661 · `Budgeting` 6,959 · `Divorce Planning` 5,915 · `Debt Management` 4,825 · `Socially Responsible Investing` 3,755 · `Elder Care` 2,386 · `Health Care` 1,389

**Client specialties (10):** `Retirees` 16,883 · `Young Professionals` 11,858 · `Intergenerational Families` 10,278 · `Women` 8,688 · `Widows/Widowers` 8,279 · `Government Employees and Military Servicepeople` 3,272 · `LGBTQ+ Individuals/Couples` 2,752 · `Veterans` 2,236 · `Special Needs Individuals` 2,033 · `Faith-Based Investing` 710

**Languages (25):** `English` 68,308 · `Spanish` 2,053 · `Mandarin` 553 · `French` 462 · `German` 302 · `Cantonese` 238 · `Portuguese` 206 · `Hindi` 202 · `Russian` 201 · `Italian` 175 · `Korean` 147 · `Japanese` 97 · `Arabic` 95 · `Gujarati` 90 · `Vietnamese` 82 · `Hebrew` 81 · `Greek` 79 · `Polish` 71 · `Armenian` 62 · `American Sign Language` 52 · `Tagalog` 52 · `Urdu` 52 · `Persian` 51 · `French Creole` 15 · `Thai` 1

**Client investable assets (cumulative):** `$0+` 36,041 · `$100,000 or less` 42,927 · `$250,000 or less` 52,131 · `$500,000 or less` 62,192 · `$1,000,000 or less` 70,568 · `$5,000,000 or less` 72,207

**States and territories (54), with live counts measured 2026-08-12:**
CA 7,522 · FL 4,884 · TX 4,874 · NY 4,126 · PA 3,650 · IL 3,432 · OH 2,881 · NJ 2,654 · NC 2,578 · MA 2,408 · MI 2,306 · VA 2,139 · GA 2,102 · MN 2,094 · CO 2,013 · WA 1,779 · MD 1,727 · MO 1,535 · AZ 1,531 · WI 1,526 · TN 1,428 · IN 1,400 · CT 1,136 · SC 964 · OR 899 · KS 890 · IA 810 · KY 776 · UT 696 · AL 631 · LA 621 · NH 523 · NE 512 · OK 429 · NV 360 · ID 301 · AR 297 · DE 296 · RI 280 · HI 266 · ME 252 · MT 224 · DC 223 · MS 218 · SD 210 · NM 184 · ND 151 · VT 141 · WV 133 · WY 90 · AK 89 · PR 28 · GU 3 · VI 3

American Samoa and the Northern Mariana Islands are deliberately absent — CFP Board lists 0 professionals in each, and offering a scope that can only return zero would be a trap.

***

### Pricing

**$0.004 per CFP® professional returned — $4 per 1,000.** Charged on the `planner-scraped` event. There is no monthly platform fee from this Actor and no charge for starting a run.

Every $1 you spend is 250 planners.

| Run | Planners | Cost |
|---|---|---|
| The default first click | 100 | **$0.40** |
| Boston, MA | 477 | **$1.91** |
| Massachusetts, end to end | 2,408 | $9.63 |
| New York State, end to end | 4,126 | **$16.50** |
| California, end to end | 7,522 | **$30.09** |
| Every Edward Jones planner | 4,461 | **$17.84** |
| Every Spanish-speaking planner in the US | 2,053 | **$8.21** |
| The entire directory | 72,207 | **$288.83** |

**What you are never charged for:**

- Rows removed by your filters. `requirePhone`, `requireWebsite`, `cities` and `zipCodes` all drop rows *before* billing — a narrow filter costs you run time and this Actor its proxy budget, not your money.
- The same planner twice. Duplicates are dropped on the profile GUID before anything is pushed, so a multi-state run never bills you for the planner with offices in two of them.
- Retries, proxy traffic, or the facet-priming request that validates your filters before the walk starts.
- `includeDisclosures`. It multiplies run time (5 s for 100 planners without, 81 s for 50 with — measured 2026-08-12) and costs exactly the same per row.

**Atomicity:** rows are charged as they are pushed (`Actor.pushData(items, 'planner-scraped')`), never delivered-then-billed. If you hit a platform spend cap mid-run you get whole rows and a stop, not a half-billed dataset.

**`maxItems` is your hard cost cap** — default 100, set `0` for everything in scope.

***

### Honest limits

**1. No email addresses. None. There is no `email` column and nothing here can produce one.** CFP Board publishes no email for any professional — the field exists in their payload and was empty on **0 of 2,500 rows** re-measured 2026-08-12, and 0.0% across an earlier 6,000-row sweep. Any CFP-directory product advertising emails is joining a different source to this one. *What to do instead:* take the `website` column (82.2%) and run it through [Decision-Maker Email Finder](https://apify.com/scrapersdelight/decision-maker-email-finder) or [Local Business Contact Enricher](https://apify.com/scrapersdelight/local-business-enricher); or if you need advisors who publish an email directly, use the [NAPFA Advisor Scraper](https://apify.com/scrapersdelight/napfa-advisor-scraper) sibling instead.

**2. Compensation method is stored but suppressed, so you get IDs, not labels.** CFP Board holds each planner's payment options and returns them as opaque GUIDs (5 distinct values, on 25.4% of rows), while the site ships `DisplayPaymentOptions: false` — the "Payment Options" panel on every live profile renders explanatory boilerplate and no labels. There is no public byte anywhere on cfp.net or letsmakeaplan.org that names them. You get `paymentOptionIds[]` and `paymentOptionCount`, which are stable and let you group planners by payment model; this Actor will not invent "fee-only" / "commission" labels CFP Board does not publish. If CFP Board switches the labels back on, this Actor will resolve them.

**3. `firmUrl` is 0.2% — five rows in 2,500.** A site-side data-completeness gap, not a scraping bug: the field is simply blank on almost every profile. Use `website` (82.2%) instead. It stays in the schema because those five rows are real.

**4. Discipline and bankruptcy flags are opt-in and slow.** `includeDisclosures` cross-references CFP Board's own verification service, which is keyed by **name** rather than by id, so it costs roughly one extra request per distinct name — measured at 81 seconds for 50 planners against 5 seconds for 100 without. About **1.0%** of professionals carry a flag (measured on 1,000 verification rows). Leave it off and both fields are `null`; `disclosuresChecked` tells you which. `disclosureUrl` on every checked row links to that planner's full CFP Board background page, which additionally shows their FINRA and SEC disciplinary status.

**5. `certificationStatus` is `"Certified"` on every row.** The directory only lists currently-certified professionals; revoked and lapsed CFP® professionals are not in it, so **you cannot use this Actor to find someone who lost their marks**. The field is kept so that a future status change is visible rather than silent. For lapsed or disciplined individuals, follow `disclosureUrl` or the `crd` into BrokerCheck.

**6. City and ZIP are filtered on our side, on purpose.** CFP Board's `cityExact` and `postalCodeExact` parameters are relevance *boosts*, not filters — asking for Boston returns all 72,207 rows with Boston sorted first. This Actor ignores them and matches your cities and ZIPs against each planner's actual office addresses, so a city run returns only that city. The trade-off is that a city or ZIP filter with no state has to walk the whole directory. Write cities as `"Boston, MA"`.

**7. The directory drifts day to day.** It read 72,207 on 2026-08-12 — 722 full pages of 100 plus 7 rows on page 723, with page 724 empty. An earlier full enumeration measured 72,210. Certifications are issued and lapse continuously. Treat any total on this page as a measurement with a date on it, not a constant.

**8. US only.** CFP Board's `Find a CFP® Professional` directory covers the United States and its territories. There is no international scope to request.

**9. No login, no API key, no CAPTCHA solving.** This Actor reads a public JSON search endpoint. It does not authenticate, does not touch the contact-a-planner form behind Cloudflare Turnstile, and does not attempt any challenge. If CFP Board ever puts the search behind auth, this Actor will fail loudly rather than work around it.

***

### How it works, and the transport ladder

CFP Board's directory is a plain JSON search API — no key, no cookie, no login, no token:

```
GET https://www.letsmakeaplan.org/api/feature/lmapprofilesearch/search
    ?limit=100&pg=<n>&sort=random&randomKey=1[&stateExact=CA][&company=…][&facet GUIDs…]
```

The Cloudflare Turnstile widget on the page guards the *contact-this-planner* form, not the search. Filter vocabularies are read from the **live facet payload on the first response of every run**, so a renamed or newly added service keeps working — and an unrecognised label stops the run with CFP Board's current list printed, instead of silently returning nothing. Disclosures, when enabled, come from CFP Board's own verification host (`cfp.net`) keyed by the same GUID.

**Transport, measured through the Apify proxy rather than assumed:**

| Rung | Result |
|---|---|
| Direct, no proxy (residential home line) | 5/5 search + 5/5 verify — **reported, then ignored**: no Apify container has a residential IP |
| **Apify datacenter proxy (default)** | **25/25 (100%)** on sustained contiguous deep pages · clean 60-page sweep · clean 76-page California walk · **20/20 (100%)** on the verification host |
| Apify RESIDENTIAL proxy | 5/5 search + 5/5 verify — no better, and it costs more |

Datacenter is the default because it is the rung that cleared sustained load, and because a 723-page walk from one fixed egress IP is exactly what a Cloudflare rate rule exists to catch — a rotating pool is safer than a single address.

The proxy itself throws the occasional 595/ECONNRESET, so every request retries on a fresh IP (5 attempts, backing off). That is not theoretical: the 25-page, 2,500-row verification run on 2026-08-12 hit exactly one 595 on page 12, the retry cleared it on a new IP, and the run finished with all 2,500 rows in **127 seconds**. CFP Board itself never returned a non-200 in any of it.

**Two API traps this Actor works around**, both of which silently corrupt a naive scraper:

1. **`limit` caps at 100.** Ask for 200 and the API returns **10 rows** while still reporting `totalPages` as though it had honoured 200 — you would walk 362 pages, collect 5% of the data, and believe you had all of it. The page size is hard-clamped to 100 and the row count is asserted on every page.
2. **`cityExact` and `postalCodeExact` are relevance boosts, not filters.** Passing them through is the classic "my city scrape returned the whole country" bug. This Actor sends only `stateExact` — which *is* a real filter, and is case-sensitive: `stateExact=ny` returns 0, `stateExact=NY` returns 4,126.

***

### Uniqueness and deduplication

Measured on **contiguous** page walks, because sparse-offset sampling proves nothing:

| Walk | Rows | Unique | Duplicates |
|---|---|---|---|
| Unfiltered, pages 1–25 (re-measured 2026-08-12) | 2,500 | 2,500 | **0 (0.00%)** |
| California, pages 1–76, end to end | 7,522 | 7,522 | **0** — an exact match to CFP Board's own reported total |
| Deep contiguous pages 200–239 | 4,000 | 4,000 | **0** |

`sort=random` is seeded by a `randomKey` that this Actor **pins to 1 for the whole run**: the same key returns a byte-identical permutation on repeat calls, while a different key gives 0/100 overlap on page 1. Without pinning, a paged walk would reshuffle underneath itself and duplicate heavily.

**The dedupe key is `cfpId`**, CFP Board's canonical profile GUID — the same one the profile URL is built from. Deduplication happens *before* anything is pushed, so the dataset and your bill never hold the same person twice.

**Where duplicates would otherwise come from:** planners with offices in more than one state (10.4% have multiple offices). Summing all 56 state and territory scopes gives **73,225 rows against a directory of 72,207** — so at least **1,018** rows are the same professional surfacing in a second state. Those are collapsed to one billed row.

**Across runs:** deduplication is per-run. Two separate runs over overlapping scopes will return the same people. Use distinct state scopes, or diff on `cfpId` after export.

***

### When a run stops early

This Actor never ends a run in a red **Failed** state, and never finishes silently green on an empty
dataset. Whatever stops it, the run ends **Succeeded** and the reason is written into the run's
**status message** (visible at the top of the run, no log-reading required), so an automation reading
run status is not tripped by a transient CFP Board hiccup or by an over-narrow filter.

- **Zero rows says why**, with the scope, the page count, the number of rows your filters dropped, and the filters themselves:
  > `0 CFP® professionals returned after fetching 1 page(s) and dropping 90 row(s) on your filters. Filters in play: states=WY, cities=Atlantis, WY. Check the state codes (2-letter, e.g. "CA") and that city names match CFP Board's spelling, or clear the filters and re-run. Nothing was charged.`
- **An unrecognised filter label stops the run before anything is fetched or billed**, and prints CFP Board's live vocabulary:

  > `"Crypto Planning" is not a valid planning service. CFP Board's list is: Retirement Planning, Comprehensive Financial Planning, Investment Planning, … Health Care.`

  The drop-downs make this unreachable from the Console; it protects API callers.
- **A changed page-size contract stops the walk.** If the API ever returns more rows than the clamped page size, the run stops and says so rather than shipping a corrupted walk. Rows already collected are still delivered.
- **A shortfall warns loudly.** If a page returns fewer rows than promised while more were expected, the log says so with both numbers, so a mid-run index shift is visible rather than silent.
- **An unresolvable designation GUID warns and still ships**, in `designationIds[]`, rather than being dropped.
- **A changed endpoint shape stops the run** with the URL, telling you the Actor needs an update rather than returning junk. Nothing is charged.
- **The run's time limit is honoured, not hit.** The Actor reads the platform's own kill time and stops
  fetching new pages with room to spare, flushes everything already collected, and reports
  *"stopped early at the run time limit"*. It does not get killed mid-flight, so you never lose
  collected rows to a timeout. Measured: unbounded runs under a 120-second limit finish in
  62–67 seconds, Succeeded, with data — three for three.
- **Your spend cap is honoured exactly.** Rows are billed and delivered in the same call, so a run that
  reaches its max-total-charge stops cleanly on a whole row. Measured at a $0.10 cap: 25 rows
  delivered, 25 rows billed, $0.10 exactly — never a delivered row that was not billed, never a
  billed row that was not delivered.

If a run returns zero rows, the status message tells you which filter did it.

***

### Who buys this

- **Wealthtech and advisor-software vendors** — Orion, Envestnet, eMoney, RightCapital, Holistiplan, Altruist and their competitors. `planningServices[]` tells you who sells tax planning (25,876) or estate planning (35,993); `firmName` tells you whether they are already inside a wirehouse's tech stack.
- **Custodians and RIA platforms** — `firmName` plus `officeStates[]` finds independent practices by territory; `crd` joins to IAPD to check whether the firm is already registered somewhere you compete.
- **Advisor recruiters and RIA M\&A aggregators** — `certificationYear` and `yearsCertified` are the tenure signal; `firmName` plus `officeCount` finds multi-office practices worth a conversation. A firm roster is one `firmName` run away.
- **Compliance, KYC and background-check vendors** — the 97.6% `crd` fill plus `includeDisclosures` (1.0% flagged) is a primary-source-adjacent credential check keyed to FINRA and SEC identifiers.
- **Insurance and annuity wholesalers** — 36,485 planners list Insurance Planning and 8,704 list Long-term Care; `minInvestableAssets` separates the mass-affluent practices from the HNW ones.
- **Financial-media and lead-gen marketplaces** — `languages[]` and `specialties[]` build the matching taxonomy (2,053 Spanish speakers, 2,236 serving Veterans, 8,688 serving Women) that a consumer-facing "find an advisor" product needs.

***

### Sibling Actors — which one do you actually want

| Actor | Universe | Record | Size | Carries email? |
|---|---|---|---|---|
| **CFP Board Scraper** (this one) | Everyone who holds the CFP® certification, at any firm | **Certified individual** | 72,207 | **No** — CFP Board publishes none |
| [NAPFA Advisor Scraper](https://apify.com/scrapersdelight/napfa-advisor-scraper) | Members of NAPFA, the fee-only association | Individual + their firm | ~4,000 | Yes, ~65% direct + 100% firm fallback |
| SEC Investment Adviser Scraper | Investment adviser **firms** registered with the SEC or a state | **Firm** (Form ADV) | ~45,500 | Firm-level only |
| [Decision-Maker Email Finder](https://apify.com/scrapersdelight/decision-maker-email-finder) | Any company domain | Person + email | — | That is the whole job |
| [Local Business Contact Enricher](https://apify.com/scrapersdelight/local-business-enricher) | Any business website | Name, role, email | — | That is the whole job |

Put plainly: NAPFA is a small, pre-qualified **fee-only membership**; SEC IAPD is the **regulated firm** register with AUM and Form ADV disciplinary items; this one is the **individual credential holder** — by far the largest of the three, and the only one that tells you *who*, personally, holds the CFP marks, since when, and what they specialise in. Many CFP® professionals here work at wirehouses and insurers that never appear in NAPFA and are not themselves an SEC-registered firm.

**Go to CFP Board for the credential and the CRD. Go to NAPFA for a direct email. Go to SEC IAPD for AUM.**

***

### Compared with the alternatives

| | This Actor | Clicking through the directory | Your own scraper | Buying an advisor list |
|---|---|---|---|---|
| Setup | Minutes | — | Days | Sales call |
| Whole directory | 72,207 rows, ~$289 | ~72,000 page views | Yours to build | Often $2,000+ |
| CRD + BrokerCheck/IAPD links | 97.6% | Manual | If you build it | Sometimes |
| Every office, not just HQ | Yes (`offices[]`) | Manual | If you build it | Rarely |
| The `limit=200` trap | Handled | n/a | **Silently loses 95% of your data** | n/a |
| The `cityExact` trap | Handled | n/a | **Returns the whole country** | n/a |
| Freshness | Live, this run | Live | Live | Whenever they last refreshed |
| Maintenance | Ours | — | Yours | Theirs |

***

### FAQ

**Does this need an account, a login or an API key?**
No. CFP Board's search endpoint is public JSON. The Actor does not authenticate and collects nothing behind any login.

**How many CFP professionals can I actually get?**
All of them: 72,207 on 2026-08-12. This is a full enumeration, not a capped sample — the walk runs to page 723, which returned the final 7 rows on that date (page 724 is empty), an exact match to CFP Board's own reported total.

**Can I get email addresses?**
No, and no CFP Board export can — CFP Board publishes none, measured 0 of 2,500 rows. Use the 82.2% `website` column with an email-finder Actor, or use the NAPFA sibling.

**Does it need a proxy?**
It uses the Apify datacenter pool by default, which measured 100% on sustained contiguous paging. You do not need to configure anything, and residential measured no better.

**Do I get charged for rows my filters remove?**
No. `requirePhone`, `requireWebsite`, `cities` and `zipCodes` all drop rows before billing. You are billed per planner delivered.

**Two runs — will I get duplicates?**
Within a run, no: 0.00% duplicates measured on 2,500, 7,522 and 4,000-row contiguous walks, deduped on `cfpId` before billing. Across runs, yes — dedupe is per-run. Use distinct state scopes or diff on `cfpId`.

**Will a run ever succeed with zero rows?**
Only when your filters genuinely match nobody — and when that happens the run's **status message**
names the scope, the page count and the filters in play, and nothing is charged. The Actor does not
end runs in a red Failed state, because an over-narrow filter or a transient CFP Board hiccup is not
a broken Actor and should not break your automation. There is no such thing as a silent empty run.

**How much does the whole directory cost?**
72,207 planners × $0.004 = **$288.83**. California alone is $30.09; New York is $16.50.

**How do I find planners in one city without paying for the whole country?**
Write the city as `"Boston, MA"`. The state narrows the server-side search to 2,408 rows instead of 72,207, and the Boston filter is applied to those before billing.

**What is the CRD number and why does it matter?**
It is FINRA's Central Registration Depository ID. Present on 97.6% of rows, it joins this dataset to BrokerCheck and SEC IAPD — employment history, exams, registrations, disciplinary events — without any name matching.

**Can I check whether a planner has been disciplined?**
Turn on `includeDisclosures` to fill `hasDiscipline` and `hasBankruptcy` from CFP Board's own verification service (~1.0% carry a flag). Measured cost is time, not money: 100 planners without it took 5 seconds, 50 planners with it took 81 seconds. Every checked row also gets a `disclosureUrl` to the full background page.

**Can I find planners who lost their certification?**
No. The directory lists only currently-certified professionals, so `certificationStatus` is `"Certified"` on every row. Follow `crd` into BrokerCheck for lapsed or disciplined individuals.

**Can I schedule this to track new certifications?**
Yes — use the **Schedules** tab on this Actor. A monthly full run and a diff on `cfpId` gives you newly certified professionals.

**What happens if CFP Board changes its filter list?**
The Actor reads the live facet vocabulary at the start of every run. A renamed or added service keeps working; an unrecognised one stops the run and prints the current list rather than returning nothing quietly.

**Something looks wrong — how do I debug it?**
Read the run log: it prints the live directory total, the matched total per scope, page-by-page progress, and a final line with rows emitted, duplicates skipped and rows dropped by your filters. Then open an issue on the **Issues** tab.

**Is `minInvestableAssets` the planner's AUM?**
No. It is the minimum a planner will accept from a client, in dollars. Every planner states one (100% fill).

***

### Changelog

| Version | Date | Change |
|---|---|---|
| 0.1.8 | 2026-08-19 | **Close-out audit pass.** (1) `scrapedAt` is now declared nullable in the dataset schema like every other field — it is always an ISO timestamp in practice, so this is defensive hardening against a `schema-validation-error` ever turning a run red; zero behaviour change. (2) **Default run memory lowered from 1024 MB to 512 MB — every run now costs you half what it did.** Apify bills compute per GB-second, so an over-provisioned Actor charges a multiple for the same work. Measured, not guessed: peak RSS was 52-74 MB across 13 platform runs, and the heaviest case this Actor has — a full 72,468-row directory walk, 725 pages, every id held in the de-duplication set — peaked at **102.7 MB**. 512 MB keeps 5x headroom over that worst case and was re-run at the new size to confirm it does not run out of memory. No behaviour change. |
| 0.1.6 | 2026-08-19 | **Reliability + billing-honesty pass, all of it measured on live platform runs.** (1) Wall-clock budget: the Actor now reads the platform's kill time and stops fetching with room to flush — before this, an unbounded run under a 120-second limit ended `TIMED-OUT` 1 time in 3 (113 s / 115 s / **122 s**); after, 3/3 Succeeded at 62–67 s with data and a status message. (2) No run ends Failed any more: a transient CFP Board outage, an unrecognised filter label, a zero-row filter combination and a changed API contract all end Succeeded with the reason in the status message, and `unhandledRejection` / `uncaughtException` nets deliver whatever was already collected. (3) The delivered count is now read from the charging manager instead of the internal counter, so the log and status message state exactly what was stored and billed — a capped run used to report "4,400 emitted" when 4,158 were delivered. (4) The walk now stops as soon as the run's charge cap is reached instead of paying run time for pages that can never be delivered. |
| 0.1 | 2026-08-12 | Every enumerable filter converted to a drop-down read from CFP Board's live facet payload (54 states, 21 services, 10 specialties, 25 languages, 6 asset bands), each labelled with its live count. Form grouped into 7 sections. `maxItems` default 500 → 100 so the first click is cheap. Field fill and dedupe re-measured on a fresh 2,500-row contiguous walk; `firmUrl` documented at 0.2%. Boston and Massachusetts counts measured. Both loud-failure paths re-tested. |

**Last verified against the live source: 2026-08-20** — 72,468 professionals (up from 72,207 on 2026-08-12; the directory grows by a few dozen a week, so every count in this README is point-in-time), facet vocabularies re-read from the live response, fill and dedupe re-measured, zero-edit run green.

***

### Legal and fair use

This Actor exports **public professional-directory information** that CFP Board publishes so that consumers can find and vet a planner. It reads a public JSON search endpoint. It does not log in, does not solve CAPTCHAs, does not touch the Cloudflare-protected contact form, and collects no data behind any authentication.

Rows describe **identifiable people**. You are responsible for complying with CFP Board's Terms of Use, and with **CAN-SPAM**, **TCPA**, **GDPR/CCPA** and any applicable opt-out, do-not-call and privacy law when you contact these professionals or store their data.

CFP®, CERTIFIED FINANCIAL PLANNER™ and the certification marks are owned by Certified Financial Planner Board of Standards, Inc.; this Actor is **not affiliated with or endorsed by** CFP Board. FINRA® and BrokerCheck® are trademarks of the Financial Industry Regulatory Authority.

***

### Support

Found a missing field, want a new filter, or hit something that looks wrong? Open an issue on the **Issues** tab — filter requests against fields CFP Board already publishes are usually quick.

If this Actor saved you time, a rating helps other buyers find it.

***

**Keywords:** CFP Board scraper · certified financial planner directory · CFP professional list · letsmakeaplan.org scraper · financial advisor leads · financial planner leads · CFP directory export · advisor lead generation · CRD number lookup · FINRA BrokerCheck link · SEC IAPD link · wealth management prospecting · RIA lead list · fintech prospecting · financial advisor database · CFP professionals by state · financial planners California · financial planners New York · financial planners Texas · financial planners Florida · Spanish speaking financial advisor · tax planning advisor list · estate planning advisor list · retirement planning advisor list · divorce financial planner · fee-only advisor data · advisor recruiting data · RIA M\&A target list · wealthtech sales leads · advisor CRM enrichment · financial services outbound · certified financial planner API · CFP verification · financial planner phone numbers · advisor LinkedIn profiles

# Actor input Schema

## `states` (type: `array`):

States and territories to walk, each to its last page. Counts in the list are live CFP Board totals measured 2026-08-12 and drift by a few each day. Picking several states runs several scopes; a planner with offices in two of them is deduplicated on their profile id and billed once. Leave empty to walk the entire 72,207-row directory. American Samoa and the Northern Mariana Islands are not offered because CFP Board lists 0 professionals in each.

## `cities` (type: `array`):

Cities to keep. Write them as "Boston, MA" — with the state attached, the state is also used to narrow the search server-side, which is the difference between fetching 25 pages and fetching 723. "Boston" on its own works but forces a full-directory walk. CFP Board's own city parameter is a relevance boost, not a filter (asking for Boston returns all 72,207 rows with Boston first), so this Actor matches your city against each planner's actual office addresses instead. Filtered-out rows are never charged.

## `zipCodes` (type: `array`):

5-digit ZIPs to keep, matched against every office address on a planner's profile (not just the primary one). Always combine with States — a ZIP filter on its own has to walk the full directory to find its matches. Filtered-out rows are never charged.

## `latitude` (type: `string`):

Centre point of the radius search, e.g. 40.7128 for New York City. Only used when Longitude is also set. Applied by CFP Board server-side, so it narrows the walk rather than filtering rows after you have paid for them.

## `longitude` (type: `string`):

Pair with Latitude, e.g. -74.0060 for New York City. West of Greenwich is negative.

## `radiusMiles` (type: `integer`):

How far from the latitude/longitude point to search. A planner is returned if ANY of their offices falls inside the circle. Ignored unless both coordinates are set.

## `planningServices` (type: `array`):

Keep only planners who offer ALL of the services you pick. This is CFP Board's own live list of 21 services, read from the directory's facet payload on 2026-08-12 — if CFP Board renames or adds one, the run stops with the current list printed in the log rather than silently returning nothing. Measured fill for this field is 91.2%, so a service filter is a genuine narrowing, not a completeness gamble.

## `specialties` (type: `array`):

Keep only planners who name these client segments. CFP Board's own live list of 10, read from the facet payload on 2026-08-12. Measured fill is 47.4% — fewer than half of all planners declare a segment at all, so this is the most aggressive filter on the form. Use it to find the practice, not to size the market.

## `languages` (type: `array`):

Keep only planners who speak these languages. CFP Board's own live list of 25, read from the facet payload on 2026-08-12. Measured fill is 94.6%, and English alone accounts for 68,308 of the 72,207 — so picking English is not a filter. The other 24 are how you build a language-matched territory list.

## `clientInvestableAssets` (type: `string`):

Keep only planners who will take a client at or below this level — the bands are cumulative, measured 2026-08-12: $0–100k returns 36,041 planners, and each step up adds the next band (42,927 / 52,131 / 62,192 / 70,568 / 72,207). Because every planner in the directory states a minimum, the top band returns the entire directory and filters nothing. Pick a LOW band to find planners who take smaller clients.

## `firmName` (type: `string`):

Only planners whose firm matches this text — "Edward Jones" returns 4,461, and "Northwestern Mutual", "Ameriprise", "Raymond James", "LPL" all work the same way. Partial matches count, so "Jones" is broader than "Edward Jones". This is the cheapest way to pull a competitor's or an acquisition target's whole advisor roster.

## `firstName` (type: `string`):

Only planners with this first name. On its own it is far too broad to be useful — pair it with Last name to verify one specific person, which is usually a 1-row, $0.004 run.

## `lastName` (type: `string`):

Only planners with this last name. With First name this is the single-person verification path: does this individual actually hold the CFP® marks, since when, and what is their CRD number.

## `requirePhone` (type: `boolean`):

Drop planners with no published phone. 73.3% of the directory carries one (measured on 6,000 rows), so expect to lose roughly a quarter of any scope. Rows dropped here are not billed.

## `requireWebsite` (type: `boolean`):

Drop planners with no published website. 82.7% of the directory carries one (measured on 6,000 rows). Turn this on when the website is the handle you enrich from. Rows dropped here are not billed.

## `includeDisclosures` (type: `boolean`):

Look every planner up in CFP Board's own verification service and fill hasDiscipline and hasBankruptcy. It is keyed by NAME rather than by id, so it costs roughly one extra request per distinct name and makes a run noticeably slower — leave it off for lead-gen runs and on for compliance and due-diligence runs. About 1.0% of professionals carry a flag (measured on 1,000 verification rows). Off = both fields are null and disclosuresChecked is false. The price per planner does not change either way.

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

Hard cap on planners returned this run, and therefore your cost ceiling: 100 planners = $0.40, 1,000 = $4.00, all 72,207 = about $289. Duplicates are dropped before billing and never count against this number. Set 0 to take everything in scope.

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

Apify Proxy settings. The datacenter pool is the default and is the rung that cleared sustained load: 25/25 on contiguous deep pages, a clean 60-page sweep, a clean 76-page California walk, and 20/20 on CFP Board's verification host. RESIDENTIAL measured 5/5 — no better, and it costs more. The site is Cloudflare-fronted, so a rotating pool is safer than one fixed IP for a 723-page walk; every request already retries on a fresh IP.

## Actor input object example

```json
{
  "states": [
    "CA"
  ],
  "cities": [],
  "zipCodes": [],
  "latitude": "",
  "longitude": "",
  "radiusMiles": 25,
  "planningServices": [],
  "specialties": [],
  "languages": [],
  "clientInvestableAssets": "any",
  "firmName": "",
  "firstName": "",
  "lastName": "",
  "requirePhone": false,
  "requireWebsite": false,
  "includeDisclosures": false,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `planners` (type: `string`):

The dataset of scraped CFP Board certified financial planners, one item per professional.

# 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 = {
    "states": [
        "CA"
    ],
    "cities": [],
    "zipCodes": [],
    "latitude": "",
    "longitude": "",
    "planningServices": [],
    "specialties": [],
    "languages": [],
    "clientInvestableAssets": "any",
    "firmName": "",
    "firstName": "",
    "lastName": "",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/cfp-board-planner-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 = {
    "states": ["CA"],
    "cities": [],
    "zipCodes": [],
    "latitude": "",
    "longitude": "",
    "planningServices": [],
    "specialties": [],
    "languages": [],
    "clientInvestableAssets": "any",
    "firmName": "",
    "firstName": "",
    "lastName": "",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/cfp-board-planner-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 '{
  "states": [
    "CA"
  ],
  "cities": [],
  "zipCodes": [],
  "latitude": "",
  "longitude": "",
  "planningServices": [],
  "specialties": [],
  "languages": [],
  "clientInvestableAssets": "any",
  "firmName": "",
  "firstName": "",
  "lastName": "",
  "maxItems": 100
}' |
apify call scrapersdelight/cfp-board-planner-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/cfp-board-planner-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/EhC95uDQAMdReLsI6/builds/Yb4CzFQu6AEfmdKOv/openapi.json
