ReachOwl Lead Finder avatar

ReachOwl Lead Finder

Pricing

Pay per usage

Go to Apify Store
ReachOwl Lead Finder

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

Product Botsify

Maintained by Community

Actor 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 rows
AI 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

FieldTypeDefaultNotes
startUrls{url}[]requiredPublic pages to crawl
targetAudiencestring—Scoring signal
locationstring—Scoring signal
keywordsstring[][]Scoring + link relevance
maxLeadsint100Cap after qualify+dedupe
minLeadScoreint50Discard below this
maxPagesPerSiteint25Per-domain crawl budget
maxRequestsPerCrawlint200Global request cap
sendToReachOwlboolfalseImport only when true
reachOwlCampaignIdstring—Required if sending
reachOwlAudienceIdstring—Optional; not used as import target (see findings)
scoreWeightsobject—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:

VariableRequiredDescription
REACHOWL_API_URLfor importBase URL, e.g. https://app.reachowl.com
REACHOWL_API_KEYfor importSanctum 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:

  1. Validates credentials via GET /api/v1/user
  2. Verifies campaign via GET /api/v1/campaigns/{id}
  3. Prefetches contacts via GET /api/v1/contacts?campaign_id=
  4. Imports each mappable lead via POST /api/v1/campaigns/{id}/contacts with { audience: [...] } — same endpoint as MCP add_campaign_contacts
  5. Never claims import success unless the API reports created > 0 or returns an audience id
  6. 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_id when native IDs are unavailable

Local development

cd apify-reachowl-lead-finder
cp .env.example .env # fill secrets only if testing import
npm install
npm test
npm run build
# Run locally (uses Apify SDK storage under ./storage)
export REACHOWL_API_URL=https://app.reachowl.com
export REACHOWL_API_KEY='id|token' # optional unless sendToReachOwl
npm run start:dev

Provide input via Apify local storage:

mkdir -p storage/key_value_stores/default
cat > 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
}
EOF
npm 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-cli
cd apify-reachowl-lead-finder
apify login
apify 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, Dockerfile
src/
main.ts
crawl/ # crawler + HTML extractors
qualify/ # lead scoring
dedupe/ # identity dedupe
reachowl/ # thin V1 client + import (MCP-compatible)
utils/
tests/
docs/