Skip to main content

Installation

pip install foxreach
Requirements: Python 3.9+

Quick Start

from foxreach import FoxReach, LeadCreate

client = FoxReach(api_key="otr_your_api_key")

# Create a lead
lead = client.leads.create(LeadCreate(
    email="[email protected]",
    first_name="Jane",
    last_name="Smith",
    company="Acme Corp",
))
print(f"Created: {lead.id}")

# List leads
page = client.leads.list(search="jane", page_size=25)
for lead in page:
    print(f"{lead.email}{lead.company}")

# Check pagination info
print(f"Page {page.meta.page}/{page.meta.total_pages}, {page.meta.total} total")

client.close()

Configuration

client = FoxReach(
    api_key="otr_...",                                  # required
    base_url="https://api.foxreach.io/api/v1",          # default
    timeout=30.0,                                        # seconds, default
    max_retries=3,                                       # retries on 429, default
)
You can also use environment variables:
export FOXREACH_API_KEY=otr_your_api_key
import os
from foxreach import FoxReach

client = FoxReach(api_key=os.environ["FOXREACH_API_KEY"])

Resources

Leads

from foxreach import LeadCreate, LeadUpdate

# List with filters
page = client.leads.list(page=1, page_size=50, search="acme", status="active")

# Get by ID
lead = client.leads.get("cld_abc123")

# Create
lead = client.leads.create(LeadCreate(
    email="[email protected]",
    first_name="Jane",
    company="Acme Corp",
    tags=["enterprise"],
))

# Update
lead = client.leads.update("cld_abc123", LeadUpdate(company="New Corp"))

# Delete
client.leads.delete("cld_abc123")

Campaigns

from foxreach import CampaignCreate, CampaignUpdate

# List
page = client.campaigns.list(status="active")

# Create (starts in draft)
campaign = client.campaigns.create(CampaignCreate(
    name="Q1 Outreach",
    timezone="America/New_York",
    daily_limit=50,
))

# Update
client.campaigns.update("cmp_xyz789", CampaignUpdate(daily_limit=100))

# Lifecycle
client.campaigns.start("cmp_xyz789")
client.campaigns.pause("cmp_xyz789")

# Add leads and accounts
client.campaigns.add_leads("cmp_xyz789", ["cld_1", "cld_2"])
client.campaigns.add_accounts("cmp_xyz789", ["acc_1"])

# Delete (must be draft)
client.campaigns.delete("cmp_xyz789")

Sequences

from foxreach import SequenceCreate, SequenceUpdate

# List steps for a campaign
steps = client.campaigns.sequences.list("cmp_xyz789")

# Add a step
step = client.campaigns.sequences.create("cmp_xyz789", SequenceCreate(
    subject="Quick question about {{company}}",
    body="Hi {{firstName}}, ...",
    delay_days=0,
))

# Update
client.campaigns.sequences.update("cmp_xyz789", "seq_1", SequenceUpdate(delay_days=3))

# Delete
client.campaigns.sequences.delete("cmp_xyz789", "seq_1")

Templates

from foxreach import TemplateCreate, TemplateUpdate

page = client.templates.list()
template = client.templates.get("tpl_abc123")
template = client.templates.create(TemplateCreate(name="Cold Intro", body="Hi {{firstName}}"))
client.templates.update("tpl_abc123", TemplateUpdate(name="Updated name"))
client.templates.delete("tpl_abc123")

Email Accounts

page = client.email_accounts.list()
account = client.email_accounts.get("acc_abc123")
client.email_accounts.delete("acc_abc123")

Inbox

from foxreach import ThreadUpdate

# List unread threads
threads = client.inbox.list_threads(is_read=False, category="interested")

# Get a thread
thread = client.inbox.get("rpl_abc123")

# Mark as read and categorize
client.inbox.update("rpl_abc123", ThreadUpdate(is_read=True, category="interested"))

Analytics

# Dashboard overview
overview = client.analytics.overview()
print(f"Leads: {overview.total_leads}, Reply rate: {overview.reply_rate}%")

# Campaign stats with daily breakdown
stats = client.analytics.campaign("cmp_xyz789")
print(f"Sent: {stats.sent}, Replied: {stats.replied}")
for day in stats.daily_stats:
    print(f"  {day.date}: {day.sent} sent, {day.replied} replied")

Pagination

List endpoints return PaginatedResponse objects:
page = client.leads.list(page=1, page_size=50)

# Iterate current page
for lead in page:
    print(lead.email)

# Check pagination metadata
print(f"Page {page.meta.page}/{page.meta.total_pages}")

# Get next page
if page.has_next_page():
    next_page = page.next_page()

# Auto-paginate through ALL results
for lead in client.leads.list(page_size=100).auto_paging_iter():
    print(lead.email)

Async Support

All operations are available asynchronously via AsyncFoxReach:
import asyncio
from foxreach import AsyncFoxReach, LeadCreate

async def main():
    async with AsyncFoxReach(api_key="otr_your_api_key") as client:
        # Create a lead
        lead = await client.leads.create(LeadCreate(email="[email protected]"))

        # Auto-paginate
        async for lead in (await client.leads.list()).auto_paging_iter():
            print(lead.email)

        # Analytics
        overview = await client.analytics.overview()
        print(f"Total leads: {overview.total_leads}")

asyncio.run(main())

Error Handling

from foxreach import (
    FoxReach,
    FoxReachError,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError,
    BadRequestError,
    ServerError,
    ConnectionError,
)

try:
    lead = client.leads.get("cld_nonexistent")
except NotFoundError:
    print("Lead not found")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except ValidationError as e:
    print(f"Validation failed: {e.response_body}")
except ServerError as e:
    print(f"Server error ({e.status_code})")
except FoxReachError as e:
    print(f"API error: {e}")
The SDK automatically retries on 429 Too Many Requests responses (up to max_retries times) with the delay specified in the Retry-After header.

Source Code