ReachOwl Lead Finder
Pricing
Pay per usage
ReachOwl Lead Finder
Find and qualify leads from public website URLs, then optionally import them into an existing ReachOwl campaign via the ReachOwl V1 API.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Product Botsify
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
ReachOwl Lead Finder (Apify Actor)
Production Apify Actor that finds leads from public website URLs, qualifies and deduplicates them, and optionally imports them into an existing ReachOwl campaign using the same V1 API contract as the ReachOwl MCP (POST /api/v1/campaigns/{id}/contacts).
Architecture
APIFY ACTOR (this project)↓Find + extract + qualify + deduplicate leads↓ReachOwl V1 API (same as MCP — not a new API layer)↓Existing campaign / audience rowsAI AGENT (separate)↓ReachOwl Skill + ReachOwl MCP↓Campaign management / Cold DM / Follow-ups / Scheduling
This Actor does not create campaigns, start outreach, or implement Cold DM / follow-ups.
See docs/FINDINGS.md for inspected API contracts and docs/AGENT_WORKFLOW.md for how an AI agent should use this Actor with MCP.
Input
| Field | Type | Default | Notes |
|---|---|---|---|
startUrls | {url}[] | required | Public pages to crawl |
targetAudience | string | — | Scoring signal |
location | string | — | Scoring signal |
keywords | string[] | [] | Scoring + link relevance |
maxLeads | int | 100 | Cap after qualify+dedupe |
minLeadScore | int | 50 | Discard below this |
maxPagesPerSite | int | 25 | Per-domain crawl budget |
maxRequestsPerCrawl | int | 200 | Global request cap |
sendToReachOwl | bool | false | Import only when true |
reachOwlCampaignId | string | — | Required if sending |
reachOwlAudienceId | string | — | Optional; not used as import target (see findings) |
scoreWeights | object | — | Advanced scoring overrides |
Example input
{"startUrls": [{ "url": "https://example.com/agents" }],"targetAudience": "real estate agents","location": "Lahore, Pakistan","keywords": ["real estate", "property dealer", "real estate agent"],"maxLeads": 50,"sendToReachOwl": true,"reachOwlCampaignId": "CAMPAIGN_ID","reachOwlAudienceId": "AUDIENCE_ID"}
Output (Dataset)
Each qualified lead is pushed to the Apify Dataset:
{"firstName": "Sara","lastName": "Ahmed","fullName": "Sara Ahmed","title": "Real Estate Agent","company": "Agency PK","email": "sara@agency.pk","phone": "923001112233","website": "https://agency.pk","linkedinUrl": null,"facebookUrl": null,"instagramUrl": null,"sourceUrl": "https://agency.pk/team","sourceDomain": "agency.pk","matchedKeywords": ["real estate", "real estate agent"],"leadScore": 85,"reachOwl": {"imported": true,"leadId": "99","campaignId": "18312","audienceId": "99"}}
Run statistics are stored in the Key-Value store under STATS / OUTPUT:
websitesProcessed,pagesVisited,leadsDiscovered,leadsQualified,leadsRejected,duplicatesRemoved,leadsSentToReachOwl,reachOwlImportFailures
Environment variables / secrets
Never hardcode credentials. On Apify, set these as Actor secrets:
| Variable | Required | Description |
|---|---|---|
REACHOWL_API_URL | for import | Base URL, e.g. https://app.reachowl.com |
REACHOWL_API_KEY | for import | Sanctum token id|plainTextToken (Bearer) |
Aliases: REACHOWL_BASE_URL, REACHOWL_TOKEN (same as MCP).
Auth header: Authorization: Bearer <token> — identical to ReachOwl MCP.
ReachOwl integration
When sendToReachOwl: true:
- Validates credentials via
GET /api/v1/user - Verifies campaign via
GET /api/v1/campaigns/{id} - Prefetches contacts via
GET /api/v1/contacts?campaign_id= - Imports each mappable lead via
POST /api/v1/campaigns/{id}/contactswith{ audience: [...] }— same endpoint as MCPadd_campaign_contacts - Never claims import success unless the API reports
created > 0or returns an audience id - On ReachOwl outage: scraped leads remain in the Apify Dataset; failures are recorded per lead
Platform mapping
- WhatsApp campaigns: require phone →
member_id{digits}@c.us+https://wa.me/{digits} - Facebook / Instagram: require a public profile/website URL; synthetic
member_idwhen native IDs are unavailable
Local development
cd apify-reachowl-lead-findercp .env.example .env # fill secrets only if testing importnpm installnpm testnpm run build# Run locally (uses Apify SDK storage under ./storage)export REACHOWL_API_URL=https://app.reachowl.comexport REACHOWL_API_KEY='id|token' # optional unless sendToReachOwlnpm run start:dev
Provide input via Apify local storage:
mkdir -p storage/key_value_stores/defaultcat > storage/key_value_stores/default/INPUT.json <<'EOF'{"startUrls": [{ "url": "https://example.com" }],"targetAudience": "real estate agents","location": "Lahore, Pakistan","keywords": ["real estate"],"maxLeads": 10,"sendToReachOwl": false}EOFnpm run start:dev
Tests
$npm test
Coverage includes normalize, score, dedupe, extract, map-to-audience, and ReachOwl import (mocked): auth failure, API failure, success, duplicates, partial failures.
Apify deployment
# Install Apify CLI if needed: npm i -g apify-clicd apify-reachowl-lead-finderapify loginapify push
Or build the Docker image used on the platform:
$docker build -f .actor/Dockerfile -t reachowl-lead-finder .
Set REACHOWL_API_URL and REACHOWL_API_KEY in the Actor’s Environment variables / secrets on Apify.
Compliance
- Only public pages; no CAPTCHA/paywall/auth bypass
- Reasonable crawl budgets (
maxPagesPerSite,maxRequestsPerCrawl) - No fabrication of contact fields
- Secrets never logged
Project layout
apify-reachowl-lead-finder/.actor/ # actor.json, input/dataset schemas, Dockerfilesrc/main.tscrawl/ # crawler + HTML extractorsqualify/ # lead scoringdedupe/ # identity dedupereachowl/ # thin V1 client + import (MCP-compatible)utils/tests/docs/