Skip to main content

Python bindings for the Encrata email intelligence API

Project description

Encrata Python Library

PyPI version Python License: MIT

The Encrata Python library provides convenient access to the Encrata API from applications written in Python. Look up any person by their email address — get their name, company, job title, social profiles, breach history, and more.

API Documentation

See the Encrata API docs.

Installation

pip install encrata

Requirements

  • Python 3.9+
  • httpx (installed automatically)

Usage

The library needs to be configured with your account's API key, available in your Encrata Dashboard.

from encrata import Encrata

client = Encrata("enc_live_...")

The client keeps a pooled HTTP connection open. Reuse a single instance for the lifetime of your application, or use it as a context manager to close the pool automatically:

with Encrata("enc_live_...") as client:
    person = client.lookup("elon@tesla.com")

Look up a person by email

person = client.lookup("elon@tesla.com")

print(person.name)        # "Elon Musk"
print(person.company)     # "Tesla"
print(person.role)        # "CEO"
print(person.location)    # "Austin, Texas"
print(person.validity)    # "valid"

Each lookup costs 1 credit. Cached results within 24 hours are served from cache.

Select specific fields

Only return the fields you need to minimize response size:

person = client.lookup("satya@microsoft.com", fields=["name", "company", "role", "socials"])

print(person.name)             # "Satya Nadella"
print(person.socials.linkedin) # "https://linkedin.com/in/satyanadella"

Available fields: name, email, company, role, industry, location, bio, age, gender, education, phone, photo, validity, socials, breaches, registered_services, news, publications.

Validate an email address (free)

Check if an email is deliverable without using any credits:

result = client.validate("satya@microsoft.com")

print(result.validity)  # "valid", "invalid", "disposable", or "unknown"
print(result.message)   # "Email is deliverable and valid."

Check data breaches (free)

See if an email has been exposed in known data breaches:

report = client.breaches("sundar@google.com")

print(report.count)         # 3
print(report.services)      # ["Adobe", "LinkedIn", "Dropbox"]
print(report.exposed_data)  # ["email", "password", "username"]

OSINT Lookups

Enrich IPs, phone numbers, domains, and companies. Each costs 1 credit.

IP intelligence

ip = client.ip("8.8.8.8")

print(ip.location.country)  # "United States"
print(ip.asn.name)          # "GOOGLE"
print(ip.threat.malicious)  # False

Phone lookup

phone = client.phone_lookup("+14155552671")

print(phone.carrier.name)        # "T-Mobile"
print(phone.country.name)        # "United States"
print(phone.validation.is_voip)  # False

Domain search

domain = client.domain_search("tesla.com")

print(domain.whois.registrar)      # "MarkMonitor Inc."
print(domain.ssl.issuer)           # "DigiCert Inc"
print(domain.threat_intel.malicious)  # False

Company search

company = client.company_search("Tesla")

print(company.profile.name)            # "Tesla, Inc."
print(company.profile.ticker)          # "TSLA"
print(company.profile.employee_count)  # 140000
for person in company.results:
    print(person.name, person.role, person.email)

Costs 1 credit per result returned.

Google dork search

search = client.google_search("site:example.com filetype:pdf")

for r in search.results:
    print(r.title, r.url)

# Free OSINT enrichment (no extra credits)
print(search.enrichment["query_type"])  # "domain"

Costs 1 credit per search; the enrichment block is free.

Dark web search

dw = client.darkweb_search("user@example.com")

print(dw.total)  # 42
for hit in dw.results:
    print(hit.source, hit.title, hit.emails)

# Paginate (20 results per page)
page2 = client.darkweb_search("user@example.com", offset=20)

Costs 1 credit per 100 results.

Scrape a web page

page = client.scrape("https://example.com/pricing")

print(page.status_code)        # 200
print(page.content)            # "# Pricing\n\nSimple, transparent pricing..."
print(page.metadata["title"])  # "Pricing — Example"

# Skip JS rendering for static pages
page = client.scrape("https://example.com", render_js=False)

Costs 2 credits; failed scrapes are auto-refunded.

Extract structured data

# Selector mode — JSON keyed by your CSS/XPath map
result = client.extract(
    "https://example.com/product/123",
    mode="selectors",
    selectors={"title": "h1", "price": ".price"},
)
print(result.extracted)  # {"title": "Wireless Headphones", "price": "$129.00"}

# Markdown mode (default), wait for lazy content
result = client.extract("https://example.com", wait_for=".loaded", timeout=45000)
print(result.extracted)

Costs 3 credits; failed extractions are auto-refunded.

Capture a screenshot

shot = client.screenshot("https://example.com", full_page=True, format="png")

import base64
with open("page.png", "wb") as f:
    f.write(base64.b64decode(shot.screenshot))

# Capture a single element instead
shot = client.screenshot("https://example.com", selector="#hero")

Costs 5 credits; failed captures are auto-refunded.

Face search

Match a probe image against your watchlist by public image URL:

result = client.face_search("https://example.com/photo.jpg")

print(result.matched)          # True
print(result.faces_detected)   # 1
for match in result.matches:
    print(match.name, match.probability)  # "Elon Musk" 0.97

# Tune the match threshold (0.0–1.0)
result = client.face_search("https://example.com/photo.jpg", threshold=0.9)

Costs 1 credit per search.

Bulk operations

Process many queries in a single request. Bulk endpoints stream results back over Server-Sent Events, so you can begin handling hits before the batch finishes. Each query costs 1 credit.

Bulk email lookup

Enrich up to 1,000 emails — results stream in one Person at a time:

for person in client.bulk_lookup(["elon@tesla.com", "satya@microsoft.com"]):
    print(person.name, person.company)

# Limit fields to shrink each result
for person in client.bulk_lookup(emails, fields=["name", "company"]):
    print(person.name)

Bulk OSINT searches

Run up to 100 queries per call for Google, company, domain, or IP intelligence. Each collects the full stream into a single BulkSearchResponse:

res = client.bulk_google_search(["site:tesla.com", "site:microsoft.com"])
print(res.credits_used)  # 2
for item in res.results:
    print(item)

client.bulk_company_search(["Tesla", "Microsoft"])
client.bulk_domain_search(["tesla.com", "microsoft.com"])
client.bulk_ip_search(["8.8.8.8", "1.1.1.1"])

Monitoring

Set up monitors to track changes in email intelligence over time. When a person changes jobs, gets a new title, or appears in a breach — you'll know.

Create a monitor

monitor = client.create_monitor(
    "Sales Leads",
    emails=["satya@microsoft.com", "jensen@nvidia.com"],
    frequency="weekly",
)

print(monitor.id)           # "mon_abc123..."
print(monitor.email_count)  # 2

List monitors

monitors = client.list_monitors()
for m in monitors:
    print(f"{m.name}: {m.email_count} emails, {m.status}")

Trigger a run

result = client.trigger_run(monitor.id)
print(result["run_id"])   # "run_xyz789..."
print(result["status"])   # "running"

Get run results

runs, total = client.list_runs(monitor.id)
for run in runs:
    print(f"Run {run.id}: {run.changes_detected} changes")

# Get detailed results for a run
snapshots, total = client.get_run_results(monitor.id, runs[0].id, changes_only=True)
for snap in snapshots:
    print(f"{snap.email}: changes={snap.changes}")

List all runs across monitors

all_runs, total = client.list_all_runs(limit=10)
all_results, total = client.list_all_results(changes_only=True)

These paginated endpoints also have iterator forms that fetch every page for you — see Automatic pagination below.

Contact Lists

Manage reusable target lists that can be used as data sources for monitors. Lists hold one type of target: email, phone, domain, ip, company, or darkweb.

Create a contact list

# Email list (the default type)
contact_list = client.create_contact_list(
    "Engineering Team",
    emails=["satya@microsoft.com", "sundar@google.com"],
)
print(contact_list.id, contact_list.list_type)

# A non-email list — pass `type` and use `targets`
domains = client.create_contact_list(
    "Competitor Domains",
    type="domain",
    targets=["competitor1.com", "competitor2.io"],
)

Manage list emails

# List all contact lists (optionally filter by type)
lists = client.list_contact_lists()
domain_lists = client.list_contact_lists(type="domain")

# Add targets
client.add_contact_list_emails(contact_list.id, ["tim@apple.com"])

# List targets in a list
emails = client.list_contact_list_emails(contact_list.id)

# Remove targets
client.delete_contact_list_emails(contact_list.id, ["sundar@google.com"])

# Delete the list
client.delete_contact_list(contact_list.id)

Use a list as a monitor source

monitor = client.create_monitor("Team Monitor", list_id=contact_list.id)

Workflows

Automate multi-step OSINT pipelines with triggers and steps. Workflows are managed on encrata.com/api/workflows — keep separate from the per-lookup endpoints above.

List, get, create, update

# List workflows (paginated → (workflows, total))
workflows, total = client.list_workflows(status="active")
for wf in workflows:
    print(wf.id, wf.name, wf.status)

# Fetch one
wf = client.get_workflow(workflows[0].id)

# Create from steps, or clone a template
wf = client.create_workflow(
    "Lead enrichment",
    trigger={"type": "webhook"},
    steps=[
        {"id": "step1", "type": "email_lookup", "config": {"field": "email"}},
        {"id": "step2", "type": "company_lookup", "config": {"field": "step1.company"}},
    ],
)

# Update (creates a new version). Returns the updated workflow;
# if the API echoes nothing, you still get a Workflow with the id set.
wf = client.update_workflow(wf.id, name="Renamed", status="paused")

Runs, templates, and secrets

runs, total = client.list_workflow_runs(workflow_id=wf.id)
run = client.get_workflow_run(runs[0].id)
for step in run.steps:
    print(step.type, step.status, step.credits_used)

templates = client.list_workflow_templates(category="enrichment")

# Secrets — referenced in webhook steps as {{secrets.NAME}} (values never returned)
secrets = client.list_workflow_secrets()
client.create_workflow_secret("SLACK_WEBHOOK_URL", "https://hooks.slack.com/...")
client.delete_workflow_secret("SLACK_WEBHOOK_URL")

API keys

Manage the keys for your account. The full key is only returned once, at creation.

keys = client.list_keys()
for k in keys:
    print(k.id, k.name, k.key_preview, k.credits_used)

new_key = client.create_key("Production")
print(new_key.key)  # store this — it is never shown again

client.revoke_key(new_key.id)                  # soft disable
client.revoke_key(new_key.id, permanent=True)  # permanent delete

Automatic pagination

List endpoints return one page at a time along with a total count. To walk an entire history without managing limit/offset yourself, use the matching iterator helpers — they fetch each subsequent page on demand as you iterate:

# Every run across all monitors
for run in client.iter_all_runs():
    print(run.id, run.status)

# Every result across all monitors (only the ones that changed)
for snapshot in client.iter_all_results(changes_only=True):
    print(snapshot.email, snapshot.has_changes)

# Scoped to a single monitor or run
for run in client.iter_runs(monitor.id):
    print(run.id)

for snapshot in client.iter_run_results(monitor.id, run.id):
    print(snapshot.email)

Pass limit to control the page size used under the hood (default 100):

for run in client.iter_all_runs(limit=250):
    ...

The async client exposes the same helpers as async iterators:

async for run in client.iter_all_runs():
    print(run.id)

Handling exceptions

from encrata import Encrata, AuthenticationError, InsufficientCreditsError

client = Encrata("enc_live_...")

try:
    person = client.lookup("satya@microsoft.com")
except AuthenticationError:
    print("Invalid API key")
except InsufficientCreditsError:
    print("No credits remaining — top up at encrata.com/settings/billing")
Exception Cause
AuthenticationError Invalid or missing API key
InsufficientCreditsError Account has 0 credits remaining
InvalidRequestError Malformed request (e.g. invalid email)
RateLimitError Too many requests
APIConnectionError Network connectivity issue
APIError Unexpected server error

Configuration options

client = Encrata(
    "enc_live_...",
    base_url="https://api.encrata.com",  # default
    timeout=30,                           # request timeout in seconds
    max_retries=3,                        # retries for transient failures
)

Transient failures (HTTP 429, 500, 502, 503, 504, timeouts, and connection errors) are retried automatically using exponential backoff with full jitter. A Retry-After response header is honored and capped at 30 seconds.

Force fresh lookup

Bypass the 24-hour cache to get the latest data:

person = client.lookup("elon@tesla.com", nocache=True)

Async

An async client, AsyncEncrata, exposes the same methods with async/await. It shares one connection pool and can run many lookups concurrently

import asyncio
from encrata import AsyncEncrata

async def main():
    async with AsyncEncrata("enc_live_...") as client:
        person = await client.lookup("elon@tesla.com")
        print(person.name)

        # Run many lookups concurrently (bounded by max_concurrency):
        people = await client.lookup_many([
            "satya@microsoft.com",
            "sundar@google.com",
            "tim@apple.com",
        ])
        for p in people:
            print(p.name)

asyncio.run(main())

MCP (Model Context Protocol)

Encrata also provides an MCP server for AI agent frameworks like Claude, Cursor, and Windsurf. Add this to your MCP configuration:

{
  "mcpServers": {
    "encrata": {
      "url": "https://api.encrata.com/mcp",
      "headers": {
        "Authorization": "Bearer enc_live_..."
      }
    }
  }
}

Support

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

encrata-0.5.1.tar.gz (36.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

encrata-0.5.1-py3-none-any.whl (37.9 kB view details)

Uploaded Python 3

File details

Details for the file encrata-0.5.1.tar.gz.

File metadata

  • Download URL: encrata-0.5.1.tar.gz
  • Upload date:
  • Size: 36.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for encrata-0.5.1.tar.gz
Algorithm Hash digest
SHA256 3b8b324f9a9ce24b6ff117ad0110e34354bf4fd0b85f42077c851ad15bc9a3a1
MD5 5d5149fb9331624f8e49d39280b1ff11
BLAKE2b-256 a5678b8087de2436f02cf3fc625a47d9e880cf887020a773c7e7a076000a91e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for encrata-0.5.1.tar.gz:

Publisher: python-ci-cd.yml on Encratahq/encrata-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file encrata-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: encrata-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 37.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for encrata-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 302e5b13b67fa5ca94605eeeaf83f09a51d8934fbca4976c3f8333336ca3c401
MD5 98382145d666bd6e1de0196b6e5496bf
BLAKE2b-256 65f944d5154b0e267338f170f5216a6c418e5986603816d8c9d118138d733210

See more details on using hashes here.

Provenance

The following attestation bundles were made for encrata-0.5.1-py3-none-any.whl:

Publisher: python-ci-cd.yml on Encratahq/encrata-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page