# Asynchronous results (https://webmetic.de/docs/en/api/asynchrone-ergebnisse)

Two Enrichment calls in the API work this way. For example, you send 300 domains from your trade show list to an Enrichment list. The API confirms at once with HTTP 202, and you collect the company data later.

So a 202 is neither an error nor a job with a job ID.

## Asynchronous endpoints [#asynchronous-endpoints]

Only two endpoints answer with 202, and both belong to Enrichment:

- **Single lookup**: [`GET /enrichment/company`](https://webmetic.de/docs/en/api/referenz/enrichment/company) returns the data of one company for a domain.
- **List**: [`POST /enrichment/lists/{list_id}/domains`](https://webmetic.de/docs/en/api/referenz/enrichment/lists-list-id-domains) adds domains to a list, which Webmetic then enriches.

All other endpoints answer at once with the result or an error, including the contact endpoints under `/contacts/*`.

Both calls need the Enrichment key, not the Visitor Intelligence key. New lists, new domains, and single lookups also need the trial, the Growth plan, or the Enterprise plan. Otherwise, the API answers with 403 and `PLAN_REQUIRED`.

## Single lookup [#single-lookup]

If Webmetic already knows the domain, the API answers at once with 200 and `"status": "found"`. Otherwise Webmetic starts the work and answers with 202:

```http
HTTP/1.1 202 Accepted
Retry-After: 15

{"status": "processing", "domain": "beispiel.de", "retry_after_seconds": 15}
```

Then repeat the same request after the number of seconds that `retry_after_seconds` or the `Retry-After` header gives. A repeated request doesn't start a second run for the same domain.

Keep asking until the API answers with 200 and `found` or with 404 and `not_found`. Here, a 404 means that the website could not be reached or had no usable data.

If Webmetic first has to read the website, it takes hours to a few days. The API doesn't end the single lookup on its own during that time. So give your client its own maximum wait and stop asking once it has passed.

For unknown domains we recommend a list, because there every row ends with a result.

```bash tab="cURL" tab-group="lang"
curl -i "https://hub.webmetic.de/enrichment/company?domain=beispiel.de" \
  -H "Authorization: wmtc_YOUR_API_KEY"
```

```js tab="JavaScript" tab-group="lang"
const url = 'https://hub.webmetic.de/enrichment/company?domain=beispiel.de';
const headers = { Authorization: 'wmtc_YOUR_API_KEY' };
const deadline = Date.now() + 10 * 60 * 1000; // your own maximum wait

let res = await fetch(url, { headers });
while (res.status === 202 && Date.now() < deadline) {
  const { retry_after_seconds } = await res.json();
  await new Promise((resolve) => setTimeout(resolve, retry_after_seconds * 1000));
  res = await fetch(url, { headers });
}
const body = await res.json(); // 200 found, 404 not_found, or still 202
```

```python tab="Python" tab-group="lang"
import time
import requests

url = "https://hub.webmetic.de/enrichment/company"
params = {"domain": "beispiel.de"}
headers = {"Authorization": "wmtc_YOUR_API_KEY"}
deadline = time.monotonic() + 10 * 60  # your own maximum wait

res = requests.get(url, params=params, headers=headers)
while res.status_code == 202 and time.monotonic() < deadline:
    time.sleep(res.json()["retry_after_seconds"])
    res = requests.get(url, params=params, headers=headers)
body = res.json()  # 200 found, 404 not_found, or still 202
```

## List workflow [#list-workflow]

1. **Send domains**: The API answers with 202 and counts new, existing, and invalid domains.
2. **Webmetic enriches**: Each new row starts as `pending` and moves to `enriching`.
3. **Check progress**: You fetch the list until `pending_rows` is 0.
4. **Collect results**: You load the rows as JSON or as a CSV file.

You create the list itself first with [`POST /enrichment/lists`](https://webmetic.de/docs/en/api/referenz/enrichment/lists-post), and the API answers at once with 201. Then you send up to 5,000 domains per request:

```bash tab="cURL" tab-group="lang"
curl -X POST "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/domains" \
  -H "Authorization: wmtc_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["beispiel.de", "muster-gmbh.de"]}'
```

```js tab="JavaScript" tab-group="lang"
const res = await fetch(
  'https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/domains',
  {
    method: 'POST',
    headers: {
      Authorization: 'wmtc_YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ domains: ['beispiel.de', 'muster-gmbh.de'] }),
  },
);
const result = await res.json(); // 202: added, skipped_existing, invalid, list_id
```

```python tab="Python" tab-group="lang"
import requests

res = requests.post(
    "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/domains",
    headers={"Authorization": "wmtc_YOUR_API_KEY"},
    json={"domains": ["beispiel.de", "muster-gmbh.de"]},
)
result = res.json()  # 202: added, skipped_existing, invalid, list_id
```

`added` counts the new rows, `invalid` the entries without a valid domain. `skipped_existing` counts domains that are already in the list, which Webmetic therefore doesn't enrich again.

[`GET /enrichment/lists/{list_id}`](https://webmetic.de/docs/en/api/referenz/enrichment/lists-list-id) shows the progress with three counters:

- **`done_rows`**: rows with the status `done`.
- **`miss_rows`**: rows with the status `miss`.
- **`pending_rows`**: all other rows, so `pending` and `enriching`.

Unlike the single lookup, the list endpoints don't name a polling interval. Because unknown domains take hours, infrequent checks are enough.

When `pending_rows` is 0, every row of the list is final. Then fetch [`/rows`](https://webmetic.de/docs/en/api/referenz/enrichment/lists-list-id-rows) page by page with `page` and `page_size`. A page holds 10,000 rows by default and at most, and the response contains `rows`, `total`, and `pagination`.

The `status` filter takes one of the four status values, and `total` then counts only the matching rows. [`/export`](https://webmetic.de/docs/en/api/referenz/enrichment/lists-list-id-export) returns all rows at once as a CSV file.

```bash tab="cURL" tab-group="lang"
curl "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID" \
  -H "Authorization: wmtc_YOUR_API_KEY"

curl "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/rows?status=done&page=1" \
  -H "Authorization: wmtc_YOUR_API_KEY"
```

```js tab="JavaScript" tab-group="lang"
const base = 'https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID';
const headers = { Authorization: 'wmtc_YOUR_API_KEY' };

const { list } = await (await fetch(base, { headers })).json();
if (list.pending_rows === 0) {
  const res = await fetch(`${base}/rows?status=done&page=1`, { headers });
  const { rows, pagination } = await res.json();
}
```

```python tab="Python" tab-group="lang"
import requests

base = "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID"
headers = {"Authorization": "wmtc_YOUR_API_KEY"}

list_info = requests.get(base, headers=headers).json()["list"]
if list_info["pending_rows"] == 0:
    res = requests.get(f"{base}/rows", params={"status": "done", "page": 1}, headers=headers)
    rows = res.json()["rows"]
```

## Row status [#row-status]

Every row in a list has one of four status values. The dashboard shows the same rows with its own labels:

| Status | Meaning | In the dashboard |
| --- | --- | --- |
| `pending` | The row is waiting to be processed | **Enriching** |
| `enriching` | Webmetic is enriching the row right now | **Enriching** |
| `done` | Webmetic found data | **Enriched** |
| `miss` | Webmetic found no usable data | **Not found** |

For all fields of a row, see [Enrichment list](https://webmetic.de/docs/en/api/datenmodell/enrichment-liste).

`done` and `miss` are final. While Webmetic still has to read the website, a row can move from `enriching` back to `pending`.

- **Partly filled**: Because Webmetic writes the fields of a row one after another, `/rows` and `/export` also show half-filled rows before the end.
- **Empty fields**: A `done` row can still have empty fields when Webmetic doesn't know a value. In `/rows` these fields appear as `null`.
- **Dashboard**: Lists from the API appear in the **Enrichment** tab, single lookups don't.

## Credits and webhooks [#credits-and-webhooks]

Checking the status costs no credits, neither the repeated single lookup nor fetching the list, rows, or export.

Enrichment currently costs no credits. For details, see [Credits in the API](https://webmetic.de/docs/en/api/credits).

Webmetic sends no webhook event for Enrichment results, because [Webhooks](https://webmetic.de/docs/en/api/webhooks) only report company visits to your website. So you check the progress of a list yourself.

## Next steps [#next-steps]

- [Pagination](https://webmetic.de/docs/en/api/paginierung): Fetch the rows of a large list page by page.
- [Errors](https://webmetic.de/docs/en/api/fehler): Understand and fix error codes such as `PLAN_REQUIRED`.
- [Enrichment](https://webmetic.de/docs/en/enrichment): Create, track, and export lists in the dashboard.
