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

# Global Search

> Find a company from its name when you do not know its country

<Note>
  Global search is in alpha. The request and response shapes may still change, and so may the wording of explanations and the list of reason kinds.
</Note>

Every `/v2/search` call needs a country. Often you do not have one: a shareholder listed in a foreign filing, a counterparty on an invoice, a holding company whose stated domicile is not where it is incorporated.

`POST /v2/search/global` takes what you know in one line of text and returns ranked candidates across every country Topograph covers. Each candidate has the country and registration number you pass to [`/v2/company`](/essentials/retrieve_company), whether it is a strong or a possible match, and a one-sentence explanation of why it matched.

## Quick example

```bash theme={null}
curl -X POST "https://api.topograph.co/v2/search/global" \
  -H "x-api-key: $TOPOGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "Bamberger GmbH, Wien" }'
```

```json theme={null}
{
  "status": "resolved",
  "summary": "Found in Austria: Bamberger GmbH (370916s).",
  "query": {
    "text": "Bamberger GmbH, Wien",
    "name": "Bamberger GmbH",
    "legalForm": "GmbH",
    "hints": { "address": "Wien" },
    "likelyCountryCode": "AT"
  },
  "candidates": [
    {
      "rank": 1,
      "match": "strong",
      "countryCode": "AT",
      "id": "370916s",
      "legalName": "Bamberger GmbH",
      "address": { "city": "Wien", "countryCode": "AT" },
      "explanation": {
        "summary": "The register in Austria lists it under exactly that name; the address you gave points to Austria; the legal form \"GmbH\" is used in Austria.",
        "reasons": [
          { "kind": "register_match", "countryCode": "AT", "text": "The register in Austria lists it under exactly that name" },
          { "kind": "address", "countryCode": "AT", "text": "The address you gave points to Austria" },
          { "kind": "legal_form", "countryCode": "AT", "text": "The legal form \"GmbH\" is used in Austria" }
        ]
      }
    }
  ],
  "countries": [
    { "countryCode": "AT", "outcome": "match", "explanation": "Austria: the register returned the company." },
    { "countryCode": "DE", "outcome": "no_match", "explanation": "Germany: the register returned no company of that name." }
  ],
  "steps": [
    { "index": 0, "kind": "reading_name", "status": "done", "label": "Read your query" },
    { "index": 1, "kind": "company_data", "status": "done", "label": "Checked Topograph's company data" },
    { "index": 2, "kind": "register", "status": "done", "countryCode": "AT", "label": "The register in Austria returned a match" }
  ],
  "usage": { "durationMs": 4180 }
}
```

The candidate is ready for a company request:

```bash theme={null}
curl -X POST "https://api.topograph.co/v2/company" \
  -H "x-api-key: $TOPOGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "countryCode": "AT", "id": "370916s", "dataPoints": ["company"] }'
```

## Sending what you know

Put everything in `query`, separated by commas. The name comes first; everything after it is read as a hint.

| You write                                           | Read as                                      |
| --------------------------------------------------- | -------------------------------------------- |
| `Bamberger GmbH, Wien`                              | address `Wien`                               |
| `MFG Investments EOOD, from a UK filing`            | the name came from a United Kingdom register |
| `Altria Group Inc., Richmond VA, altria.com`        | address `Richmond VA`, website `altria.com`  |
| `Metinvest B.V., Netherlands`                       | country `NL`                                 |
| `Acme Holding, HRB 12345`                           | identifier `HRB 12345`                       |
| `Приватна компанія Метінвест Б.В. (Metinvest B.V.)` | name with its Latin alias                    |

What was understood is returned in `query`: the `name`, the `legalForm` it carries, and the `hints` read from the text, so you can check the search understood your input.

If your data is already structured, send `name` and `hints` instead of `query`:

```json theme={null}
{
  "name": "Bamberger GmbH",
  "hints": {
    "countries": ["AT", "DE"],
    "excludeCountries": ["GB"],
    "sourceCountry": "IT",
    "address": { "city": "Wien", "countryCode": "AT" },
    "legalForm": "GmbH",
    "identifier": "FN 370916s",
    "website": "bamberger.at"
  }
}
```

| Hint               | Meaning                                                                                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `countries`        | Countries you believe in, most likely first. US states as `US-DE`.                                                                                                |
| `excludeCountries` | Countries already searched without a hit. They are never searched again.                                                                                          |
| `sourceCountry`    | The register the name came from, when it comes from a filing. A foreign holder's stated domicile is often not where it is incorporated, so this is a weak signal. |
| `address`          | Free text or structured.                                                                                                                                          |
| `legalForm`        | The legal form, when the name does not carry it.                                                                                                                  |
| `identifier`       | Any registration, VAT or LEI number, in any format.                                                                                                               |
| `website`          | A national domain is a country signal.                                                                                                                            |

`hints` sent with a `query` take precedence over what was extracted from the text. Add `"activeOnly": true` to leave out companies the registers report as inactive.

## How it decides

The search works through cheap signals first and stops as soon as one candidate is a strong match.

1. **The name.** The legal form and the wording of the name point to the jurisdictions that use them: `EOOD` is Bulgarian, `S.p.A.` Italian, `GmbH` German, Austrian or Swiss. `Ltd` is most often British, but an American-sounding `Ltd` is weighed toward the United States. `LLC` and `Inc.` point to the United States, and the state still has to be found. Scanning noise such as `S.a r.1.` is corrected.
2. **Your hints**, as above.
3. **Topograph's company data**, checked once across all countries.
4. **The web**, only when the name and Topograph's data are not enough, or the company is American and its state is unknown. A source page is kept as evidence.
5. **The registers.** Live searches in the most likely countries, three at a time, with one rephrased retry when a register returns nothing. The search stops once a register returns the exact name and no other country is a serious contender.

For a US company with no known state, the usual incorporation states are tried first (Delaware, New York, Nevada, Wyoming, Florida, Texas). Then the search moves on through the other states, up to almost every state, six at a time and fastest registers first.

**When you give an address**, it decides a lot. If the address names a country or a US state, that register is asked first, before any web search. When it returns the company under that name at the address you gave, or nearby, the search stops there and no other country is searched. When a register holds several companies with the same name, they are ranked by the distance between their registered address and yours, and each explanation says how far it is ("Its registered address is 3 km from Richmond").

A search runs at most 15 register and web searches, plus up to 40 state registers for a US company with no known state, within 100 seconds.

## Reading the answer

`status` is the first thing to check.

| `status`    | Meaning                                                                                                                            | What to do                                                       |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `resolved`  | One strong match, and no plausible rival in another country.                                                                       | Use `candidates[0]`.                                             |
| `ambiguous` | Several plausible candidates (the same name exists in Germany and Austria), or one candidate that is not strong enough on its own. | Show the candidates to a person, or add a hint and search again. |
| `not_found` | Nothing acceptable.                                                                                                                | Read `reason`.                                                   |

`summary` says the same thing in one sentence you can show a user.

On `not_found`, `reason` says why:

| `reason`               | Meaning                                                                                                                           |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `not_a_legal_entity`   | The input names a class of holders ("Undisclosed shareholders", "Employees"), a trust or a nominee account. Nothing was searched. |
| `no_country_signal`    | Nothing in the name, the hints, Topograph's data or public sources pointed at a country.                                          |
| `country_not_covered`  | The evidence points to a register Topograph does not cover.                                                                       |
| `no_register_match`    | The likely countries were searched and none returned the company.                                                                 |
| `search_limit_reached` | The search limit was reached before every likely country was tried.                                                               |
| `timeout`              | The time limit was reached.                                                                                                       |

`query.likelyCountryCode` names the country the evidence settled on, even when the company could not be confirmed or its register is not covered. A Cayman Islands fund still gets its country.

### Candidates

Each candidate carries `countryCode`, `id`, `legalName`, its registered `address` and `isActive`, and a `match`: `strong` when the evidence points to this company, `possible` when it needs a second look. A `resolved` search has one strong match. `explanation.summary` says why it matched in one or two sentences, and `explanation.reasons` lists the reasons one by one, each with a `text` you can show as is:

| `kind`           | Meaning                                                      |
| ---------------- | ------------------------------------------------------------ |
| `register_match` | The register returned the company.                           |
| `public_source`  | A public page states where it is registered, with its `url`. |
| `identifier`     | The identifier you gave has this country's format.           |
| `address_match`  | The register address matches the address you gave.           |
| `distance`       | How far the registered address is from the address you gave. |
| `address`        | The address you gave points to this country.                 |
| `legal_form`     | The legal form in the name is used in this country.          |
| `source_filing`  | The name came from a filing in this country.                 |
| `country_hint`   | You named this country.                                      |
| `website`        | The website domain points to this country.                   |
| `company_data`   | Topograph's company data lists it under that name.           |

`countries` lists the countries considered, each with an `outcome` (`match`, `no_match`, `not_searched`, `not_covered`, `excluded`, `unavailable`) and a sentence. `steps` is the search as it ran, one `label` per step.

## Streaming

With `"stream": true` and the `Accept: text/event-stream` header, the endpoint sends Server-Sent Events while the search runs:

* `progress`: the search so far, as `{ revision, steps, candidates, countries, query }`. A step with `status: "running"` is in progress, and its `label` says what it is doing ("Asking the register in Austria"). A new event is sent every time something changes.
* `complete`: the final response, same shape as the non-streaming answer.
* `error`: `{ error: { code, message } }`.

```ts theme={null}
const response = await fetch('https://api.topograph.co/v2/search/global', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.TOPOGRAPH_API_KEY!,
    'Content-Type': 'application/json',
    Accept: 'text/event-stream',
  },
  body: JSON.stringify({ query: 'Altria Group Inc., Richmond VA', stream: true }),
});

const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  const frames = buffer.split('\n\n');
  buffer = frames.pop() ?? '';
  for (const frame of frames) {
    const event = frame.match(/^event: (.*)$/m)?.[1];
    const data = JSON.parse(frame.match(/^data: (.*)$/m)?.[1] ?? '{}');
    if (event === 'progress') {
      const running = data.steps.filter((s: any) => s.status === 'running');
      console.log(running.map((s: any) => s.label).join(' · '));
    }
    if (event === 'complete') console.log(data.status, data.summary);
    if (event === 'error') console.error(data.error.code, data.error.message);
  }
}
```

Streaming is the better choice for anything a person watches: candidates appear as each register answers, and a long search never runs into an HTTP timeout.

## Billing

A search is billed only when it succeeds, that is when `status` is `resolved`. Ambiguous and not-found searches, inputs rejected as not a legal entity, failures and timeouts are not billed. `usage.priceCents` shows what the search was charged. The current price is on the [pricing page](https://topograph.co/pricing).

## Limits and errors

Each account can run 60 global searches per minute and 5 at the same time.

| HTTP | `error.code`         | Meaning                                                                               |
| ---- | -------------------- | ------------------------------------------------------------------------------------- |
| 400  | `invalid_request`    | Neither `query` nor `name` was sent, or a country code in the hints is not a country. |
| 402  | `insufficient_funds` | The account cannot pay for a search.                                                  |
| 429  | `rate_limited`       | Too many searches this minute, or too many running at once. Wait `retryAfterSeconds`. |
| 503  | `search_timeout`     | The search outlived the request. Use streaming, or add a hint.                        |

## Development environments

With an `sk_dev_` key, global search answers from generated data: candidates in the countries the name's legal form points to, each resolvable with `/v2/company` in the same environment. Coverage is honoured, so a name that only points at uncovered countries ends `not_found` with `country_not_covered`, as it would live. Nothing is billed.

## In the app

The same search is in the app under **Global search** in the sidebar: one text box, the search progressing live, and the candidates with their explanations.

Click a candidate, or **Get data**, to pick the datapoints and order the company exactly as from the country search.
