# Search

> Search graph8's global B2B database for contacts and companies with advanced filters

Source: https://docs.graph8.com/developers/search/

> **Tip: Using the SDK or CLI?**
> The [JavaScript SDK](https://docs.graph8.com/developers/sdk/#enrichment) wraps search as `g8.enrich.search(filters)`. The [CLI](https://docs.graph8.com/developers/cli/#g8-find-contacts) supports `g8 find-contacts --filters '...'` and `g8 build-list`.

> **Note: Free on both plans**
> Search endpoints are **free on both PAYG and Platform within a 50 rps cap** — they hit graph8's owned indexes (700M+ contacts / 100M+ companies) with no third-party COGS, so there is no monthly cap on either plan. Save-to-list is also free. Credits only deduct when you later run waterfall enrichment, AI generation, voice minutes, meeting bookings, or outreach sends on the saved records. See [Pricing](https://docs.graph8.com/developers/pricing/) for the full matrix.

The Search endpoints query graph8's **open data index** — a global B2B database of millions of contacts and companies aggregated from multiple data providers. Use it to discover new prospects that match your ideal customer profile (ICP), then save them to your workspace.

This is different from the [`/contacts`](https://docs.graph8.com/developers/contacts/) and [`/companies`](https://docs.graph8.com/developers/companies/) endpoints, which only return data you've already saved — your first-party CRM data.

> **Note: Organization deliverability policy**
> Open-data searches and save-to-list operations automatically apply your organization's default-on deliverability protection policy. The policy can exclude account categories associated with higher spam-report risk. Users with settings access can inspect or change it under **Settings → Compliance → Deliverability**; it is not a request-body filter and cannot be overridden by an API call.

> **Tip: Which endpoint should I use?**
> - **I want to find NEW contacts or companies I don't have yet** → Use Search (this page)
> - **I want to query contacts or companies I've already saved** → Use [Contacts](https://docs.graph8.com/developers/contacts/) or [Companies](https://docs.graph8.com/developers/companies/) (free, no credits)
> - **I know a specific person's email or LinkedIn and want their profile** → Use [Enrichment Lookup](https://docs.graph8.com/developers/enrichment/) (single-record lookup)

### What you can search for

- **Contacts**: Filter by job title, seniority, department, skills, location, company attributes, and more (~28 filterable fields)
- **Companies**: Filter by industry, employee count, revenue, geography, founded year, LinkedIn presence, and more (~15 filterable fields)

### How it connects to your data

Search results come from the open data index and are **read-only**. To work with them in your workflows:

1. **Search** — find contacts or companies matching your criteria
2. **Save to list** — use `/search/contacts/save` or `/search/companies/save` to copy matching records into your workspace
3. **Manage** — once saved, they appear in [`/contacts`](https://docs.graph8.com/developers/contacts/) and [`/companies`](https://docs.graph8.com/developers/companies/) as your data

> **Note:**
> Pricing depends on your plan - see the [credit costs](#credit-costs) section below.

---

## Credit Costs

| Operation | PAYG | Platform |
|-----------|------|----------|
| Contact search | **Free** (within 50 rps) | **Free** (within 50 rps) |
| Company search | **Free** (within 50 rps) | **Free** (within 50 rps) |
| Save to list | **Free** (within 50 rps) | **Free** (within 50 rps) |

Search and save-to-list are part of the free graph8-owned data tier on **both plans** — the only ceiling is the [50 rps rate cap](https://docs.graph8.com/developers/rate-limits/) and there is no monthly volume cap. Credits only deduct when you act on the saved records: waterfall enrichment via 3rd-party providers, AI generation, message sends, voice minutes, and meeting bookings.

> **Tip: Iterate fast**
> Paginate freely and refine filters without burning credits — save-to-list is also free on both plans. Credits only deduct when you run waterfall enrichment (`g8 enrich`), generate AI content, send outreach, dial, or book meetings on the saved records.

---

## Filter Operators

All search endpoints accept a `filters` array in the request body. Each filter has a `field`, `operator`, and `value`.

| Operator | Description | Example Value |
|----------|-------------|---------------|
| `any_of` | Exact match — field matches any value in the list | `["CEO", "CTO"]` |
| `contains` | Fuzzy/substring match | `["sales"]` |
| `all_of` | Field must contain ALL values | `["python", "javascript"]` |
| `none_of` | Field must NOT match any value | `["intern", "student"]` |
| `is_empty` | Field has no value | `[]` |
| `is_not_empty` | Field has a value | `[]` |
| `between` | Numeric range (inclusive) | `[2015, 2025]` |
| `exists` | Field exists in the record | `[]` |

---

## Search Contacts

`POST /search/contacts`

Search the open data index for contacts matching the given filters. Returns paginated results with up to 100 results per page.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `filters` | array | **Yes** | Filter conditions (AND logic). At least one filter is required. See [filter operators](#filter-operators) |
| `page` | integer | No | Page number, 1-indexed (default: 1, max: 100) |
| `limit` | integer | No | Results per page (default: 25, max: 100) |

> **Note:**
> Pagination is limited to 10,000 total results (`page × limit ≤ 10,000`). For larger result sets, use the [save to list](#save-contacts-to-list) endpoint.

### Filterable Contact Fields

| Field | Description |
|-------|-------------|
| `first_name` | First name |
| `last_name` | Last name |
| `work_email` | Work email address |
| `personal_emails` | Personal email addresses |
| `direct_phone` | Direct phone number |
| `mobile_phone` | Mobile phone number |
| `job_title` | Job title |
| `job_department` | Department |
| `seniority_level` | Seniority level (e.g., "VP", "Director", "Manager") |
| `role` | Role |
| `linkedin_url` | LinkedIn profile URL |
| `gender` | Gender |
| `skills` | Skills |
| `education_degree` | Education degree |
| `education_field` | Field of study |
| `education_university_name` | University name |
| `city` | City |
| `state` | State/province |
| `country` | Country |
| `confidence_score` | Data confidence score |
| `company_name` | Company name |
| `company_domain` | Company domain |
| `company_industry` | Company industry |
| `company_employee_count` | Company employee count |
| `company_country` | Company country |
| `company_state` | Company state |
| `company_city` | Company city |
| `company_revenue` | Company revenue |
| `company_founded_year` | Company founded year |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/search/contacts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {"field": "job_title", "operator": "any_of", "value": ["CEO", "CTO", "VP Engineering"]},
      {"field": "company_industry", "operator": "contains", "value": ["Technology"]},
      {"field": "company_employee_count", "operator": "between", "value": [50, 500]}
    ],
    "page": 1,
    "limit": 25
  }'
```

**Python**

```python
response = requests.post(
    f"{BASE_URL}/search/contacts",
    headers=HEADERS,
    json={
        "filters": [
            {"field": "job_title", "operator": "any_of", "value": ["CEO", "CTO", "VP Engineering"]},
            {"field": "company_industry", "operator": "contains", "value": ["Technology"]},
            {"field": "company_employee_count", "operator": "between", "value": [50, 500]}
        ],
        "page": 1,
        "limit": 25
    }
)
```

**TypeScript**

```typescript
const response = await fetch(`${BASE_URL}/search/contacts`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    filters: [
      { field: "job_title", operator: "any_of", value: ["CEO", "CTO", "VP Engineering"] },
      { field: "company_industry", operator: "contains", value: ["Technology"] },
      { field: "company_employee_count", operator: "between", value: [50, 500] }
    ],
    page: 1,
    limit: 25
  }),
});
```

### Response

```json
{
  "data": [
    {
      "first_name": "Jane",
      "last_name": "Doe",
      "middle_name": null,
      "work_email": "jane@acme.com",
      "personal_emails": null,
      "direct_phone": "+1-555-0100",
      "mobile_phone": null,
      "job_title": "CEO",
      "job_department": "Executive",
      "seniority_level": "C-Suite",
      "role": "Chief Executive Officer",
      "linkedin_url": "https://linkedin.com/in/janedoe",
      "linkedin_headline": "CEO at Acme Inc",
      "city": "San Francisco",
      "state": "California",
      "country": "United States",
      "company_name": "Acme Inc",
      "company_domain": "acme.com",
      "company_industry": "Technology",
      "company_employee_count": "200",
      "company_country": "United States",
      "confidence_score": 95
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 142,
    "has_next": true
  }
}
```

---

## Search Companies

`POST /search/companies`

Search the open data index for companies matching the given filters.

### Request Body

Same structure as contact search. See [request body](#request-body) above.

### Filterable Company Fields

| Field | Description |
|-------|-------------|
| `name` | Company name |
| `domain` | Company domain |
| `website` | Website URL |
| `description` | Company description |
| `industry` | Industry |
| `industry_group` | Industry group |
| `employee_count` | Number of employees |
| `revenue` | Annual revenue |
| `founded_year` | Year founded |
| `country` | Country |
| `state` | State/province |
| `city` | City |
| `phone` | Phone number |
| `linkedin_url` | LinkedIn URL |
| `linkedin_followers` | LinkedIn follower count |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/search/companies" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {"field": "industry", "operator": "contains", "value": ["Banking"]},
      {"field": "employee_count", "operator": "between", "value": [100, 1000]},
      {"field": "country", "operator": "any_of", "value": ["United States"]}
    ],
    "page": 1,
    "limit": 50
  }'
```

**Python**

```python
response = requests.post(
    f"{BASE_URL}/search/companies",
    headers=HEADERS,
    json={
        "filters": [
            {"field": "industry", "operator": "contains", "value": ["Banking"]},
            {"field": "employee_count", "operator": "between", "value": [100, 1000]},
            {"field": "country", "operator": "any_of", "value": ["United States"]}
        ],
        "page": 1,
        "limit": 50
    }
)
```

**TypeScript**

```typescript
const response = await fetch(`${BASE_URL}/search/companies`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    filters: [
      { field: "industry", operator: "contains", value: ["Banking"] },
      { field: "employee_count", operator: "between", value: [100, 1000] },
      { field: "country", operator: "any_of", value: ["United States"] }
    ],
    page: 1,
    limit: 50
  }),
});
```

### Response

```json
{
  "data": [
    {
      "name": "First National Bank",
      "domain": "fnb.com",
      "website": "https://www.fnb.com",
      "description": "Regional banking institution...",
      "industry": "Banking",
      "industry_group": "Financial Services",
      "employee_count": "450",
      "revenue": "$100M-$500M",
      "founded_year": 1987,
      "phone": "+1-555-0200",
      "address": "100 Main St",
      "city": "Pittsburgh",
      "state": "Pennsylvania",
      "country": "United States",
      "zip": 15222,
      "linkedin_url": "https://linkedin.com/company/fnb",
      "linkedin_followers": "12500",
      "facebook_url": "https://facebook.com/fnb",
      "twitter_url": "https://twitter.com/fnb",
      "crunchbase_url": null,
      "logo_url": "https://logo.clearbit.com/fnb.com"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 89,
    "has_next": true
  }
}
```

---

## Save Contacts to List

`POST /search/contacts/save`

Search contacts and save matching results to a new list in your organization. Results are saved asynchronously. Returns `202 Accepted`.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `filters` | array | **Yes** | Filter conditions (AND logic). At least one filter is required. |
| `list_title` | string | **Yes** | Title for the new list |
| `max_results` | integer | No | Maximum results to save (default: 1,000, max: 10,000) |
| `page` | integer | No | Page number (default: 1; ignored on save, kept for request symmetry) |
| `limit` | integer | No | Results per page (default: 25; ignored on save, kept for request symmetry) |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/search/contacts/save" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {"field": "job_title", "operator": "any_of", "value": ["VP Sales", "Head of Sales"]},
      {"field": "company_industry", "operator": "contains", "value": ["SaaS"]}
    ],
    "list_title": "Sales Leaders in SaaS",
    "max_results": 5000
  }'
```

**Python**

```python
response = requests.post(
    f"{BASE_URL}/search/contacts/save",
    headers=HEADERS,
    json={
        "filters": [
            {"field": "job_title", "operator": "any_of", "value": ["VP Sales", "Head of Sales"]},
            {"field": "company_industry", "operator": "contains", "value": ["SaaS"]}
        ],
        "list_title": "Sales Leaders in SaaS",
        "max_results": 5000
    }
)
list_id = response.json()["data"]["list_id"]
```

**TypeScript**

```typescript
const response = await fetch(`${BASE_URL}/search/contacts/save`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    filters: [
      { field: "job_title", operator: "any_of", value: ["VP Sales", "Head of Sales"] },
      { field: "company_industry", operator: "contains", value: ["SaaS"] }
    ],
    list_title: "Sales Leaders in SaaS",
    max_results: 5000
  }),
});
const listId = (await response.json()).data.list_id;
```

### Response

```json
{
  "data": {
    "list_id": 42,
    "list_title": "Sales Leaders in SaaS",
    "estimated_total": 3200,
    "status": "processing"
  }
}
```

> **Tip: What happens next?**
> Saving moves contacts from graph8's open data index into your workspace. Once processing completes, the saved contacts become **your data** — you can find them via [`GET /contacts`](https://docs.graph8.com/developers/contacts/) with `list_id` filter, manage list membership via [`/lists`](https://docs.graph8.com/developers/lists/), and enrich them via [`/enrichment`](https://docs.graph8.com/developers/enrichment/).

---

## Save Companies to List

`POST /search/companies/save`

Search companies and save matching results to a new list. Returns `202 Accepted`.

### Request Body

Same structure as [save contacts to list](#save-contacts-to-list) — uses company filter fields instead.

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/search/companies/save" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {"field": "industry", "operator": "any_of", "value": ["Financial Services"]},
      {"field": "employee_count", "operator": "between", "value": [500, 5000]}
    ],
    "list_title": "Mid-Market Financial Services",
    "max_results": 2000
  }'
```

**Python**

```python
response = requests.post(
    f"{BASE_URL}/search/companies/save",
    headers=HEADERS,
    json={
        "filters": [
            {"field": "industry", "operator": "any_of", "value": ["Financial Services"]},
            {"field": "employee_count", "operator": "between", "value": [500, 5000]}
        ],
        "list_title": "Mid-Market Financial Services",
        "max_results": 2000
    }
)
```

**TypeScript**

```typescript
const response = await fetch(`${BASE_URL}/search/companies/save`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    filters: [
      { field: "industry", operator: "any_of", value: ["Financial Services"] },
      { field: "employee_count", operator: "between", value: [500, 5000] }
    ],
    list_title: "Mid-Market Financial Services",
    max_results: 2000
  }),
});
```

### Response

```json
{
  "data": {
    "list_id": 43,
    "list_title": "Mid-Market Financial Services",
    "estimated_total": 890,
    "status": "processing"
  }
}
```

> **Tip: What happens next?**
> Saved companies become part of your workspace data and are accessible via [`GET /companies`](https://docs.graph8.com/developers/companies/) and [`/lists`](https://docs.graph8.com/developers/lists/).
