> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

**The search is free** — no balance check, no billing. 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.

**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_url` and the API returns immediately — usually with `search_status: "pending"` and an empty candidate list — then POSTs a `kyb.registry_search.resolved` callback 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"}` — note `timestamp` and `created_at` in the callback are epoch integers, unlike this endpoint's ISO `created_at` — and is sent **unsigned** (header `X-Didit-Unsigned-Callback: true`, no `X-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.

Use this endpoint first to find matching company registry candidates. Search is free and does not create a saved Manual Check.

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](/standalone-apis/kyb-registry#search-callback).

After you pick the correct `kyb_response_id`, call `POST /v3/kyb/select/` to retrieve the billable registry profile.

<Note>
  Each candidate's `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 ephemeral handle scoped to one search request: repeating the same search returns new ids. Select the candidate promptly after searching, and use `registration_number` when you need a stable identifier for the company itself.
</Note>


## OpenAPI

````yaml POST /v3/kyb/search/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/kyb/search/:
    post:
      tags:
        - Standalone APIs
      summary: KYB registry company search (standalone)
      description: >-
        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.


        **The search is free** — no balance check, no billing. 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.


        **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_url` and the API returns immediately —
        usually with `search_status: "pending"` and an empty candidate list —
        then POSTs a `kyb.registry_search.resolved` callback 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"}` — note `timestamp` and `created_at` in the callback are
        epoch integers, unlike this endpoint's ISO `created_at` — and is sent
        **unsigned** (header `X-Didit-Unsigned-Callback: true`, no
        `X-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.
      operationId: post_v3_kyb_search
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - country_code
              properties:
                country_code:
                  type: string
                  maxLength: 10
                  description: >-
                    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.
                  example: GB
                name:
                  type: string
                  maxLength: 255
                  description: >-
                    Company name to search for. Either `name` or
                    `registration_number` is required.
                  example: Tesco
                registration_number:
                  type: string
                  maxLength: 100
                  description: >-
                    Registry registration number to search for. Either `name` or
                    `registration_number` is required.
                search_type:
                  type: string
                  enum:
                    - contains
                    - start_with
                    - fuzzy
                  default: contains
                  description: Name-matching strategy applied by the registry search.
                vendor_data:
                  type: string
                  description: >-
                    Your identifier for this lookup; echoed back and included in
                    the webhook callback.
                metadata:
                  type: object
                  nullable: true
                  description: >-
                    Free-form JSON; echoed back and included in the webhook
                    callback.
                webhook_url:
                  type: string
                  format: uri
                  maxLength: 500
                  description: >-
                    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.
      responses:
        '200':
          description: >-
            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.
          content:
            application/json:
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    format: uuid
                    description: >-
                      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).
                  kyb_registry:
                    type: object
                    properties:
                      companies:
                        type: array
                        items:
                          type: object
                          properties:
                            kyb_response_id:
                              type: string
                              nullable: true
                              description: >-
                                Handle for this candidate — pass it to `POST
                                /v3/kyb/select/` to retrieve the full profile.
                                Ephemeral and scoped to this search: do not
                                store it long-term; re-search to get a fresh
                                one.
                              example: 69aeeb95febb0f1704042259
                            name:
                              type: string
                              nullable: true
                              description: Registered company name.
                              example: Tesco PLC
                            registration_number:
                              type: string
                              nullable: true
                              description: Registry registration number.
                              example: '00445790'
                            status:
                              type: string
                              nullable: true
                              description: >-
                                Normalized registry status of the company (e.g.
                                `active`, `dissolved`, `inactive`, `struck
                                off`).
                              example: active
                            type:
                              type: string
                              nullable: true
                              description: Legal form as reported by the registry.
                              example: Public Limited Company
                            risk_level:
                              type: string
                              nullable: true
                              description: >-
                                Registry-reported risk level, when available.
                                Usually `null` at search time.
                            fetch_status:
                              type: string
                              nullable: true
                              description: >-
                                Always `pending` for search candidates — the
                                full company profile is only fetched (and
                                billed) when you select the candidate.
                              example: pending
                        description: >-
                          Deduplicated candidate list. Empty while
                          `search_resolved=false` (webhook mode) or when nothing
                          matched.
                      pagination:
                        type: object
                        properties:
                          total:
                            type: integer
                            description: Number of candidates returned.
                          page:
                            type: integer
                          per_page:
                            type: integer
                            example: 25
                      search_status:
                        type: string
                        nullable: true
                        enum:
                          - pending
                          - resolved
                        description: Registry search lifecycle status.
                      search_resolved:
                        type: boolean
                        description: '`true` once the candidate list is final.'
                  vendor_data:
                    type: string
                    nullable: true
                    description: Echo of the `vendor_data` you sent.
                  metadata:
                    type: object
                    nullable: true
                    description: Echo of the `metadata` you sent.
                  created_at:
                    type: string
                    format: date-time
              examples:
                Resolved search:
                  summary: Synchronous mode — final candidate list
                  value:
                    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'
                Pending search (webhook mode):
                  summary: >-
                    `webhook_url` provided — candidates arrive via the
                    kyb.registry_search.resolved callback
                  value:
                    request_id: 5f2d8c1b-7e4a-4b9c-9f0d-3a6e8c2b1d4f
                    kyb_registry:
                      companies: []
                      pagination:
                        total: 0
                        page: 1
                        per_page: 25
                      search_status: pending
                      search_resolved: false
                    vendor_data: company-1234
                    metadata:
                      source: onboarding
                    created_at: '2026-06-11T10:41:00.000000+00:00'
        '400':
          description: >-
            Validation error. Field-level problems use DRF's standard envelope;
            the name/registration-number rule reports under `non_field_errors`.
          content:
            application/json:
              examples:
                Missing search criteria:
                  summary: Neither `name` nor `registration_number` provided
                  value:
                    non_field_errors:
                      - Either 'name' or 'registration_number' is required.
                Invalid country_code:
                  summary: Must be `XX` (alpha-2) or `XX-YY` subdivision
                  value:
                    country_code:
                      - country_code must be a 2-letter ISO code.
                Invalid search_type:
                  summary: Only contains / start_with / fuzzy
                  value:
                    search_type:
                      - '"exact" is not a valid choice.'
                Invalid webhook_url:
                  summary: '`webhook_url` must be a valid URL'
                  value:
                    webhook_url:
                      - Enter a valid URL.
        '403':
          description: >-
            Permission denied. Returned when the `x-api-key` header is missing,
            malformed, revoked, or belongs to another environment.
            Authentication failures return `403` with `{"detail": ...}`; this
            API never returns `401`. This endpoint is free, so there is no
            credit-shortfall variant.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: No `x-api-key` header, or the key is invalid/revoked
                  value:
                    detail: You do not have permission to perform this action.
        '429':
          description: >-
            Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget
            of 300 write requests per minute per API key. The response carries
            `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`,
            and `Retry-After` headers.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: More than 300 write requests in the current minute
                  value:
                    detail: >-
                      Write request rate limit exceeded. You can make up to 300
                      requests per minute.
              schema:
                type: object
                properties:
                  detail:
                    type: string
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: |-
            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"
              }'
        - lang: python
          label: Python
          source: >-
            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/
        - lang: javascript
          label: JavaScript
          source: >-
            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;
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````