---
title: recipes skill
description: "The looot plugin's step lists for common jobs: lead enrichment, account research, SEO and GEO checks, page to markdown, and social lookups."
sidebar:
  label: Recipes
---

`recipes` loads automatically to enrich a lead list, research a company from its domain, check
SEO and GEO (AI answer) visibility for a keyword, turn a web page into markdown, or look up a
social profile. Every recipe reuses the search-and-run loop from
[find-and-run](/plugin/skills/find-and-run): before a job it hasn't run yet, it searches and reads
[`jobInputs`](/concepts/job-inputs), since live input names win over anything written here. It quotes the estimated total
before a batch, checks [`balance`](/mcp-tools/balance), and gives every run its own [`idempotencyKey`](/concepts/idempotency).

## Enrich a lead list: find and verify a work email

1. Quote it: read `costPerSuccessUsd` for `people.email.find` and `people.email.verify`,
   multiply by the number of leads, check `balance`.
2. Find, one lead per run, with `first_name`/`last_name` (or `name`) plus `domain`:

```json
{
  "tool": "run",
  "input": {
    "endpointId": "job:people.email.find",
    "input": {"first_name": "Jane", "last_name": "Doe", "domain": "example.com"},
    "idempotencyKey": "leads-row1-find",
    "fallback": {"maxAttempts": 3, "maxCostUsd": 0.1}
  }
}
```

3. Read `normalized.fields.email`. An `outcome: "weak"` with `verdict: "guessed"` is a pattern
   guess, not a confirmed address.
4. Verify every address it keeps:

```json
{
  "tool": "run",
  "input": {
    "endpointId": "job:people.email.verify",
    "input": {"email": "jane.doe@example.com"},
    "idempotencyKey": "leads-row1-verify",
    "fallback": true
  }
}
```

5. Read `normalized.fields.status`: `valid`, `invalid`, `catch_all`, `risky` or `unknown`. Keep
   `valid`, flag `catch_all` and `risky` for the user.
6. Runs a few leads at a time; on [`too_many_inflight_runs`](/errors/rest-errors#too_many_inflight_runs) it waits 2 seconds and retries with the
   same key. It reports found, verified and the total from `actualCost`.

## Account research from a domain

Each of these runs with `{"domain": "<domain>"}` and fallback: `job:company.enrich` (name,
industry, size), `job:company.technographics` (technologies the site runs), `job:company.news`
(recent news, also takes `company`), `job:people.domain.search` (known email addresses at the
domain), and `job:linkedin.company.profile` (takes `linkedin_url` when known).

```json
{
  "tool": "run",
  "input": {
    "endpointId": "job:company.enrich",
    "input": {"domain": "example.com"},
    "idempotencyKey": "research-example-enrich",
    "fallback": {"maxAttempts": 2, "maxCostUsd": 0.1}
  }
}
```

It writes one brief: what the company does, its size, stack, recent news and contacts, naming the
provider behind each fact from [`servedProviderId`](/concepts/served-provider).

## SEO and GEO check for a keyword

1. Google results, `job:google.serp.organic` (`query`, optionally `location`, `language`,
   `country`, `device`).
2. AI answers (GEO): `google.serp.ai-mode`, `search.google-ai-overview`, `search.chatgpt`, checked
   for the brand and domain in the answer and its cited sources.
3. Search volume: `google.keywords.volume`, quoted first since it's priced higher than a results
   page.
4. Domain strength: `backlinks.domain.summary`.
5. Steps 2 to 4 are jobs with one provider each: the skill searches for the job, inspects the
   endpoint, then runs it with that endpoint's own `requiredInputFields`.
6. It reports the domain's position in the results, whether each AI answer mentions or cites it,
   the volume, and the backlink totals.

## Web page to markdown

```json
{
  "tool": "run",
  "input": {
    "endpointId": "job:web.scrape.markdown",
    "input": {"url": "https://example.com/pricing"},
    "idempotencyKey": "scrape-example-pricing",
    "fallback": {"maxAttempts": 3, "maxCostUsd": 0.05}
  }
}
```

It reads `normalized.fields.markdown` and `title`. A blocked, empty or sign-in page comes back as
a `miss`, and fallback moves on to the next scraper. `web.scrape.structured` returns JSON instead
of markdown.

## Social profile lookup

| Network | Job | Input |
| --- | --- | --- |
| LinkedIn person | `job:linkedin.person.profile` | `linkedin_url` |
| LinkedIn company | `job:linkedin.company.profile` | `linkedin_url` |
| TikTok | `job:tiktok.user.profile` | `handle` (no @) |
| Instagram | `job:instagram.user.profile` | `handle` (no @) |

```json
{
  "tool": "run",
  "input": {
    "endpointId": "job:tiktok.user.profile",
    "input": {"handle": "exampleco"},
    "idempotencyKey": "tiktok-exampleco",
    "fallback": true
  }
}
```

For another network, the skill searches for it ("x user profile", "youtube channel") and reads
`jobInputs`.

See [find-email](/plugin/skills/find-email) and [research-company](/plugin/skills/research-company)
for the two recipes above wrapped as slash commands.

<Related />
