> ## 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.

# Join the people and company feeds

> Join the Forager person and organization data feeds in Snowflake with SQL: flatten roles, match on company domain and filter to current roles.

```text Recipe prompt theme={"theme":{"light":"github-light","dark":"github-dark"}}
Implement this recipe in my project: https://www.forager.ai/docs/recipes/feed-join-snowflake.md

Write a Snowflake SQL view that joins the Forager person data feed to the organization data feed. Flatten the roles array in each person's serialized_data, keep roles with no end_date, and join to the organization feed on the lowercase domain. Use only fields in the documented feed schema, and mark table names and column types that the docs do not state as configuration. Handle null domains, people with several current roles, and duplicate organizations. Keep Snowflake credentials in your secrets manager, not in the repo. Add a test query that checks the join row counts.
```

![People and company data feeds joined in a Snowflake warehouse](https://mintcdn.com/forager/OnhSHNo_LBQ7yKjR/images/recipes/company-data-snowflake-feed-join.svg?fit=max\&auto=format\&n=OnhSHNo_LBQ7yKjR\&q=85\&s=b53b32f314d97f2487b2755dcd1803b1)

## What you'll build

A view that answers questions such as "who currently works at companies in this industry". The person feed holds each person's roles. The organization feed holds the company. You flatten the roles and join on company domain.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart LR
  A[Person feed] --> B[Flatten roles]
  B --> C[Current roles only]
  D[Organization feed] --> E[Join on domain]
  C --> E
  E --> F[View]
```

> [!NOTE]
>
> Each feed row carries the record in its `serialized_data` property. See the [feed schema](https://www.forager.ai/docs/data-license/v2/schema.md). The docs do not state the table names, the column type of `serialized_data`, or the schema name in Snowflake.

## Steps

**1. Get the feeds into your account**

Request delivery as described in [Snowflake data share delivery](https://www.forager.ai/docs/data-license/v2/data-delivery-snowflake.md). The Marketplace listings are covered separately in [Forager on Snowflake Marketplace](https://www.forager.ai/docs/integrations/snowflake-marketplace.md). The queries below use `PERSON_FEED` and `ORGANIZATION_FEED` as stand-in table names. Replace them with the names in your share.

**2. Flatten each person's roles**

A [PersonProfile](https://www.forager.ai/docs/data-license/v2/schema/person/person-profile.md) record has a `roles` array. Each role has `role_title`, `start_date`, `end_date`, `organization_id`, `organization_name` and `organization_domain`. The feed has no `is_current` field, so treat a role with no `end_date` as current.

```sql theme={"theme":{"light":"github-light","dark":"github-dark"}}
CREATE OR REPLACE VIEW CURRENT_ROLES AS
SELECT
  p.serialized_data:forager_id::INTEGER   AS person_id,
  p.serialized_data:first_name::STRING    AS first_name,
  p.serialized_data:last_name::STRING     AS last_name,
  p.serialized_data:linkedin_url::STRING  AS linkedin_url,
  r.value:role_title::STRING              AS role_title,
  LOWER(r.value:organization_domain::STRING) AS organization_domain
FROM PERSON_FEED p,
     LATERAL FLATTEN(input => p.serialized_data:roles) r
WHERE r.value:end_date IS NULL
  AND r.value:organization_domain IS NOT NULL;
```

**3. Join to the organization feed**

A [Forager Organization Data](https://www.forager.ai/docs/data-license/v2/schema/organization/forager-organization-data.md) record has `forager_id`, `name`, `domain`, `industry`, `employees_range`, `country` and `city`. Match on the lowercase `domain`.

```sql theme={"theme":{"light":"github-light","dark":"github-dark"}}
CREATE OR REPLACE VIEW PEOPLE_AT_COMPANIES AS
SELECT
  r.person_id,
  r.first_name,
  r.last_name,
  r.role_title,
  o.serialized_data:forager_id::INTEGER   AS organization_id,
  o.serialized_data:name::STRING          AS organization_name,
  o.serialized_data:industry::STRING      AS industry,
  o.serialized_data:employees_range::STRING AS employees_range
FROM CURRENT_ROLES r
JOIN ORGANIZATION_FEED o
  ON LOWER(o.serialized_data:domain::STRING) = r.organization_domain;
```

**4. Query it**

```sql theme={"theme":{"light":"github-light","dark":"github-dark"}}
SELECT organization_name, COUNT(*) AS engineers
FROM PEOPLE_AT_COMPANIES
WHERE role_title ILIKE '%engineer%'
GROUP BY organization_name
ORDER BY engineers DESC
LIMIT 20;
```

To add the job post feed, join its `organization_forager_id` to the organization's `forager_id`. Each job post also carries `organization_domain`.

**5. Keep it fresh**

Person and organization records always have a `date_updated`. On job posts it can be null. Filter on it when you build incremental tables, so you only reprocess rows that changed since your last run.

## Handle the edge cases

* **Not found.** A person whose roles have no `organization_domain` drops out of the join. Count them so you know how many.
* **Several current roles.** A person with two open roles appears twice. Deduplicate on `person_id` if you want one row per person.
* **Duplicate organizations.** More than one organization row can share a domain. Check with `GROUP BY domain HAVING COUNT(*) > 1` before you trust counts.
* **Domain formatting.** Domains are compared in lowercase. Strip a scheme or a `www.` prefix if either feed includes one.
* **API errors and credits.** These queries run in Snowflake and do not call the REST API, so 403 and 429 do not apply. API requests use credits; see [credit pricing](https://www.forager.ai/docs/api-overview/credit-pricing.md) and [errors and rate limits](https://www.forager.ai/docs/errors-and-rate-limits.md).

## Related

> [card] [Data feed overview](https://www.forager.ai/docs/data-license/v2/data-feed-overview.md)
>
> Feeds, license and delivery options.

> [card] [Feed schema](https://www.forager.ai/docs/data-license/v2/schema.md)
>
> Every field of the three feeds.

> [card] [Snowflake delivery](https://www.forager.ai/docs/data-license/v2/data-delivery-snowflake.md)
>
> Request and access the share.

> [card] [Role fields](https://www.forager.ai/docs/data-license/v2/schema/person/role.md)
>
> The roles array in the person feed.


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