> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.aventure.vc/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.aventure.vc/_mcp/server.

# Identify a company or person

> **Warning**
>
> Lookup is an experimental feature. Its behavior and response fields can change
> between releases. If an answer looks wrong for the clues you sent, report the
> request body and the response through [Support](/support).

Lookup answers one question: **which aVenture record is this name?** You send a
name as your source writes it plus any clues you have. The answer says whether
one record matches, none does, or a person has to decide.

## Pick an entry point

| Operation                  | Use it when                                               | CLI                        | MCP `operationId` |
| -------------------------- | --------------------------------------------------------- | -------------------------- | ----------------- |
| `POST /v1/lookup`          | You do not know whether the name is a company or a person | `aventure lookup`          | `lookupRecord`    |
| `POST /v1/entities/lookup` | The subject is a company                                  | `aventure entities lookup` | `lookupEntity`    |
| `POST /v1/people/lookup`   | The subject is a person                                   | `aventure people lookup`   | `lookupPerson`    |

All three take the same request body and return the same response shape.

The universal `POST /v1/lookup` first checks whether the name is exactly a
record's id, public handle, slug, or registry id such as a ticker, LEI, or EIN.
If it is, that record is the answer. Otherwise it runs the company lookup and
the person lookup together. `match.owner` says which kind matched: `entityId`
for a company, `personId` for a person. When a company and a person share the
name, the answer is `NEEDS_REVIEW`.

If you already hold an exact slug, URL, or domain and want the full record, use
`GET /v1/entities/lookup-exact` or `GET /v1/people/lookup-exact` instead. Those
reads return `404` or `409` rather than guessing.

## Send every clue you have

Only `name` is required. Each other field narrows the answer.

| Field          | What to send                                                                                            | Why it helps                                                                               |
| -------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `name`         | The name exactly as the source writes it, such as `Acme AI` or `Jane Doe`                               | Starting point for every match                                                             |
| `url`          | Up to 10 URLs the subject owns: its website, its LinkedIn company or person profile, or a registry page | A URL the record already holds proves the match without further judgment                   |
| `location`     | City, region, or country, such as `Austin, TX`                                                          | Tells namesakes apart; not proof on its own                                                |
| `context`      | Specific facts: product, industry, employer and title, or founder names                                 | Separates companies or people with the same name. Generic words like "startup" add nothing |
| `sourceUrl`    | The URL of the article that mentions the subject                                                        | Supplies the surrounding text as evidence                                                  |
| `sourceNewsId` | The aVenture news id of that article, instead of `sourceUrl`                                            | Same as `sourceUrl`; an unknown id returns `400`                                           |

Put articles in `sourceUrl`, not in `url`: `url` is only for pages the subject
owns. A parent company, subsidiary, or namesake is a different company. The same
person name at a different employer is a different person.

## Act on the answer

Every response has a `status`, a one-sentence `detail`, and a `candidate` list
ordered most probable first.

| `status`       | Meaning                                                   | What to do next                                                                                                                 |
| -------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `MATCHED`      | One record is the subject                                 | Read it by `match.record.id`: `GET /v1/entities/{entityId}` for a company or `GET /v1/people/{personId}` for a person           |
| `NO_MATCH`     | No record you can see is the subject                      | Treat the subject as new. For a company, use `officialUrl` as its website when it is present and is the company's own domain    |
| `NEEDS_REVIEW` | The clues fit more than one record, or cannot rule one in | Show `candidate` to the user and ask which record it is, or whether the subject is new. Never create a record on `NEEDS_REVIEW` |

`duplicate` lists other stored records that appear to be the same subject
stored again. `NO_MATCH` covers only the records your credential can see.

## How the answer was settled

`stage` names the step that settled the answer:

* **`DETERMINISTIC`**: exact proof. A URL you sent is already held by the
  record, or the name is an exact id, slug, or registry id. No judgment is
  involved.
* **`JUDGMENT`**: a decision model compared the candidates against your name
  and clues. `matchConfidence` and each candidate's `probability` carry its
  scores from 0 to 1.
* **`WEB_EVIDENCE`**: the judgment also used a web search. `officialUrl` holds
  the subject's own website or profile when the search found one.

Deterministic answers are the most reliable. Sending the subject's website or
LinkedIn URL is the best way to reach one.

## Examples

A company with its website and location:

**`curl`**

```bash title="curl"
curl -X POST "https://api.aventure.vc/v1/entities/lookup" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme AI","url":["https://acme.ai"],"location":"Austin, TX"}'
```

**`CLI`**

```bash title="CLI"
aventure entities lookup --name "Acme AI" --url https://acme.ai --location "Austin, TX"
```

**`MCP (aventure_lookup)`**

```json title="MCP (aventure_lookup)"
{
  "operationId": "lookupEntity",
  "body": { "name": "Acme AI", "url": ["https://acme.ai"], "location": "Austin, TX" }
}
```

A person, with their employer as context:

**`curl`**

```bash title="curl"
curl -X POST "https://api.aventure.vc/v1/people/lookup" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","context":"CEO at Acme AI"}'
```

**`CLI`**

```bash title="CLI"
aventure people lookup --name "Jane Doe" --context "CEO at Acme AI"
```

**`MCP (aventure_lookup)`**

```json title="MCP (aventure_lookup)"
{ "operationId": "lookupPerson", "body": { "name": "Jane Doe", "context": "CEO at Acme AI" } }
```

A name when you do not know whether it is a company or a person:

**`curl`**

```bash title="curl"
curl -X POST "https://api.aventure.vc/v1/lookup" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme AI","url":["https://acme.ai"]}'
```

**`CLI`**

```bash title="CLI"
aventure lookup --name "Acme AI" --url https://acme.ai
```

**`MCP (aventure_lookup)`**

```json title="MCP (aventure_lookup)"
{ "operationId": "lookupRecord", "body": { "name": "Acme AI", "url": ["https://acme.ai"] } }
```

To accept only an exact id, handle, registry id, or slug on `POST /v1/lookup`,
add `legacy=true` (`--legacy true` in the CLI). The lookup then skips judgment
and web search and returns `404` when nothing matches exactly.

## Access and limits

Lookup and article lookup require a signed-in caller: an API key or a CLI or
MCP sign-in (see [Authentication](/authentication)). A signed-in caller whose
plan does not include natural search receives [`402`](/errors) with `code`
`subscription_required`.

Each company or person lookup spends one call of your natural-search quota. On
`POST /v1/lookup`, a name that exactly matches an id, handle, registry id, or
slug settles for free; only names that go on to the company and person lookups
spend quota. When the quota is exhausted the call returns [`429`](/errors)
with a `Retry-After` header. [Plans and usage](/authentication#plans-and-usage)
explains what counts against an allowance.

## Pick records worth a research update

Each candidate, including `match`, carries two fields that tell you how current
the stored record is:

| Field                    | Meaning                                                                                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dataCompletionCoverage` | Companies only. The share, from 0 to 100, of the required profile facts the record holds. Absent for people and for companies whose coverage has not been computed yet |
| `updatedAt`              | When the stored record last changed                                                                                                                                    |

A low `dataCompletionCoverage` or an old `updatedAt` marks a record that a
[research update](#update-records-with-research) is likely to improve. A
company near 100 that changed recently rarely needs one. When coverage is
absent, decide from `updatedAt` alone.

## Identify everything an article names

An article lookup reads one article, extracts every company and person it
names, and identifies each name as `POST /v1/lookup` would, with the article as
the source.

1. Start the job with `POST /v1/lookup-jobs` and a body holding `sourceUrl`,
   `sourceNewsId`, or both. The call returns `202` with a `jobId`. Sending the
   same article again soon returns the earlier job.
2. Read the job with `GET /v1/lookup-jobs/{jobId}` until `state` is
   `COMPLETED` or `FAILED`. Only the caller that started the job can read it.
3. Act on each entry in `mention`. Every entry has the `name` as the article
   writes it and a `mentionType` of `COMPANY` or `PERSON`:
   * `identification` is the lookup answer, read exactly as in
     [Act on the answer](#act-on-the-answer).
   * `shell` is set when the answer is `NO_MATCH` and the job created a hidden
     placeholder record for the name. The job creates one only when the article
     and a live page on another site both name the subject. `shell` holds its
     `entityId` or `personId`; creating it costs nothing, and you can send it
     straight to a research update.
   * `shellDetail` says, in one sentence, why no placeholder was created for a
     `NO_MATCH` name.
   * `failureReason` replaces `identification` when a name could not be
     identified. Send that name alone to `POST /v1/lookup`.

When the job is accepted it spends one natural-search quota call for each name
it may identify, up to its per-article limit. A repeat of the same article that
returns the earlier job spends nothing. The job answers `402` and `429` under
the same rules as a single lookup.

## Update records with research

A research update queues a run that researches one record and updates its
aVenture profile.

| Operation                                  | Use it for                                                 | CLI                                    |
| ------------------------------------------ | ---------------------------------------------------------- | -------------------------------------- |
| `POST /v1/entities/{entityId}/enrichments` | One company, product, or service                           | `aventure entities enrichments enrich` |
| `POST /v1/people/{personId}/enrichments`   | One person                                                 | `aventure people enrichments enrich`   |
| `POST /v1/enrichments`                     | Many records: a body with `entityId` and `personId` arrays | `aventure enrichments enrich`          |

Take the id from `match.owner` on a `MATCHED` answer, from the candidate the
user chose on `NEEDS_REVIEW`, or from an article lookup's `shell`. When a run
for the same record is already queued or running, the call returns that run
instead of starting and charging another.

The batch call files one run per distinct id, companies first, and returns one
outcome per record in request order: its `run`, or a `refusal` of `NOT_FOUND`
for an unknown id or `ALLOWANCE_EXHAUSTED` once your research allowance runs
out. Runs filed before the allowance ran out stay queued. A list longer than
the batch limit is refused before any run is filed.

Each new run is billed as one company research ($2.50) or one person research
($0.65). Runs first draw on your plan's monthly research allowance: AI Plus
includes 30 company and 30 person researches, and AI Pro includes 270 of each.
Past the allowance, runs continue only as additional usage if you turned it on;
otherwise a single-record call answers `429` and the batch call marks the
remaining records `ALLOWANCE_EXHAUSTED`. A plan without research runs answers `402`.
[Plans and usage](/authentication#plans-and-usage) explains allowances and
additional usage.

Check a run with `GET /v1/harness/runs/{runId}` (`aventure harness runs get`),
using the `id` of the returned run. `run.status` moves through `queued` and
`running` to `completed`, `failed`, or `stopped`. When it is `completed`, read
the updated record with `GET /v1/entities/{entityId}` or
`GET /v1/people/{personId}`.

## From article to researched records

Start an article lookup:

**`curl`**

```bash title="curl"
curl -X POST "https://api.aventure.vc/v1/lookup-jobs" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sourceUrl":"https://techcrunch.com/2024/01/01/example"}'
```

**`CLI`**

```bash title="CLI"
aventure lookup-jobs create --source-url https://techcrunch.com/2024/01/01/example
```

Read the job with the returned `jobId` until `state` is `COMPLETED`:

**`curl`**

```bash title="curl"
curl "https://api.aventure.vc/v1/lookup-jobs/$JOB_ID" \
  -H "Authorization: Bearer $AUTH_TOKEN"
```

**`CLI`**

```bash title="CLI"
aventure lookup-jobs get --job-id "$JOB_ID"
```

Collect the company and person ids from each mention's `match.owner` or
`shell`, keep the ones worth updating, and queue research on all of them in one
call:

**`curl`**

```bash title="curl"
curl -X POST "https://api.aventure.vc/v1/enrichments" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entityId":["'"$ENTITY_ID"'"],"personId":["'"$PERSON_ID"'"]}'
```

**`CLI`**

```bash title="CLI"
aventure enrichments enrich --entity-id "$ENTITY_ID" --person-id "$PERSON_ID"
```

Check each returned run:

**`curl`**

```bash title="curl"
curl "https://api.aventure.vc/v1/harness/runs/$RUN_ID" \
  -H "Authorization: Bearer $AUTH_TOKEN"
```

**`CLI`**

```bash title="CLI"
aventure harness runs get --run-id "$RUN_ID"
```