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

# Search and save free company data

> Resolve a target list, query a bounded segment, and save matching companies to your workspace.

[Human guide: work with your agent →](/docs/human/recipes/search-free-companies)

> [!NOTE]
>
> Query a bounded free-company result and preserve its source identity before
> further enrichment.

Use the [free company search contract](/docs/free-company-search-database) for
columns, limits, and snapshot semantics. These procedures query the shared
company snapshot. they do not perform live web discovery.

## Start from the CLI

Inspect the live tool schema first:

```bash theme={null}
deepline describe free_simple_company_search --json
```

Then run one bounded query. This exact-domain lookup is the safest starting
point for a CRM or CSV export that already contains normalized domains:

```bash theme={null}
PAYLOAD=$(cat <<'JSON'
{
  "sql": "SELECT normalized_domain, domain, company_name, industry, location, employee_count, year_founded FROM companies WHERE normalized_domain = 'openai.com' LIMIT 5"
}
JSON
)

deepline tools execute free_simple_company_search --input "$PAYLOAD"
deepline tools execute free_simple_company_search --input "$PAYLOAD" --out companies.csv
```

The direct command returns matching rows. `--out` writes row-shaped results to
CSV. It does not modify the Database or the shared snapshot.

## Practical SQL patterns

### Resolve and normalize a target list

Normalize website values to lowercase root domains before putting them in the
`IN` list. The output gives you the snapshot's company name and LinkedIn URL
for review. It does not automatically overwrite CRM values.

```sql theme={null}
SELECT normalized_domain, company_name, linkedin_url
FROM companies
WHERE normalized_domain IN ('openai.com', 'anthropic.com', 'vercel.com')
LIMIT 10
```

For a small list that already has LinkedIn company URLs, use exact equality or
`IN` as well:

```sql theme={null}
SELECT company_name, normalized_domain, linkedin_url
FROM companies
WHERE linkedin_url IN ('linkedin.com/company/openai')
LIMIT 5
```

### Find candidates after an exact miss

Use a prefix only after an exact domain, LinkedIn URL, or company-name lookup
misses. Inspect the returned domain before using a candidate downstream:

```sql theme={null}
SELECT company_name, normalized_domain, location, employee_count
FROM companies
WHERE company_name ILIKE 'acme%'
LIMIT 25
```

### Build a bounded pilot segment

First count a narrowly defined segment, then request a small page. Employee
count is the upper bound of a source size bucket, not a live headcount.

```sql theme={null}
SELECT COUNT(*) AS company_count
FROM companies
WHERE industry ILIKE '%software%'
  AND employee_count >= 50
LIMIT 1
```

```sql theme={null}
SELECT normalized_domain, company_name, industry, location, employee_count, year_founded
FROM companies
WHERE location ILIKE 'san francisco%'
  AND year_founded >= 2020
ORDER BY employee_count DESC
LIMIT 100
```

### Check coverage and source duplicates

Compare `matched_domains` with the number of unique normalized domains in your
input file. Unmatched domains do not appear in the result, so this tool cannot
label them as missing for you.

```sql theme={null}
SELECT
  COUNT(*) AS source_records,
  COUNT(DISTINCT normalized_domain) AS matched_domains
FROM companies
WHERE normalized_domain IN ('openai.com', 'anthropic.com', 'vercel.com')
LIMIT 1
```

This query checks duplicate source records for the requested domains. It does
not detect duplicate rows in your CRM or CSV.

```sql theme={null}
SELECT normalized_domain, COUNT(*) AS source_record_count
FROM companies
WHERE normalized_domain IN ('openai.com', 'anthropic.com', 'vercel.com')
GROUP BY normalized_domain
HAVING COUNT(*) > 1
LIMIT 20
```

## Query from the API

Use the normal tool execution endpoint:

```bash theme={null}
curl -X POST "https://code.deepline.com/api/v2/integrations/free_simple_company_search/execute" \
  -H "Authorization: Bearer $DEEPLINE_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
    "payload": {
      "sql": "SELECT normalized_domain, company_name FROM companies WHERE normalized_domain = 'openai.com' LIMIT 5"
    }
}
JSON
```

The response wraps the SQL result in `data`:

```json theme={null}
{
  "data": {
    "schema": "public.companies",
    "command": "SELECT",
    "row_count_returned": 1,
    "truncated": false,
    "columns": [
      { "name": "company_name", "table_id": null, "data_type_id": null }
    ],
    "sql": "SELECT company_name FROM companies WHERE normalized_domain = 'openai.com' LIMIT 5",
    "executed_sql": "SELECT company_name FROM COMPANIES WHERE normalized_domain = 'openai.com' LIMIT 5",
    "limit": 5,
    "rows": [{ "company_name": "OpenAI" }]
  }
}
```

## Use inside a Play

Inside a Play, call the tool through `ctx.tools.execute`. Row-shaped extracted
list getters return a Dataset Handle. Pass the rows through the Play output or
into another `ctx.dataset` stage.

```ts theme={null}
import { definePlay } from 'deepline';

export default definePlay('free-company-search', async (ctx) => {
  const result = await ctx.tools.execute({
    id: 'free_company_search',
    tool: 'free_simple_company_search',
    input: {
      sql: "SELECT normalized_domain, company_name, industry, location FROM companies WHERE location ILIKE 'san francisco%' LIMIT 100",
    },
    description: 'Find matching companies from the free company corpus.',
    staleAfterSeconds: 0,
  });

  const companies = result.extractedLists.rows.get();

  return {
    companies,
    count: await companies.count(),
  };
});
```

After the Play run completes, use the emitted run ID to export the returned dataset:

```bash theme={null}
RUN_ID_FILE="./free-company-search-run.json"
deepline plays run --file ./free-company-search.play.ts --watch --run-id-file "$RUN_ID_FILE"
RUN_ID="$(jq -er '.runId' "$RUN_ID_FILE")"
deepline runs export "$RUN_ID" --dataset result.companies --out companies.csv
```

## Save matching companies to your workspace database

To save query results in your Database, use
`materialize_free_customer_companies`. It runs the SQL query and upserts rows
into `enrichments.companies`.

The query must return only these company columns:

```txt theme={null}
domain, company_name, industry, location, linkedin_url, employee_count, year_founded
```

Example:

```bash theme={null}
PAYLOAD=$(cat <<'JSON'
{
  "sql": "SELECT normalized_domain AS domain, company_name, industry, location, linkedin_url, employee_count, year_founded FROM companies WHERE industry ILIKE '%software%' AND employee_count >= 50 LIMIT 1000"
}
JSON
)

deepline tools execute materialize_free_customer_companies --input "$PAYLOAD"
```

Then query the saved rows with `query_customer_db`:

```bash theme={null}
deepline tools execute query_customer_db --input '{
  "sql": "SELECT domain, company_name, industry, employee_count FROM enrichments.companies ORDER BY updated_at DESC LIMIT 50"
}'
```

## TAM builder guidance

For TAM work, use this source when the ICP fits its documented fields:

* Exact domains or company names
* LinkedIn company URLs
* Industry or location
* Employee count or founding year
  When the required signal is absent, inspect another supported source. Apollo requires an explicitly selected customer-credential route. Do not assume managed Apollo access.

## Verify this task

Check selected columns and returned row scope. Keep direct source reads distinct from writes to your workspace customer tables.

Ask your agent to explain a failed check before repeating work or expanding the scope.


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