Entity Bind and Python
Pattern over the HTTP API
How you get a key
Create your workspace online and verify your email, then create your own read or resolve API keys, each shown once and revocable. Free includes 300 credits and needs no card. Paid subscriptions are purchased through Stripe in Billing, with applicable VAT and no automatic usage or overage charges.
host.example.com is a placeholder everywhere below, and a client configured against it will not connect. The hosted base URL is https://app.entitybind.com. Replace the placeholder with that origin; registry checks follow your activated plan.
The full-pipeline evaluation is arranged separately and is not activated by signup. Request it for your workspace; we confirm availability, sources and the start date before activation. It starts with a conversation about your file.
Create your workspace online, verify your email and create your own API key. Free includes 300 credits and shallow matching with no card; choose a paid subscription in Billing when you need registry checks.
Start freeOn this page
What you would build for Python
There is nothing to install, so the blocks below are the job itself: the query, the request, and the branch on what comes back. Endpoint paths, header names and field names are the API's own and are exact — the code around them is a shape to fit to your own stack.
- 01
One call, to see the shape
Send at least one identifying field. Unknown fields are rejected rather than ignored, which is deliberate: a field dropped in silence is a wrong answer arriving in silence. client_ref takes up to 200 characters, comes back verbatim, and is how the response joins to your data on your own key.
One call, to see the shapeimport os import httpx response = httpx.post( f"{os.environ['ENTITYBIND_BASE_URL']}/v1/resolve", headers={"Authorization": f"Bearer {os.environ['ENTITYBIND_API_KEY']}"}, json={ "name": "Acme Widgets Ltd", "domain": "acmewidgets.co.uk", "country": "GB", "depth": "research", "client_ref": "crm-4417", }, timeout=60.0, ) response.raise_for_status() result = response.json() - 02
Branch on the outcome first
A match below 0.90 confidence is unrepresentable. The record comes back as an abstention with a ranked shortlist instead. The rule lives in the model that carries the result rather than in a caller who could skip it. Enforced as a domain invariant on the resolution outcome and again by a predicate called on the online, batch and replay paths, so a sub-floor accepted match cannot be constructed on any of them. A cached replay whose inherited confidence has fallen below the floor is withheld rather than re-served. So an accepted match has already cleared the floor by the time you read it. Branch on whether the record resolved and treat every abstention as a work list. If your policy needs a stricter threshold, measure what it costs you in coverage on labelled records first. Confidence bounds depend on the recorded evidence population; there is no universal score for every match. Read match_confidence_basis and the evidence. A 0.99 filter can return some v8 adjudicated matches while excluding valid identifier matches, so validate its coverage cost on your own inputs.
Branch on the outcome firstif result["decision"] == "match": entity_id = result["entity_id"] decision_id = result["decision_id"] # reopens the evidence trail else: # Not an error. A ranked shortlist, a typed reason, the column that # would have decided, and a review id — and it cost nothing. reason = result["reason_code"] shortlist = result["candidates"] prescription = result.get("prescription_fields") - 03
Batch when you have more than a handful
Up to 500 records a request, treated as an unordered set, with a repeated idempotency key recognised as the same request and one ledger row written per outcome. Batches run one at a time rather than queueing, so plan a large file as a window rather than as a rate. A byte-identical replay is free — per tenant, per exact content, with no expiry — so an interrupted job restarts without paying twice for the rows it already did.
- 04
Read the usage block rather than counting calls
Every metered response carries a usage block, and the module that quotes the price in that block is the same module that writes the ledger row and serves the public pricing endpoint. The number in your response, the number in your usage export and the number you are charged cannot drift apart, so there is nothing to reconcile and no reason to keep a count of your own.
If a step above fails against your own deployment, send us what the client reported rather than working around it.
Talk to usWhat this route gets you
Every surface runs the same pipeline: the same cascade, the same confidence floor, the same evidence and the same credit rule. These are the things this route adds on top of it.
- A plain JSON API with a published schema and no client library between you and it, so a change in the payload is visible in your code rather than absorbed by a wrapper.
- Your own key echoed on every row, so results join to your data without a mapping table.
- Free replays: a byte-identical request costs nothing, per tenant, per exact content, with no expiry — which makes retry logic cheap and restarts safe.
- An NDJSON export of the whole catalogue, so leaving is a download — portability in the sense the data protection regulation uses the word.
What it will not do
Start with the first one: it decides whether there is anything to install at all. The rest are the parts of the shape that most often get scoped wrong.
- 01
This is an integration pattern over the HTTP API, not a shipped connector. There is no package, no managed application, no marketplace listing and nothing to install: what is described below is the shape of the integration you would build, written down because knowing the shape in advance is most of the work.
- 02
No package to install, no async helper, no retry policy and no pagination helper. You write those, and the API documentation is written on the assumption that you will.
- 03
The OAuth endpoints are excluded from the published schema on purpose: they are form-encoded, browser-facing, and answer with a different error body, so a client generated from the schema must never call them.
- 04
Rate limits are per tenant and shared with every other surface, so a Python job and an editor agent draw on one budget.
- 05
Scope is company and organisation identity. Not people, vessels, bank-account ownership, sanctions or emissions, and results may not be used for eligibility decisions about individuals, including employment, credit and tenancy.
Check these integration limits before building. Create your Free workspace to inspect your own rows and keys; contact us if you need help choosing a supported integration shape.
Start freeThe other ways in
3 of the 6 are shipped integrations and 3 are patterns over the HTTP API. Every card says which kind it is, so you know what you are scoping before you open it.
Claude Code
Shipped4 steps · 4 named limits
Claude Desktop
Shipped4 steps · 4 named limits
Cursor
Shipped4 steps · 3 named limits
Snowflake
Pattern4 steps · 5 named limits
Salesforce
Pattern4 steps · 5 named limits
Python sits with Snowflake and Salesforce on the same shelf: nothing to install. If you are weighing a shipped integration against a pattern, the line between the two shelves is drawn on the hub.
Whichever surface you land on, the cheapest first move is the same: a few hundred of your own rows.
Start freeWhat to read next
The work this surface is installed to do, the method behind the answers it returns, and the terms those answers use. Each card names its own shelf.
- Use caseAI agentsA counterparty check before the agent acts, and a receipt for what it acted on.
- Use caseReference dataEvery identifier a company is known by, each separately cited — and the ones nobody holds.
- GuideWhat is entity resolution?Entity resolution decides which records refer to the same real-world thing. What it is, why company identity is hard, deterministic versus probabilistic matching, and why fuzzy matching is not entity resolution.
- DefinitionAbstentionAn abstention is a first-class 'we do not know' returned instead of a match: the record comes back unresolved, with a ranked shortlist, a typed reason and the column that would have decided it.
- DefinitionIdempotencyAn operation is idempotent when performing it repeatedly has the same effect as performing it once, which is what makes a retry after a timeout safe rather than duplicating work.
- DefinitionReplayReplay is re-serving a previous decision for byte-identical content rather than recomputing it, which makes repeated submissions free and deterministic — provided the replayed decision is still one the system would stand behind.
Start with a file, not an integration.
Create your workspace, verify your email and run your first company file. The Free plan needs no card. Create API keys in your workspace when you need an integration.
300 credits. No card.
For a guided review of your file, explore the Merge Safety Sprint →
Questions about Python
Is there a Python SDK?
No, and none is planned until there are enough integrations running to keep one current. The API is JSON over HTTPS with a bearer token and a published schema; a wrapper would add a dependency, a version skew and a layer that hides payload changes from you, in exchange for saving about thirty lines. If you want a typed client, generate one from the schema — with the caveat that the OAuth endpoints are excluded from it on purpose, so a generated client must never call them.
What confidence threshold should I use in my code?
Start by branching on the outcome and sending abstentions to a review queue. The 0.90 floor is already enforced in the engine: a match below it cannot be constructed at all, because the rule lives in the model that carries the result — a domain invariant — and a predicate checks it again on the online, batch and replay paths. Anything you receive as an accepted match has already cleared it. It is an acceptance contract, not calibration evidence: it says what we refuse to return, not how often an accepted match is right. Read it with the coverage it costs. A stricter threshold is possible, and worth evaluating for precision and coverage on your own labelled records before you ship it. Confidence bounds depend on the recorded evidence population; there is no universal score for every match. Read match_confidence_basis and the evidence. A 0.99 filter can return some v8 adjudicated matches while excluding valid identifier matches, so validate its coverage cost on your own inputs.