Python bindings for the Encrata email intelligence API
Project description
Encrata Python Library
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
- Documentation: docs.encrata.com
- Dashboard: encrata.com
- Email: support@encrata.com
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b8b324f9a9ce24b6ff117ad0110e34354bf4fd0b85f42077c851ad15bc9a3a1
|
|
| MD5 |
5d5149fb9331624f8e49d39280b1ff11
|
|
| BLAKE2b-256 |
a5678b8087de2436f02cf3fc625a47d9e880cf887020a773c7e7a076000a91e3
|
Provenance
The following attestation bundles were made for encrata-0.5.1.tar.gz:
Publisher:
python-ci-cd.yml on Encratahq/encrata-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
encrata-0.5.1.tar.gz -
Subject digest:
3b8b324f9a9ce24b6ff117ad0110e34354bf4fd0b85f42077c851ad15bc9a3a1 - Sigstore transparency entry: 2066239539
- Sigstore integration time:
-
Permalink:
Encratahq/encrata-python@616ef8bb7293642a0ce87b6bb9fcb5a21b9eb518 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/Encratahq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-ci-cd.yml@616ef8bb7293642a0ce87b6bb9fcb5a21b9eb518 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
302e5b13b67fa5ca94605eeeaf83f09a51d8934fbca4976c3f8333336ca3c401
|
|
| MD5 |
98382145d666bd6e1de0196b6e5496bf
|
|
| BLAKE2b-256 |
65f944d5154b0e267338f170f5216a6c418e5986603816d8c9d118138d733210
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
encrata-0.5.1-py3-none-any.whl -
Subject digest:
302e5b13b67fa5ca94605eeeaf83f09a51d8934fbca4976c3f8333336ca3c401 - Sigstore transparency entry: 2066239650
- Sigstore integration time:
-
Permalink:
Encratahq/encrata-python@616ef8bb7293642a0ce87b6bb9fcb5a21b9eb518 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/Encratahq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-ci-cd.yml@616ef8bb7293642a0ce87b6bb9fcb5a21b9eb518 -
Trigger Event:
push
-
Statement type: