Psychology Today Scraper | Therapist Leads Data
Pricing
from $15.00 / 1,000 results
Psychology Today Scraper | Therapist Leads Data
Therapist & psychiatrist leads from Psychology Today, by city: names, credentials, phones, addresses, profile links — plus practice websites, emails & social profiles with lead details on. Clean JSON streamed to your dataset in real time. Free plan: 2 results per run.
Pricing
from $15.00 / 1,000 results
Rating
0.0
(0)
Developer
Emmanuel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Psychology Today Real-Time Data
⚠️ Paid only: free Apify accounts can export a maximum of 2 results per run. Upgrade to a paid Apify plan for full, unlimited data. See Free tier vs paid plans.
Collect mental-health professional data from the Psychology Today directory at scale. Search therapists and psychiatrists by city in one run, pull full profile details, and enrich every result with practice websites, contact emails, and social profiles — all as clean, structured JSON streamed to your Apify dataset in real time.
Built for therapists growing a practice, referral coordinators, healthcare recruiters, telehealth companies, EHR/practice-tool vendors, and agencies that need reliable, contactable provider lists — without slow, expensive manual research.
Why this Actor
| Psychology Today Real-Time Data | Typical directory tooling | |
|---|---|---|
| Speed | Fast per-result collection, streaming output | Often 5–15 s per profile |
| Memory | 512 MB default — rows stream out as they are collected | 2–4 GB+ |
| Setup | Organized input UI, run immediately | Fragile & high-maintenance |
| Output | Structured JSON, LLM-ready | Often messy HTML |
| Scale | Large runs — 10,000+ items per run | Often capped lower |
| Coverage | Multi-city, two categories, deep per-location coverage | Usually one city at a time |
What it does — outcomes
- Build provider lists by city: every therapist or psychiatrist listed for a location, with name, credentials, address, and phone.
- Generate outreach-ready leads: with lead details enabled, each row gains the practice website, contact emails, and social profiles when they can be discovered.
- Enrich a list you already have: paste profile URLs and get the same structured rows back.
- Pipeline-friendly output: one flat JSON row per professional — no nesting, no cleanup, straight into a CRM, sheet, or AI workflow.
- Predictable runtime: results are never filtered. You always get one row per professional found, whether or not lead details were found — so cost and time per 1,000 rows stay predictable.
Who it is for
| Persona | What they get |
|---|---|
| Therapists & practice owners | Map competitors in a city, build referral networks, find practices without a website (your web-design or SEO pitch). |
| Referral & intake coordinators | Build vetted referral lists by specialty and city fast. |
| Healthcare recruiters | Source licensed professionals (LCSW, LMFT, PhD, PsyD, MD) by location with phones and practice sites. |
| Telehealth & digital-health companies | Build provider supply maps city by city as you launch markets. |
| Practice-management & EHR vendors | Prospect practices that match your ICP with direct contact points. |
| Marketing agencies | Local-provider prospect lists for ad, web, and reputation clients. |
| Researchers & analysts | Snapshot the mental-health provider landscape of any US city. |
| AI/automation builders | Clean JSON + MCP support + webhooks make it a drop-in data tool for agents. |
Use cases
- Lead generation for therapy-adjacent services: billing, EHR, CE training, malpractice insurance, office space, supervision.
- Recruiting: build city-by-city candidate pools with phones and practice websites.
- Referral network building: find colleagues by specialty for cross-referrals.
- Market mapping: count providers per city/specialty before opening a clinic or launching telehealth.
- Competitive research: see which practices dominate a metro, and which have no website.
- Directory enrichment: merge emails/socials into your existing CRM rows via profile URLs.
- Outreach personalization: credentials + specialties + practice name per row = contextual first lines.
- AI agent pipelines: run via MCP from Claude, ChatGPT, or any MCP client; results land in your dataset and webhooks.
Features
🔎 Directory search — on by default
Search the Psychology Today directory by category (therapists / psychiatrists), optional specialty filter (e.g. anxiety, depression, ADHD), and city in City, ST format. Run multiple city searches in one run, with deep per-location coverage.
🔗 Profile URLs
Already have a list of Psychology Today links? Paste them and get full structured rows — ideal for enriching or de-duplicating an existing database.
🎯 Enable lead details (opt-in)
Enrich every result with:
- Practice website — resolved even when the directory hides it behind an interstitial.
- Contact emails — harvested from the practice website (homepage + contact/about pages), including obfuscated and protected addresses.
- Social profiles — Instagram, Facebook, LinkedIn, X/Twitter and more, as flat columns and a
socials[]list.
Every business is exported even if no lead details are found — lead details are an enhancement, not a filter. Runtime stays predictable: each result just takes a little extra time when this option is on.
⚙️ Output & limits
searchMaxPages— directory depth per task.concurrency— how many lead-detail enrichments run in parallel (1–4).- Webhooks — push every record to your URL the moment it is collected.
Input schema (full reference)
| Field | Type | Default | Description |
|---|---|---|---|
enableSearch | boolean | true | Enable directory search by location. |
searchTasks | array of objects | [{category:"therapists", location:"New York, NY", maxResults:10}] | The one place to define searches — one row per city/category, click + Add for each extra city. Each item: category (therapists | psychiatrists), location (required, "City, ST" format, e.g. "New York, NY"), maxResults (optional integer, caps that row only), specialty (optional string, e.g. "anxiety"). |
enableScrapeByUrl | boolean | false | Enable collecting specific profile URLs. |
scrapeUrls | array of strings | [] | Psychology Today profile URLs (…/us/therapists/… or …/us/psychiatrists/…). |
enableLeadDetails | boolean | false | Enrich every result with practice website, emails, and socials. Results are never filtered — every professional is exported regardless. Adds a little extra time per result. |
searchMaxPages | integer (1–15) | 5 | Search depth per task — higher values reach deeper into the results list for each location. |
concurrency | integer (1–4) | 2 | Parallel lead-details enrichments. |
webhookUrl | string | "" | Optional URL that receives every record in real time (see Webhooks). |
webhookFormat | json | slack | "json" | Webhook payload format. |
proxyConfiguration | object | Apify RESIDENTIAL, US | Apify proxy settings. Change only if you need a different country. |
Controlling how many results you get
One control you actually need: Max results on each search task. Three cities at 100 each give you up to 300 rows — nothing else to configure.
Every run states its plan up front so the dataset is never a surprise:
Search plan: 2 search(es) — 20 row(s) requested.
Leave a task's Max results empty to have that search collect everything its city offers (up to Max pages per search).
There is also a hard ceiling on the whole run — useful for cost control on very large jobs — but it is deliberately not part of the Console form, because a second visible limit only caused truncated runs. The actor owner sets it with the RUN_MAX_ITEMS env var; any leftover maxItems value in a previously saved input is ignored. If the ceiling ever applies, the run says so in the log and in the OUTPUT:
Your 3 searches ask for 300 row(s), but this run is limited to 100 — only 100 row(s) will be exported and the remaining searches are skipped.Run limit of 100 row(s) reached — 2 remaining search(es) skipped.
Example input — therapists in two cities with lead details:
{"enableSearch": true,"searchTasks": [{ "category": "therapists", "specialty": "anxiety", "location": "New York, NY", "maxResults": 100 },{ "category": "psychiatrists", "location": "Chicago, IL", "maxResults": 50 }],"enableLeadDetails": true,"webhookUrl": "","webhookFormat": "json"}
Example input — enrich profile URLs you already have:
{"enableSearch": false,"enableScrapeByUrl": true,"scrapeUrls": ["https://www.psychologytoday.com/us/therapists/jane-doe-example-ny/1234567"],"enableLeadDetails": true}
Output schema (field-by-field)
One flat JSON row per professional. Every row includes featureType and scrapedAt so you can filter, join, and pipe into any workflow.
| Field | Type | Description |
|---|---|---|
type | string | Always "therapist" (one row per professional). |
platform | string | Always "psychologytoday". |
featureType | string | "search" (directory search) or "scrape_by_url" (profile URL). |
name | string | Professional's name as listed. |
practiceName | string | null | Practice or group name when listed. |
profileUrl | string | Directory profile link. |
profileId | string | null | Numeric profile identifier. |
title | string | null | Credentials line (e.g. "Clinical Social Work/Therapist, LCSW"). |
specialty | string | null | Specialty filter this row was collected under. |
specialties | string[] | null | Practice areas listed on the profile. |
phone | string | null | Phone (formatted). |
website | string | null | Practice website (lead details). |
email | string | null | Best contact email found (lead details). |
emails | string[] | null | All emails found (lead details). |
socials | string[] | null | Social profile URLs (lead details). |
instagram / facebook / linkedin / twitter | string | null | Flattened social columns (lead details). |
street / city / state / zipCode | string | null | Address parts. |
location | string | null | Street, city, state, ZIP joined. |
lat / lng | number | null | Coordinates when published. |
category | string | null | therapists or psychiatrists. |
searchSpecialty / searchLocation | string | null | The task that produced this row. |
searchTaskIndex | integer | null | 0-based index of the producing search task. |
searchTaskLabel | string | null | Human-readable task label, e.g. "therapists | anxiety | New York, NY". |
leadDetails | boolean | True when lead details were collected for this row. |
scrapedAt | string | ISO timestamp of collection. |
Sample row:
{"type": "therapist","platform": "psychologytoday","featureType": "search","name": "Jane Doe, PhD","practiceName": "Brooklyn Mindful Health","profileUrl": "https://www.psychologytoday.com/us/therapists/jane-doe-example-ny/1234567","profileId": "1234567","title": "Psychologist, PhD","phone": "(555) 123-4567","website": "https://brooklynmindful.example.com","email": "hello@brooklynmindful.example.com","emails": ["hello@brooklynmindful.example.com"],"socials": ["https://www.instagram.com/brooklynmindful"],"instagram": "https://www.instagram.com/brooklynmindful","street": "123 Example St, Suite 4","city": "Brooklyn","state": "New York","zipCode": "11201","location": "123 Example St, Suite 4, Brooklyn, New York, 11201","lat": 40.6932,"lng": -73.9903,"category": "therapists","specialty": "anxiety","searchLocation": "New York, NY","searchTaskIndex": 0,"searchTaskLabel": "therapists | anxiety | New York, NY","leadDetails": true,"scrapedAt": "2026-09-24T12:00:00.000Z"}
Webhooks
Every record is always saved to the dataset. A webhook is an additional real-time push: as soon as a row is collected it is also POSTed to your URL — perfect for CRMs, Zapier/Make, Google Sheets, or Slack.
Setup:
- Put your URL in
webhookUrl(must behttp://orhttps://). - Choose
webhookFormat:json— the full record object (same shape as the dataset row above).slack— a Slack-friendly{ "text": "..." }message with name, category, location, phone, website, email, and profile link.
Delivery semantics:
- One POST per record, sent immediately after the record is saved to the dataset (streaming, not batched).
- 15 s timeout per delivery, 1 retry. Delivery failures are logged and never stop the run or affect the dataset.
- Payloads contain record data only — never internal collection details.
Slack payload example:
{"text": ":stethoscope: *Jane Doe, PhD*\n*Category:* therapists • *Specialty:* anxiety\nBrooklyn, New York\n*Phone:* (555) 123-4567\n*Website:* https://brooklynmindful.example.com\n*Email:* hello@brooklynmindful.example.com\n<https://www.psychologytoday.com/us/therapists/...|View profile>"}
Free tier vs paid plans
| Plan | Behavior |
|---|---|
| Free Apify accounts | Capped at 2 results per run (default). The run finishes gracefully with a clear warning — it never crashes. |
| Paid Apify plans | Full, unlimited output. No caps, no changes. |
- The cap applies to the whole run, across all features.
- The run's OUTPUT summary always includes a transparent
paywallobject (detected,isPaying,pricingTier,limited,blocked) so you can verify how a run was treated. - Owner-side environment variables (
FREE_TIER_MODE,FREE_TIER_MAX_ITEMS) can tune this without code changes — see the private notes shipped with this actor.
Run summary (OUTPUT)
Each run writes a JSON summary to the key-value store under OUTPUT:
{"totalPushed": 2,"durationMs": 18420,"errors": [],"spendingLimitReached": false,"paywall": {"detected": true,"isPaying": false,"pricingTier": "FREE","limited": true,"blocked": false,"freeTierMaxItems": 2}}
spendingLimitReachedistruewhen the run stopped early because it reached your max-pay limit for the run — the actor never spends beyond what you approved.
LLM & MCP integration
Apify MCP (Model Context Protocol)
Use the Apify MCP server so AI assistants can run this actor:
Typical MCP tool flow:
→ Assistant decides it needs therapist leads in Austin→ MCP calls this Actor with searchTasks=[{location:"Austin, TX", category:"therapists", maxResults:50}]→ MCP reads dataset items→ Assistant builds the outreach list / analysis / report
Suggested prompts for your MCP client:
- "Get me 50 therapists in Miami with phones and emails."
- "Find psychiatrists in Seattle and put them in a table with website and email."
- "Compare the number of therapists vs psychiatrists in Boston."
Direct API
curl -X POST "https://api.apify.com/v2/acts~run" \-H "Authorization: Bearer $APIFY_TOKEN" \-d '{"actId": "your-actor-id","input": { "enableSearch": true, "searchTasks": [{ "location": "Denver, CO", "maxResults": 25 }] }}'
Cost & speed
Measured on a real run (two cities, 10 results each, lead details on — 20 rows in ~4 minutes):
| What | Measured | Notes |
|---|---|---|
| Data traffic through the proxy | ~1 MB per enriched result | The lead-details floor for this source |
| Practice-site lookups | ~0.2 MB per result | Kept off the paid route by default, with one paid attempt as a fallback |
| Startup | ~0.5 MB per run | Fixed, one-off |
| Runtime | ~2–6 s per enriched result | Turn lead details off for pure directory pulls |
What this means for large runs: lead details are the cost driver, not the number of cities — a 1,000-row job with lead details moves roughly a gigabyte, while the same 1,000 rows without lead details cost about as much as a single city page. If you are running at scale:
- Turn Enable lead details off for bulk directory pulls and enrich only the shortlist.
- Raise concurrency (1–4) — faster runs, same traffic.
- The actor owner can point the run at their own proxy pool (owner env vars) or set a run-wide
RUN_MAX_ITEMSceiling. SetTRAFFIC_REPORT=1to log a per-lane traffic breakdown (requests and megabytes, host-free) on every run so cost is never a guess.
FAQ
Which locations are supported?
United States cities, in "City, ST" format (e.g. "New York, NY", "Austin, TX"). Always include the two-letter state.
What's the difference between therapists and psychiatrists? Two separate directory sections. Therapists cover counselors, social workers, psychologists; psychiatrists are medical doctors who can prescribe. Pick per task.
Are results filtered if a professional has no email or website? No. Lead details are an enhancement: every professional is exported, and the lead-detail fields stay empty when nothing is found. This keeps runtime per 1,000 rows predictable.
How fast is it? Directory rows stream immediately. Lead details add a little extra time per result (website resolution + contact scan). With concurrency 2, expect roughly 2–4 s per result when lead details are on; turn them off for pure directory listings.
How many results can I get?
Up to the Max results you set on each search task (leave it empty for everything a city offers) — so three cities at 100 gives up to 300 rows. A city listing typically shows ~30 professionals at a time, so raise searchMaxPages for deep runs and combine cities via searchTasks.
Why do some rows have no email? The directory routes contact through its own form and some practices publish no direct email. When lead details are enabled the actor checks the practice website (homepage + contact/about pages) before giving up.
Is this legal to use? The actor collects publicly listed directory information. Always comply with Psychology Today's Terms of Use and applicable privacy laws (CAN-SPAM, TCPA, GDPR where relevant) when using the data for outreach.
Does it work on the free Apify plan? Partially — free accounts are capped at 2 results per run. Upgrade to any paid Apify plan for full, unlimited data.
Why did my run stop early with "spending limit reached"? You set a max pay limit for the run in Apify. The actor respects it and stops gracefully — no charges beyond your limit.
Can I get data pushed to me instead of polling the dataset?
Yes — set webhookUrl and every record arrives the moment it is collected.
Does it work with Make / Zapier / n8n? Yes, via webhooks (real-time per record) or by polling the dataset export URL after the run.
Can I run it for multiple cities at once?
Yes — add rows to searchTasks; tasks run in sequence and rows are tagged with searchTaskLabel so you can tell them apart.
How fresh is the data? Every run queries the live directory in real time — no stale caches.
What proxy should I use? The default Apify RESIDENTIAL (US) proxy is recommended and pre-configured. A country-specific residential pool is used automatically when configured by the actor owner.
Troubleshooting
| Symptom | Fix |
|---|---|
No directory for "X" | Use the "City, ST" format; very small towns may have no dedicated listing — try the nearest metro. |
| Run ends with 0 results but no error | Raise searchMaxPages; check that the category/specialty combination exists in that city. |
| Fewer rows than the sum of your searches | A run-wide ceiling is in force (owner's RUN_MAX_ITEMS env var) — the log and run output name the exact numbers. |
| Run feels expensive for a small result count | Lead details are the traffic-heavy part of a run (each enriched result reads an extra source page and the practice's own site). Turn Enable lead details off for bulk directory pulls, and see Cost & speed. |
| Emails missing on many rows | Some practices publish no email. leadDetails: true + higher concurrency improves coverage; rows are exported either way. |
Free tier limit reached message | You are on a free Apify plan — the run is capped at 2 results. Upgrade for full data. |
Spending limit for this run reached | Your max-pay limit for the run was hit. Raise it in the run settings if you want more results. |
Limitations & good to know
- US directory only.
- The directory routes member contact through its own form; direct emails come from the practice website when it is discoverable.
- Search depth per task is capped at 15 — use multiple tasks for very large metros.
- Review data is not included; rows are one-per-professional for clean tabular output.
Resources
Questions or a custom data need? Reach out via the Apify Actor page.