curl -X POST https://verification.didit.me/v3/kyb/search/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"country_code": "GB",
"name": "Tesco",
"search_type": "contains",
"vendor_data": "company-1234"
}'import os, requests
resp = requests.post(
"https://verification.didit.me/v3/kyb/search/",
headers={"x-api-key": os.environ["DIDIT_API_KEY"]},
json={"country_code": "GB", "name": "Tesco"},
timeout=60, # synchronous mode waits for the registry to resolve
)
resp.raise_for_status()
candidates = resp.json()["kyb_registry"]["companies"]
kyb_response_id = candidates[0]["kyb_response_id"] # pass to /v3/kyb/select/const res = await fetch('https://verification.didit.me/v3/kyb/search/', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ country_code: 'GB', name: 'Tesco' }),
});
const data = await res.json();
const kybResponseId = data.kyb_registry.companies[0].kyb_response_id;<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/kyb/search/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'country_code' => 'GB',
'name' => 'Tesco',
'registration_number' => '<string>',
'tier' => 'basic',
'search_type' => 'contains',
'vendor_data' => '<string>',
'metadata' => [
],
'webhook_url' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/kyb/search/"
payload := strings.NewReader("{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://verification.didit.me/v3/kyb/search/")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/kyb/search/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"request_id": "0c7e9a40-3a13-4f7e-8a44-9d2e2f1c5b6a",
"kyb_registry": {
"companies": [
{
"kyb_response_id": "69aeeb95febb0f1704042259",
"name": "Tesco PLC",
"registration_number": "00445790",
"status": "active",
"type": "Public Limited Company",
"risk_level": null,
"fetch_status": "pending"
},
{
"kyb_response_id": "69aeeb95febb0f170404225a",
"name": "E-TESCO LTD",
"registration_number": "15168476",
"status": "active",
"type": "Private Limited Company",
"risk_level": null,
"fetch_status": "pending"
}
],
"pagination": {
"total": 2,
"page": 1,
"per_page": 25
},
"search_status": "resolved",
"search_resolved": true
},
"vendor_data": "company-1234",
"metadata": null,
"created_at": "2026-06-11T10:40:00.000000+00:00"
}KYB Registry Search
Search official company registries for candidate companies by name and/or registration number. This is step 1 of the two-step registry flow: search returns lightweight candidates, then POST /v3/kyb/select/ retrieves the full profile of one candidate.
Search billing. Successful searches using a paid company-data source cost USD 0.50 per request, regardless of candidate count. Empty results, failures and results served only by other sources are not charged. The applicable price is reserved before searching; insufficient balance returns 403 with code insufficient_credits. Contract rates apply where agreed, and an unconfigured contract search rate returns 403. Sandbox searches are free. Repeating a successful search can incur another charge; callback delivery does not add a charge. Profile retrieval is billed separately. This search fee also applies to v2. Candidates always come back with fetch_status: "pending": the full company data is only fetched (and billed) on select.
kyb_response_id is ephemeral. It is a per-search handle, not a stable company id. Select promptly after searching and re-search to get a fresh handle instead of storing it.
tier picks the provider chain. tier names the registry tier the search runs on: basic (the default, the Lite chain), shareholders or ubo. The search uses the provider chain that serves that tier, and it is not always the same chain the Lite search reads, so an empty Lite search is not proof that the registry does not hold the company — search again at the tier you need. A country that does not offer the tier you name is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells that tier: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/.
Authentication. POST /v3/kyb/search/ (and /v2/) takes your application’s API key in the x-api-key header. The hosted POST /v1/kyb/search/ is the session-authenticated route a hosted verification flow calls: it answers 403 to an API key and takes no tier — the workflow’s KYB registry configuration picks the chain.
Two modes.
- Synchronous (default): the API polls the registry until the search resolves and returns the final candidate list (
search_resolved: true). - Asynchronous: pass
webhook_urland the API returns immediately — usually withsearch_status: "pending"and an empty candidate list — then POSTs akyb.registry_search.resolvedcallback to your URL when the registry finishes. The callback body is{"event_id", "event_type": "kyb.registry_search.resolved", "request_id", "vendor_data", "metadata", "search_status", "search_resolved", "kyb_registry", "timestamp", "created_at"}— notetimestampandcreated_atin the callback are epoch integers, unlike this endpoint’s ISOcreated_at— and is sent unsigned (headerX-Didit-Unsigned-Callback: true, noX-Signature).
Country codes. ISO 3166-1 alpha-2 (GB, DE), plus XX-YY subdivision codes where registries are regional — e.g. US-CA for California.
Sandbox. Keys from sandbox applications return one static candidate without contacting any registry.
curl -X POST https://verification.didit.me/v3/kyb/search/ \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"country_code": "GB",
"name": "Tesco",
"search_type": "contains",
"vendor_data": "company-1234"
}'import os, requests
resp = requests.post(
"https://verification.didit.me/v3/kyb/search/",
headers={"x-api-key": os.environ["DIDIT_API_KEY"]},
json={"country_code": "GB", "name": "Tesco"},
timeout=60, # synchronous mode waits for the registry to resolve
)
resp.raise_for_status()
candidates = resp.json()["kyb_registry"]["companies"]
kyb_response_id = candidates[0]["kyb_response_id"] # pass to /v3/kyb/select/const res = await fetch('https://verification.didit.me/v3/kyb/search/', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ country_code: 'GB', name: 'Tesco' }),
});
const data = await res.json();
const kybResponseId = data.kyb_registry.companies[0].kyb_response_id;<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/kyb/search/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'country_code' => 'GB',
'name' => 'Tesco',
'registration_number' => '<string>',
'tier' => 'basic',
'search_type' => 'contains',
'vendor_data' => '<string>',
'metadata' => [
],
'webhook_url' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/kyb/search/"
payload := strings.NewReader("{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://verification.didit.me/v3/kyb/search/")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/kyb/search/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"registration_number\": \"<string>\",\n \"tier\": \"basic\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"<string>\",\n \"metadata\": {},\n \"webhook_url\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"request_id": "0c7e9a40-3a13-4f7e-8a44-9d2e2f1c5b6a",
"kyb_registry": {
"companies": [
{
"kyb_response_id": "69aeeb95febb0f1704042259",
"name": "Tesco PLC",
"registration_number": "00445790",
"status": "active",
"type": "Public Limited Company",
"risk_level": null,
"fetch_status": "pending"
},
{
"kyb_response_id": "69aeeb95febb0f170404225a",
"name": "E-TESCO LTD",
"registration_number": "15168476",
"status": "active",
"type": "Private Limited Company",
"risk_level": null,
"fetch_status": "pending"
}
],
"pagination": {
"total": 2,
"page": 1,
"per_page": 25
},
"search_status": "resolved",
"search_resolved": true
},
"vendor_data": "company-1234",
"metadata": null,
"created_at": "2026-06-11T10:40:00.000000+00:00"
}403 with code: "insufficient_credits".
Contract billing follows your agreed search rate; an unconfigured contract rate is rejected before the search.
Sandbox searches are free.
Each repeated successful search is a new billable request; callback delivery does not add another search charge.
If you send webhook_url, Didit can return immediately while the provider resolves the search, then post the candidates to your callback URL when they are ready. The callback is unsigned by design, so you do not need to configure a webhook secret for this search notification. See the search callback payload example.
After you pick the correct kyb_response_id, call POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/ to retrieve the billable registry profile at the tier you need.
fetch_status is pending at search time and stays pending until you select that candidate — search only lists candidates, and select is what fetches the full profile. Do not poll search waiting for it to resolve.kyb_response_id is an opaque, ephemeral handle scoped to one search request: pass it back exactly as you received it and never parse it or derive anything from its contents, and expect repeating the same search to return new ids. Select the candidate promptly after searching, and use registration_number when you need a stable identifier for the company itself.Search tier
tier selects the registry tier whose provider chain the search runs on: basic (the default, the Lite chain), shareholders or ubo. The candidate list comes from that tier’s chain, and that is not always the source the Lite search reads, so an empty Lite search does not prove the registry does not hold the company — search again at the tier you need.
Where the country does not offer the tier you name, the search runs on the highest tier it offers up to that one. A country with no registry data at all is not offered for registry checks; continue with manual company entry.
Search at the tier you intend to buy, then retrieve the candidate from the endpoint that sells it:
tier you searched | Endpoint that retrieves the candidate |
|---|---|
basic (default) | POST /v3/kyb/select/ (Lite) |
shareholders | POST /v3/kyb/shareholders/ |
ubo | POST /v3/kyb/ubo/ |
search_type
search_type accepts contains only, and that is the default. The registry sources behind the search match a company name on containment, so start_with and fuzzy are rejected with 400 ("start_with" is not a valid choice) instead of being silently treated as contains. Send registration_number for an exact company match.
Hosted and standalone search
POST /v1/kyb/search/ is the search a hosted verification flow runs: it is authenticated by the verification session, answers 403 to an API key, and takes no tier — the workflow’s KYB registry configuration picks the chain. The standalone API calls POST /v2/kyb/search/ or POST /v3/kyb/search/ with your x-api-key; the tier input and the tiered select endpoints are how a standalone integration reaches the same candidates, at the tier it needs.
Handle registry outages
If the registry provider is temporarily unavailable, the endpoint returns502 with a stable machine-readable code:
{
"detail": "The company registry provider is temporarily unavailable.",
"code": "kyb_registry_provider_unavailable"
}
400 and does not use this outage code.Authorizations
Your application's API key, from Developers -> API keys in the Business Console. The primary key has full access. A named key can be scoped: none, read or write per resource, limited to some workflows or to approved sessions, to a list of IP addresses, and to an expiry date. 401 means the key is missing, wrong, revoked or expired; 403 means the key has no access to this resource or action, or the request came from an address outside its IP list; 404 on a session route means the session is outside the key's workflows or statuses. A key without media access receives image, video and PDF URLs as null, and a key without sessions write receives session links and tokens as null. See https://docs.didit.me/console/api-keys.
Body
ISO 3166-1 alpha-2 code of the country whose company registry to search. For example, GB. Where company registries operate at state or province level, append the ISO 3166-2 subdivision code as XX-YY: US-CA searches the California registry. United States searches always require the subdivision — a bare US is rejected. Case-insensitive; normalized to upper case.
10"GB"
Company name to search for. Either name or registration_number is required.
255"Tesco"
Registry registration number to search for. Either name or registration_number is required.
100Registry tier whose provider chain this search runs on. basic (default) searches the country's Lite chain; shareholders and ubo search that tier's ownership chain, which can be a different source, so an empty Lite search does not prove the registry lacks the company. A country that does not offer the named tier is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells the tier you searched: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/.
basic, shareholders, ubo "basic"
Name-matching strategy applied by the registry search. contains is the only accepted value: no registry source behind the search offers a prefix or fuzzy mode, so start_with and fuzzy are rejected with 400 (not a valid choice).
contains Your identifier for this lookup; echoed back and included in the webhook callback.
Free-form JSON; echoed back and included in the webhook callback.
Switches the search to asynchronous mode: the API returns immediately and POSTs an unsigned kyb.registry_search.resolved callback to this URL when the candidate list is ready.
500Response
Search accepted. Synchronous mode returns the resolved candidate list; webhook mode returns immediately (often search_status: "pending") and delivers the final list via the kyb.registry_search.resolved callback.
With webhook_url: the persisted search-request id, echoed as request_id in the kyb.registry_search.resolved callback. Without webhook_url: a transient correlation UUID (the search is not stored).
Show child attributes
Show child attributes
Echo of the vendor_data you sent.
Echo of the metadata you sent.