Skip to main content

Enrichfold

Provider-neutral, provenance-first entity enrichment for people and companies.

enrichfold is an offline-first core, not a scraping product: applications supply their own discovery providers and credentials. An optional Keenable adapter provides web search when explicitly configured. Every accepted attribute retains its source URL, observation time, and confidence, so downstream systems can decide whether a result is suitable for an automated action or requires review.

from enrichfold import Entity, EnrichmentPipeline, Evidence

class CompanyProvider:
    def discover(self, entity):
        return [Evidence(
            source_url="https://example.com/team",
            observed_at="2026-08-20T12:00:00Z",
            confidence=0.94,
            attributes={"industry": "software", "company_size": "51-200"},
        )]

company = Entity.company(domain="example.com")
result = EnrichmentPipeline([CompanyProvider()]).enrich(company)
print(result.attributes["industry"].value)  # software

Evidence, conflicts, and review gates

enrichfold keeps provider I/O outside the library. Its core turns supplied evidence into deterministic decisions while retaining disagreements for a human approval flow. A conflicting value is never silently accepted:

from enrichfold import Claim, Evidence, reconcile_claims

result = reconcile_claims([
    Claim(
        field="industry",
        value="software",
        evidence=Evidence(
            source_url="https://acme.example/about",
            observed_at="2026-08-20T12:00:00Z",
            confidence=0.91,
        ),
    ),
    Claim(
        field="industry",
        value="retail",
        evidence=Evidence(
            source_url="https://directory.example/acme",
            observed_at="2026-08-20T12:00:00Z",
            confidence=0.88,
        ),
    ),
])

industry = result.fields["industry"]
assert industry.value == "software"          # stable suggested value
assert industry.status == "needs_review"     # do not automate an action
assert result.requires_review is True

Claim(kind="inferred", ...) also requires review even with no competing claim. This distinction makes it possible to keep model-produced hypotheses without presenting them as observed facts.

Multi-provider research runs

ResearchEngine is the boundary for applications that call several research providers. It runs caller-owned adapters concurrently, reserves generic units before starting work, retains each provider outcome, and returns an explicit coverage/review state. It does not make network calls itself.

from enrichfold import (
    Claim,
    Evidence,
    Entity,
    ProviderOutput,
    ProviderSpec,
    ResearchBudget,
    ResearchEngine,
)

def official_site(entity):
    # The host application owns this adapter, its HTTP client, and credentials.
    return ProviderOutput(
        claims=(Claim(
            field="industry",
            value="software",
            evidence=Evidence(
                source_url="https://example.com/about",
                observed_at="2026-08-20T12:00:00Z",
                confidence=0.9,
            ),
        ),),
        usage_units=2,
    )

result = ResearchEngine(
    [ProviderSpec("official-site", official_site, reserved_units=2)],
    budget=ResearchBudget(max_units=5),
).run(Entity.company(domain="example.com"), requested_fields=("industry",))

assert result.status in {"completed", "partial", "needs_review", "failed"}
assert result.budget.reserved_units == 2

An optional EvidenceValidator can return EvidenceVerdict("needs_review", reason) for a weak source or EvidenceVerdict("rejected", reason) to keep it out of resolution. In either case, the result preserves the original claim, source URL, and verdict in evidence_assessments.

Keenable search provider

KeenableProvider uses the same REST search contract as Scrapefold's Keenable engine. It reads KEENABLE_API_KEY by default and performs one search per entity. Because search results are sources rather than verified attributes, the caller maps them to claims explicitly:

from enrichfold import (
    Claim,
    Entity,
    Evidence,
    KeenableProvider,
    ProviderSpec,
    ResearchEngine,
)

def claims(entity, results):
    for result in results:
        yield Claim(
            field="summary",
            value=result["description"],
            kind="inferred",
            evidence=Evidence(
                source_url=result["url"],
                observed_at=result["acquired_at"],
                confidence=0.8,
                provider="keenable",
            ),
        )

provider = KeenableProvider(
    lambda entity: f'{entity.identifiers["domain"]} company',
    claims,
)
result = ResearchEngine([
    ProviderSpec("keenable", provider, reserved_units=1),
]).run(Entity.company(domain="example.com"), requested_fields=("summary",))

Company identity gate

Before a caller enriches or acts on a company, use the offline identity gate. It is deliberately conservative: free mailboxes, invalid sites, domain conflicts, and corporate domains that do not exactly match the name receive a review status. Applications can pass separately verified site metadata when they have it.

from enrichfold import derive_company_identity

identity = derive_company_identity(
    email="hello@acme.example",
    company_name="Acme",
    website="https://www.acme.example/about",
)

assert identity.status == "verified"
assert identity.canonical_domain == "acme.example"

Design boundaries

  • Core reconciliation and identity APIs make no network calls; explicit provider adapters such as KeenableProvider may do so.
  • No inferred facts: a field is returned only when a provider supplies evidence.
  • Conflicts have a deterministic suggested value but are marked needs_review.
  • Inferred claims are always marked needs_review.
  • Multi-provider runs reserve caller-defined generic units before execution and expose partial coverage rather than hiding failed or skipped providers.
  • Optional source-policy hooks can accept, reject, or route evidence to review; the package never fetches or validates URLs on its own.
  • Company identity is verified only through an exact name/domain match or caller-supplied, independently verified same-domain site metadata.
  • Bring your own providers for search engines, public data APIs, browser tools, or internal approved sources.

The package intentionally does not decide whether a review is approved or run an action after one; persistence, permissions, UI, and provider-specific claim extraction stay with the host application.

Installation

Python

pip install enrichfold

TypeScript / Node.js

The TypeScript companion currently exposes the same offline company identity gate. It is intentionally a normal npm dependency, rather than a Python subprocess hidden inside a web application:

npm install @mihailorama/enrichfold

Its provider runtime will follow as a compatible TypeScript surface; Python and TypeScript package versions are released independently.

Development

uv run --with pytest pytest -q
python -m build

License

MIT.

Download files

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

Source Distribution

enrichfold-0.4.0.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

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

enrichfold-0.4.0-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

File details

Details for the file enrichfold-0.4.0.tar.gz.

File metadata

  • Download URL: enrichfold-0.4.0.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for enrichfold-0.4.0.tar.gz
Algorithm Hash digest
SHA256 b5a3294c21d7c94a002ba6b0958f052cce6641b711279260bd7d1fe897bb0e0f
MD5 27d76c24cb715dc6aa84f4b4441072c8
BLAKE2b-256 6ff63d569cf59e57b3bf1c89b268c78d23e20bd4e3e2f87941951f68ace60917

See more details on using hashes here.

Provenance

The following attestation bundles were made for enrichfold-0.4.0.tar.gz:

Publisher: publish.yml on Mihailorama/enrichfold

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

File details

Details for the file enrichfold-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: enrichfold-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 15.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for enrichfold-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 97931f7fcc0efc6c267e184dfef74ff4f272997ac746e643665677171483aa03
MD5 9bcd7f84c177a2e6391d9c7b4ce0205f
BLAKE2b-256 1382221907b0a8f68939cbea16d7c3c11caca5f588678ab85c514cd07dec23e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for enrichfold-0.4.0-py3-none-any.whl:

Publisher: publish.yml on Mihailorama/enrichfold

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

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.0

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page