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

# Enrichment Tools

> Three tools that take what you already know about a person, company or place and return an attribute you don't: an SMTP-verified email address, social handles across LinkedIn, X, Instagram and TikTok, and latitude/longitude for any free-text location.

Enrichment tools all share a shape: you pass an identifier you already have, and you get back an attribute you don't. A name plus a domain becomes a verified email address. A name becomes a set of social handles. A free-text location becomes coordinates.

They are grouped together because they behave alike, not because they share a data source. Each answers in one call, none of them paginate, and each returns a `status` field that tells you whether the answer is trustworthy rather than handing back a guess. When enrichment can't confirm something, it says so — that contract is the reason these tools are safe to run across a list unattended.

## Tools

| Tool                  | Credits | Description                                                     |
| --------------------- | ------- | --------------------------------------------------------------- |
| `get_email`           | 5       | Get a person's professional email (SMTP-verified)               |
| `get_social_profiles` | 5       | Get a person's handles across LinkedIn, X, Instagram and TikTok |
| `get_coordinates`     | 1       | Get coordinates (lat/lng) for a free-text location              |

> **Note**
>
> `get_email` requires the [GTM Tools browser extension](/extension) to be connected. It confirms Google Workspace mailboxes (the largest provider) through a logged-in Google session shared via the extension. If no session is connected, the tool returns setup instructions instead of running. Install it and press **Connect** once per workspace. `get_social_profiles` and `get_coordinates` need no session.

## get_email

| Parameter          | Type   | Required | Description                                                                  |
| ------------------ | ------ | -------- | ---------------------------------------------------------------------------- |
| `name`             | string | Yes      | Full name of the person (e.g. `"Justin Mares"`)                              |
| `domain`           | string | Yes      | Company domain (e.g. `"kettleandfire.com"`)                                  |
| `input_parameters` | object | No       | Free-form metadata echoed back in the response (useful for batch processing) |

```bash
curl -X POST https://api.gtm-tools.sh/api/v0/get_email \
  -H "Authorization: Bearer $GTM_TOOLS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Justin Mares",
    "domain": "kettleandfire.com",
    "input_parameters": {"source": "linkedin"}
  }'
```

```json
{
  "email": "justin@kettleandfire.com",
  "status": "ok",
  "domain": "kettleandfire.com",
  "input_parameters": {"source": "linkedin"}
}
```

### Response fields

| Field                 | Type           | Description                                                                                                                                                                                 |
| --------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`               | string \| null | The SMTP-verified deliverable address, or `null` on any non-`ok` status                                                                                                                     |
| `status`              | string         | `ok`, `not_found`, or `try_again_later`, the whole contract in one field                                                                                                                    |
| `reason`              | string         | Present on non-`ok` results: `catch_all`, `no_mx`, `unverifiable_provider`, `no_pattern_verified`, `smtp_blocked`, `prober_rejected`, `smtp_unavailable`, `google_cooldown`, `ms_throttled` |
| `retry_after_seconds` | number         | Present on `try_again_later` only                                                                                                                                                           |
| `domain`              | string         | Echoes back the input domain                                                                                                                                                                |
| `input_parameters`    | object         | Echoes back the input metadata (for batch correlation)                                                                                                                                      |

### When the domain is catch-all

Catch-all domains accept SMTP traffic for every local-part, so verification can't disambiguate the true address. Rather than hand back a guess that might silently bounce, `get_email` stays strict:

```json
{ "email": null, "status": "not_found", "reason": "catch_all", "domain": "acme.com" }
```

If you want to act anyway, generate the dominant pattern yourself and treat it as a hypothesis: cross-check against LinkedIn, and prefer a channel where being wrong is cheap. Every `reason` value and what to do about it is on [Why is get_email returning not_found?](/knowledge-base/troubleshooting/email-not-found), and the batching and retry patterns are in [Outbound](/outbound#step-4-verify-the-professional-email).

### How verification works

`get_email` does three things:

1. **Generate candidates.** Every common pattern is produced from the name (`justin@`, `justinmares@`, `justin.mares@`, `j.mares@`, `jmares@`, etc.).
2. **Verify via SMTP.** Each candidate is probed against the domain's MX records: the verifier speaks SMTP, issues `RCPT TO`, and observes the server's response, all without `DATA`, so no mail is ever sent.
3. **Return the first deliverable hit.** Catch-all domains are detected by probing a known-bogus address first; if the server accepts it, the result comes back as `not_found` with `reason: "catch_all"` rather than a best guess.

This adds 2–5 seconds of latency per call.

## get_social_profiles

`get_social_profiles` answers a different question: given a person's name, which accounts across
LinkedIn, X, Instagram and TikTok belong to them?

| Parameter  | Type   | Required | Description                                         |
| ---------- | ------ | -------- | --------------------------------------------------- |
| `name`     | string | Yes      | The person's full name                              |
| `company`  | string | No       | Company name, to disambiguate a common name         |
| `domain`   | string | No       | Company domain, to disambiguate a common name       |
| `location` | string | No       | City or country, to disambiguate a common name      |
| `keywords` | string | No       | Other distinguishing terms, such as a role or niche |

```bash
curl -X POST https://api.gtm-tools.sh/api/v0/get_social_profiles \
  -H "Authorization: Bearer $GTM_TOOLS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Andrei Negrau", "domain": "siena.cx"}'
```

It reads search-engine snippets only. There is no platform login and no profile fetch, which is why
it works on Instagram and TikTok at all: their profile pages are hostile to anonymous requests, but
their snippets are indexed.

**Always pass context when you have it.** A bare common name returns whoever ranks highest in search,
which may be a different person entirely. Any `company`, `domain`, `location` or `keywords` you supply
must be corroborated in a candidate's own snippet before that candidate is accepted, so context does
not just re-rank the results, it filters them.

The strongest signal is a brand appearing on two different platforms. That is what resolves a handle
bearing no resemblance to the person's name, such as a personal brand or a nickname account.

### Response fields

| Field                        | Type   | Description                                                                            |
| ---------------------------- | ------ | -------------------------------------------------------------------------------------- |
| `status`                     | string | `ok` or `error`                                                                        |
| `data.name`                  | string | Echoes back the name searched                                                          |
| `data.confidence`            | number | 0 to 1, reflecting how many platforms were resolved — **not** a per-handle probability |
| `data.linkedin_profile_url`  | string | Present only when LinkedIn resolved confidently                                        |
| `data.x_profile_url`         | string | Present only when X resolved confidently                                               |
| `data.instagram_profile_url` | string | Present only when Instagram resolved confidently                                       |
| `data.tiktok_profile_url`    | string | Present only when TikTok resolved confidently                                          |
| `data.matches`               | array  | Per-platform detail: the `platform`, `handle`, `url` and the `score` it won on         |

Platforms with no confident match are omitted rather than guessed, so treat a missing key as "not
found", not as an error.

Reach for [`get_linkedin_profile_url`](/linkedin-tools#resolving-identifiers) instead when you only
need LinkedIn and you already know the company domain: it is cached, so a repeat lookup is instant.

## get_coordinates

`get_coordinates` forward-geocodes any free-text location — a street address, city, region, landmark
or country — to a latitude/longitude using global OpenStreetMap data. At 1 credit it is the cheapest
tool in the catalog.

| Parameter          | Type   | Required | Description                                                                  |
| ------------------ | ------ | -------- | ---------------------------------------------------------------------------- |
| `location`         | string | Yes      | Free-text location (e.g. `"1600 Amphitheatre Parkway, Mountain View, CA"`)   |
| `input_parameters` | object | No       | Free-form metadata echoed back in the response (useful for batch processing) |

```bash
curl -X POST https://api.gtm-tools.sh/api/v0/get_coordinates \
  -H "Authorization: Bearer $GTM_TOOLS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"location": "1600 Amphitheatre Parkway, Mountain View, CA"}'
```

```json
{
  "status": "ok",
  "location": "1600 Amphitheatre Parkway, Mountain View, CA",
  "lat": 37.4224858,
  "lng": -122.0855846,
  "formatted": "Google Building 41, 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States of America",
  "country": "United States",
  "city": "Mountain View",
  "result_type": "amenity",
  "confidence": 1
}
```

### Response fields

| Field              | Type           | Description                                                                         |
| ------------------ | -------------- | ----------------------------------------------------------------------------------- |
| `status`           | string         | `ok`, `not_found`, or `try_again_later`                                             |
| `location`         | string         | Echoes back the input                                                               |
| `lat` / `lng`      | number \| null | Coordinates of the best match, `null` on any non-`ok` status                        |
| `formatted`        | string \| null | The full address that was actually matched — check this before trusting `lat`/`lng` |
| `country`          | string \| null | Country of the match                                                                |
| `city`             | string \| null | City of the match                                                                   |
| `result_type`      | string \| null | What kind of thing matched: `amenity`, `city`, `country`, and so on                 |
| `confidence`       | number \| null | Match confidence from 0 to 1                                                        |
| `input_parameters` | object         | Echoes back the input metadata (for batch correlation)                              |

`formatted` and `result_type` together are how you catch a wrong answer. A `location` of `"Springfield"`
resolves, but `result_type: "city"` with a `formatted` naming the wrong state tells you the input was
ambiguous rather than the geocoder wrong. On `try_again_later` the upstream geocoder was rate-limited;
retry the same input.

## Next steps

#### [LinkedIn Tools](/linkedin-tools)

Find the people and companies you then enrich.

#### [Signals Tools](/signals-tools)

Detect buying intent on the same domains.

#### [Browser Extension](/extension)

Connect the Google session `get_email` verifies against.

#### [Outbound](/outbound)

Stitch enrichment together with LinkedIn and signals: catch-all, batching, retries.