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

> ## Agent Instructions
> Forager is a REST API for B2B people, company and job post data: contact lookups (phone numbers, personal emails, work emails), person, company and job post search, and bulk data feeds.
> Server: https://api-v2.forager.ai. Send the API key in the X-API-KEY header. Most paths include {account_id}. Read FORAGER_API_KEY and FORAGER_ACCOUNT_ID from the environment; never put the key in client-side code, logs or prompts.
> To add Forager to a project, follow https://www.forager.ai/docs/guides/agent-quickstart.md. To pick an endpoint, read https://www.forager.ai/docs/guides/choose-an-endpoint.md. For end-to-end builds, see https://www.forager.ai/docs/recipes/overview.md.
> Most requests spend credits. Do not guess costs; use https://www.forager.ai/docs/api-overview/credit-pricing.md and ask the user before spending credits.
> Use only endpoints and fields defined in https://www.forager.ai/docs/openapi.yaml. Forager has no MCP server for the API yet; the /mcp endpoint on this site searches the documentation only.

# cURL quickstart for the Forager API

> cURL quickstart for the Forager API: set your key, run a person lookup, handle 403, 429 and out-of-credits responses, then poll an async work email job.

Forager is a REST API, so everything on this page is a plain `curl` call: one person lookup, the responses to handle when a call fails, and one asynchronous work email lookup that you submit, poll and read. You need `curl` and, for the polling loop, `jq`.

**1. Set your key and Account ID**

Create a key on the [API](https://app.forager.ai/keys) page of the Forager Web App and copy your Account ID from [My Account](https://app.forager.ai/profile). Export both; every call below reads them from the environment, never from the command line history.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export FORAGER_API_KEY="your-api-key"
export FORAGER_ACCOUNT_ID="your-account-id"
```

The key travels in the `X-API-KEY` header. See [API key authentication](https://www.forager.ai/docs/api-overview/authentication.md).

**2. Run your first lookup**

A [person lookup](https://www.forager.ai/docs/openapi/people/datastorage_person_detail_lookup_create.md) costs 1 credit and takes a LinkedIn public identifier (the slug after `/in/`) or a Forager `person_id`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -sS -X POST "https://api-v2.forager.ai/api/$FORAGER_ACCOUNT_ID/datastorage/person_detail_lookup/" \
  -H "X-API-KEY: $FORAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"linkedin_public_identifier": "jane-doe-example"}'
```

The response is one person record. Trimmed, with fictional values:

```json Response (illustrative) theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": 48213,
  "full_name": "Jane Doe",
  "headline": "Head of Data at Acme Example",
  "linkedin_info": { "public_identifier": "jane-doe-example" },
  "roles": [
    { "role_title": "Head of Data", "is_current": true, "organization": { "name": "Acme Example", "domain": "acme.com" } }
  ]
}
```

Keep `id`: the phone and personal email lookups take it as `person_id`.

**3. Handle the responses that are not a 200**

Add `-w '\n%{http_code}\n'` to see the status code with the body, then branch on it. The documented cases:

| Status | What happened | What to do |
| - | - | - |
| `403` | `{"detail": "Authentication credentials were not provided."}`: the header is missing or the key is wrong, or the key cannot use this Account ID. | Fix the credentials. Never retry in a loop. |
| `429` | Rate limit. Documented for the [autocomplete](https://www.forager.ai/docs/openapi/autocomplete) and [feedback](https://www.forager.ai/docs/openapi/feedback) endpoints; the single work email lookup is also limited for interactive use. | Wait, then retry with backoff. Use the `Retry-After` header when one is sent. |
| Out of credits | A bulk work email batch your balance cannot cover is rejected and nothing is charged. The status code and body are not documented yet. | Buy credits in the [Web App](https://app.forager.ai/subscription), then resubmit. |

This script retries a `429` twice, with a growing wait, and prints everything else:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
forager_post() {  # usage: forager_post <path-after-/api/> '<json body>'
  local attempt=0 wait=2 code body
  while :; do
    body=$(curl -sS -X POST "https://api-v2.forager.ai/api/$1" \
      -H "X-API-KEY: $FORAGER_API_KEY" -H "Content-Type: application/json" \
      -d "$2" -w '\n%{http_code}')
    code=${body##*$'\n'}; body=${body%$'\n'*}
    case "$code" in
      2??) printf '%s\n' "$body"; return 0 ;;
      403) echo "forager: check FORAGER_API_KEY and FORAGER_ACCOUNT_ID: $body" >&2; return 1 ;;
      429) attempt=$((attempt + 1)); [ "$attempt" -gt 2 ] && { echo "forager: still rate limited" >&2; return 1; }
           sleep "$wait"; wait=$((wait * 2)) ;;
      402) echo "forager: out of credits (status not yet documented): $body" >&2; return 1 ;;
      *)   echo "forager: HTTP $code: $body" >&2; return 1 ;;
    esac
  done
}

forager_post "$FORAGER_ACCOUNT_ID/datastorage/person_detail_lookup/" '{"linkedin_public_identifier": "jane-doe-example"}'
```

An empty list or an empty body on a contact lookup is a result, not an error: the person was found but no number or address is on record.

**4. Submit an async work email lookup**

The [work email lookup](https://www.forager.ai/docs/openapi/work-emails/v2_datastorage_person_contacts_lookup_work_emails_create.md) answers `202` at once with a `job_id`, because verifying an address takes longer than a request should wait. Identify the person by name and company domain, or by LinkedIn public identifier.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
JOB=$(forager_post "v2/$FORAGER_ACCOUNT_ID/datastorage/person_contacts_lookup/work_emails/" \
  '{"first_name": "Jane", "last_name": "Doe", "domain": "acme.com"}')
JOB_ID=$(printf '%s' "$JOB" | jq -r .job_id)
echo "$JOB_ID"
```

```json 202 response (illustrative) theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "job_id": "2b7e1a40-3c5d-4f6e-8a9b-0c1d2e3f4a5b",
  "status": "queued",
  "status_check_url": "https://api-v2.forager.ai/api/v2/123456/datastorage/person_contacts_lookup/work_emails/2b7e1a40-3c5d-4f6e-8a9b-0c1d2e3f4a5b/",
  "webhook_url": ""
}
```

**5. Poll until the job is terminal**

Poll the [job result](https://www.forager.ai/docs/openapi/work-emails/v2_datastorage_person_contacts_lookup_work_emails_retrieve.md) until `status` is `completed` or `failed`. Polling is free. While the job is still running the response carries a `Retry-After` header that says how long to wait.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
while :; do
  resp=$(curl -sS -D - "https://api-v2.forager.ai/api/v2/$FORAGER_ACCOUNT_ID/datastorage/person_contacts_lookup/work_emails/$JOB_ID/" \
    -H "X-API-KEY: $FORAGER_API_KEY")
  body=$(printf '%s' "$resp" | sed '1,/^\r\{0,1\}$/d')
  status=$(printf '%s' "$body" | jq -r .status)
  case "$status" in
    completed|failed) printf '%s\n' "$body" | jq .; break ;;
  esac
  wait=$(printf '%s' "$resp" | awk 'tolower($1)=="retry-after:" {gsub("\r","",$2); print $2}')
  sleep "${wait:-5}"
done
```

A completed job has `result` set to `found` (with `emails`) or `not_found`. A failed job has an `error_code` such as `person_not_found` or `no_current_employer`.

```json Completed job (illustrative) theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "job_id": "2b7e1a40-3c5d-4f6e-8a9b-0c1d2e3f4a5b",
  "status": "completed",
  "result": "found",
  "emails": ["jane.doe@acme.com"],
  "error_code": null,
  "person_id": 48213
}
```

5 credits are charged only when `result` is `found`. To skip polling, send a `webhook_url` with the submit request and Forager POSTs the result there. For more than a handful of people, use the [bulk lookup](https://www.forager.ai/docs/guides/bulk-work-emails.md) instead of a loop of single lookups.

## Next steps

> [card] [Choose the right endpoint](https://www.forager.ai/docs/guides/choose-an-endpoint.md)
>
> Match what you have to the endpoint you need, with each call's credit cost.

> [card] [Errors and rate limits](https://www.forager.ai/docs/errors-and-rate-limits.md)
>
> Every documented status code and limit.

> [card] [Python quickstart](https://www.forager.ai/docs/quickstarts/python.md)
>
> The same walkthrough with `requests`.

> [card] [Node.js quickstart](https://www.forager.ai/docs/quickstarts/node.md)
>
> The same walkthrough with `fetch`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.