---
slug: enrichment-api-response-schema
title: "B2B Enrichment API Response Schema: Every Field Explained"
description: "What does a B2B enrichment API actually return? Complete field reference for company and person enrichment — with real examples, data types, and how to use each field."
date: "2026-05-04"
author: DataLayer Team
tags: [API Reference, Developer Guide, Data Fields]
readingTime: 12
---

Before you integrate a B2B data API, you want to see the actual data. Not marketing copy about "60M companies" — the real JSON, every field, what it means, and whether it's useful for your use case.

This is that guide. We're documenting every field returned by DataLayer's company and person enrichment APIs, with real example values, data types, null behavior, and practical guidance on how to use each field. No vague descriptions.

---

## Company Enrichment: Full Response Example

Request:
```bash
curl -X POST https://api.datalayer.sh/v1/enrich/company \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "stripe.com"}'
```

Response (real data, some values illustrative):
```json
{
  "id": "org_2k8xPqR7mN3j",
  "name": "Stripe",
  "domain": "stripe.com",
  "website": "https://stripe.com",
  "is_website_working": true,
  "linkedin_url": "https://www.linkedin.com/company/stripe",
  "employee_count_range": "10001+",
  "industry_linkedin": "Financial Services",
  "headquarters_city": "South San Francisco",
  "headquarters_state": "California",
  "headquarters_country_code": "US",
  "founded": 2010,
  "revenue_range": "$1B+",
  "about_us": "Stripe is a technology company that builds economic infrastructure for the internet...",
  "specialties": ["Payments", "APIs", "E-commerce", "SaaS", "Fintech"],
  "total_funding_amount": 2200000000,
  "last_funding_date": "2021-03-14",
  "last_funding_type": "Series I",
  "lead_investors": ["Andreessen Horowitz", "Sequoia Capital", "General Catalyst"],
  "employee_on_linkedin_growth_rate": 8.2,
  "has_mobile_app": true,
  "has_web_app": true,
  "monthly_google_adspend": 450000,
  "monthly_organic_traffic": 8200000,
  "total_monthly_traffic": 12500000,
  "crm_tech": "Salesforce",
  "cms_tech": "Contentful",
  "cloud_provider_tech": "AWS",
  "development_tech": ["React", "Ruby on Rails", "Go", "Kubernetes"],
  "analytics_tech": "Amplitude",
  "marketing_automation_tech": "Marketo",
  "sales_automation_tech": "Outreach",
  "ecommerce_tech": null,
  "engineering_open_roles_count": 43,
  "sales_open_roles_count": 28,
  "marketing_open_roles_count": 15,
  "product_open_roles_count": 19,
  "operations_open_roles_count": 12,
  "data_open_roles_count": 8,
  "engineering_role_count": 4200,
  "sales_role_count": 890,
  "marketing_role_count": 320,
  "product_role_count": 410,
  "updated_at": "2026-03-28T00:00:00Z"
}
```

---

## Company Enrichment Fields Reference

### Core Identity

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `id` | string | `"org_2k8xPqR7mN3j"` | DataLayer's internal company identifier. Stable across updates. |
| `name` | string | `"Stripe"` | Official company name as registered on LinkedIn/company website. |
| `domain` | string | `"stripe.com"` | Primary web domain. This is the canonical key for a company record. |
| `website` | string | `"https://stripe.com"` | Full website URL including protocol. May differ from domain (subdomains, redirects). |
| `is_website_working` | boolean | `true` | Whether the domain resolves to a working website. `false` can indicate defunct company or domain change. |
| `linkedin_url` | string | `"https://linkedin.com/company/stripe"` | LinkedIn company page URL. Use for deeplinks and LinkedIn automation. |

### Size and Financials

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `employee_count_range` | string | `"10001+"` | LinkedIn-reported headcount range. Values: `"1-10"`, `"11-50"`, `"51-200"`, `"201-500"`, `"501-1000"`, `"1001-5000"`, `"5001-10000"`, `"10001+"`. Note: this is LinkedIn's self-reported range, not a precise number. |
| `revenue_range` | string | `"$1B+"` | Estimated annual revenue range. Values: `"<$1M"`, `"$1M-$10M"`, `"$10M-$50M"`, `"$50M-$200M"`, `"$200M-$1B"`, `"$1B+"`. Derived from funding, headcount, and industry benchmarks. |
| `founded` | integer | `2010` | Year the company was founded. Null if not available. |
| `total_funding_amount` | integer | `2200000000` | Total funding raised in USD. Null if not funded or data not available. |
| `last_funding_date` | string | `"2021-03-14"` | ISO 8601 date of most recent funding round. |
| `last_funding_type` | string | `"Series I"` | Most recent funding round type. Common values: `"Seed"`, `"Series A"` through `"Series J"`, `"IPO"`, `"Acquired"`, `"Grant"`. |
| `lead_investors` | array | `["a16z", "Sequoia"]` | Names of lead investors in the most recent round. |

### Location

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `headquarters_city` | string | `"South San Francisco"` | HQ city. May differ from mailing address for large companies. |
| `headquarters_state` | string | `"California"` | HQ state (US) or province. Full name, not abbreviation. |
| `headquarters_country_code` | string | `"US"` | ISO 3166-1 alpha-2 country code. `"US"`, `"GB"`, `"DE"`, etc. |

### Description and Category

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `industry_linkedin` | string | `"Financial Services"` | Industry category from LinkedIn's taxonomy. Use for ICP filtering. Common values: "Computer Software", "Information Technology and Services", "Internet", "Financial Services", "Healthcare", "Marketing and Advertising". |
| `about_us` | string | `"Stripe is a technology..."` | Company description text. Useful for AI summarization, context in outreach personalization. Can be long (500-2000 chars). |
| `specialties` | array | `["Payments", "APIs"]` | Company-listed specialties from their LinkedIn page. Useful as keyword signals for GTM targeting. |

### Tech Stack (Technographics)

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `crm_tech` | string | `"Salesforce"` | Primary CRM. Values include: `"Salesforce"`, `"HubSpot"`, `"Dynamics 365"`, `"Pipedrive"`, `"Zoho"`. |
| `cms_tech` | string | `"Contentful"` | Content management system. |
| `cloud_provider_tech` | string | `"AWS"` | Primary cloud provider: `"AWS"`, `"Google Cloud"`, `"Azure"`, `"Multi-cloud"`. |
| `development_tech` | array | `["React", "Go"]` | Engineering tech stack — languages, frameworks, infrastructure tools. |
| `analytics_tech` | string | `"Amplitude"` | Product/web analytics tool. |
| `marketing_automation_tech` | string | `"Marketo"` | Marketing automation platform. |
| `sales_automation_tech` | string | `"Outreach"` | Sales engagement platform. |
| `ecommerce_tech` | string or null | `null` | E-commerce platform if applicable. |

**Using tech signals for GTM:** These fields are ideal for defining TAM and building target lists. If you sell a Salesforce integration, filter `crm_tech = "Salesforce"`. If you compete with HubSpot, filter `crm_tech = "HubSpot"` to find churn candidates.

### Growth and Engagement Signals

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `employee_on_linkedin_growth_rate` | float | `8.2` | Percentage change in LinkedIn headcount over the trailing 12 months. Positive = growing, negative = shrinking. |
| `monthly_google_adspend` | integer | `450000` | Estimated monthly Google Ads spend in USD. Useful as a growth signal — companies spending heavily on paid search are in acquisition mode. |
| `monthly_organic_traffic` | integer | `8200000` | Estimated monthly organic (SEO) visits. |
| `total_monthly_traffic` | integer | `12500000` | Total estimated monthly web traffic (organic + paid + direct). |
| `has_mobile_app` | boolean | `true` | Whether the company has a mobile app (iOS/Android). |
| `has_web_app` | boolean | `true` | Whether the company has a web application (not just marketing site). |

### Open Roles by Department

These fields count the number of currently open job listings in each department — a strong growth-area signal:

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `engineering_open_roles_count` | integer | `43` | Open engineering/technical roles |
| `sales_open_roles_count` | integer | `28` | Open sales roles |
| `marketing_open_roles_count` | integer | `15` | Open marketing roles |
| `product_open_roles_count` | integer | `19` | Open product management roles |
| `operations_open_roles_count` | integer | `12` | Open operations/ops roles |
| `data_open_roles_count` | integer | `8` | Open data/analytics roles |

**Interpretation:** A company with 28 open sales roles and 8% headcount growth is actively scaling their revenue team. That's a high-intent signal for sales tools, sales training, and RevOps solutions.

### Headcount by Function

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `engineering_role_count` | integer | `4200` | Current LinkedIn-reported engineering headcount |
| `sales_role_count` | integer | `890` | Current sales team headcount |
| `marketing_role_count` | integer | `320` | Current marketing headcount |
| `product_role_count` | integer | `410` | Current product team headcount |

**Ratio analysis:** `sales_role_count / engineering_role_count` gives you a sales-to-engineering ratio. A high ratio suggests a sales-led company; low ratio suggests product-led or engineering-first culture. Useful for tailoring messaging.

### Metadata

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `updated_at` | string | `"2026-03-28T00:00:00Z"` | ISO 8601 timestamp of when this record was last refreshed in DataLayer's database. |

---

## Person Enrichment: Full Response Example

Request:
```bash
curl -X POST https://api.datalayer.sh/v1/enrich/person \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@hubspot.com"}'
```

Response:
```json
{
  "id": "per_7nRqX2sK9mL4p",
  "full_name": "Jane Smith",
  "email_address": "jane@hubspot.com",
  "email_status": "verified",
  "cellphone": null,
  "direct_phone": "+16175551234",
  "job_title": "VP of Marketing",
  "job_level": "VP",
  "job_function": "Marketing",
  "job_is_current": true,
  "company_name": "HubSpot",
  "persona": "Marketing Leader",
  "linkedin_url": "https://www.linkedin.com/in/janesmith",
  "skills": ["B2B Marketing", "Demand Generation", "Content Marketing", "HubSpot", "Marketo"],
  "city": "Boston",
  "state": "Massachusetts",
  "country_code": "US",
  "updated_at": "2026-02-14T00:00:00Z"
}
```

---

## Person Enrichment Fields Reference

### Identity

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `id` | string | `"per_7nRqX2sK9mL4p"` | DataLayer's internal person identifier. Stable across updates. |
| `full_name` | string | `"Jane Smith"` | Full professional name as it appears on LinkedIn. |
| `email_address` | string | `"jane@hubspot.com"` | Primary work email address. This is the lookup key. |
| `email_status` | string | `"verified"` | Email deliverability status. See detailed breakdown below. |

### Contact Details

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `cellphone` | string or null | `null` | Mobile/cell phone number in E.164 format (`+15555551234`). Available for ~15-25% of contacts. |
| `direct_phone` | string or null | `"+16175551234"` | Direct work phone number. Higher availability than cellphone. |

**Phone availability note:** Direct phones are available for roughly 30-40% of senior-level contacts (VP+). Cell phones are less common in the database as they're derived from professional records. Do not assume phone will be populated.

### Professional Role

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `job_title` | string | `"VP of Marketing"` | Current job title as listed on LinkedIn. |
| `job_level` | string | `"VP"` | Normalized seniority level. Values: `"C-Suite"`, `"VP"`, `"Director"`, `"Manager"`, `"Senior IC"`, `"IC"`, `"Entry"`. Use this for filtering, not `job_title` (titles vary wildly by company). |
| `job_function` | string | `"Marketing"` | Department/function. Values: `"Engineering"`, `"Sales"`, `"Marketing"`, `"Product"`, `"Operations"`, `"Finance"`, `"HR"`, `"Legal"`, `"Data"`, `"Design"`, `"Executive"`. |
| `job_is_current` | boolean | `true` | Whether this role is the person's current active position. `false` means the most recent known role may be outdated — the person may have changed jobs. |
| `company_name` | string | `"HubSpot"` | Employer company name for this role. |

### Persona and Classification

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `persona` | string | `"Marketing Leader"` | DataLayer's classification of this person's buyer persona. Values vary by function and level. Examples: `"Engineering Manager"`, `"Sales Leader"`, `"Technical Founder"`, `"Head of Data"`. Useful for message personalization. |
| `skills` | array | `["Demand Gen", "HubSpot"]` | LinkedIn-listed skills. Useful for understanding persona depth and tech familiarity. |

### Location

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `city` | string | `"Boston"` | Current city (work or home location as listed on LinkedIn). |
| `state` | string | `"Massachusetts"` | State or province. Full name for US states. |
| `country_code` | string | `"US"` | ISO 3166-1 alpha-2 country code. |

### Social

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `linkedin_url` | string | `"https://linkedin.com/in/janesmith"` | LinkedIn profile URL. Use for connection requests, LinkedIn Ads custom audiences, or manual research. |

### Metadata

| Field | Type | Example | Description |
|-------|------|---------|-------------|
| `updated_at` | string | `"2026-02-14T00:00:00Z"` | ISO 8601 timestamp of last record refresh. |

---

## The `email_status` Field in Detail

This is one of the most important fields for outreach operations. Here's exactly what each value means:

| Status | Definition | Recommended Action |
|--------|-----------|-------------------|
| `verified` | Email passed real-time SMTP check — the mailbox is confirmed open and accepting messages | Send without hesitation |
| `likely` | High-confidence inference based on: known email pattern for this company, domain health checks, LinkedIn activity | Send — bounce rate is <3% in practice |
| `risky` | Valid format but deliverability uncertain. Common causes: catch-all domain (server accepts everything), dormant inbox (hasn't sent/received in 90+ days), recently changed jobs | Run through NeverBounce/ZeroBounce before bulk sends |
| `invalid` | Will bounce. Causes: typo in email, domain doesn't accept mail, mailbox explicitly confirmed non-existent | Do not send |

**Practical guidance for cold email:**
- Include `verified` and `likely` in your sends
- Remove `invalid` always
- Handle `risky` separately — either accept a higher bounce rate or run a second validation step

For email campaigns where domain reputation matters (you're sending from your primary domain), use `verified` and `likely` only. For highly targeted 1:1 outreach (you'd send the email manually anyway), `risky` is usually fine.

---

## Null Values vs. Missing Fields

DataLayer's schema is consistent: every field in the response always appears, but may be `null` if data isn't available for that specific company or person.

```json
{
  "cellphone": null,
  "direct_phone": "+16175551234",
  "total_funding_amount": null,
  "crm_tech": "Salesforce"
}
```

A `null` field means: "We know this company/person exists, but we don't have data for this field."

A 404 HTTP status means: "We have no record matching your query." On a 404, you are not charged credits.

This distinction matters for database design. Always use nullable columns for DataLayer fields — do not use empty strings or default values.

---

## Mapping DataLayer Fields to CRM Properties

### HubSpot Contact Properties

| DataLayer Field | HubSpot Property Name |
|----------------|----------------------|
| `full_name` | `firstname` + `lastname` (split) |
| `email_address` | `email` |
| `job_title` | `jobtitle` |
| `company_name` | `company` |
| `direct_phone` | `phone` |
| `cellphone` | `mobilephone` |
| `city` | `city` |
| `state` | `state` |
| `country_code` | `country` |
| `linkedin_url` | `hs_linkedin_url` |
| `job_level` | Custom property: `job_level` |
| `job_function` | Custom property: `job_function` |
| `persona` | Custom property: `datalayer_persona` |

### HubSpot Company Properties

| DataLayer Field | HubSpot Property Name |
|----------------|----------------------|
| `name` | `name` |
| `domain` | `domain` |
| `employee_count_range` | `numberofemployees` (map range to midpoint) |
| `revenue_range` | `annualrevenue` (map range to midpoint) |
| `industry_linkedin` | `industry` |
| `headquarters_city` | `city` |
| `headquarters_state` | `state` |
| `headquarters_country_code` | `country` |
| `linkedin_url` | `linkedin_company_page` |
| `total_funding_amount` | Custom: `total_funding` |
| `crm_tech` | Custom: `crm_tech` |

### Salesforce Contact Fields

| DataLayer Field | Salesforce API Name |
|----------------|---------------------|
| `full_name` | `FirstName` + `LastName` |
| `email_address` | `Email` |
| `job_title` | `Title` |
| `company_name` | `AccountId` (via lookup) |
| `direct_phone` | `Phone` |
| `cellphone` | `MobilePhone` |
| `city` | `MailingCity` |
| `state` | `MailingState` |
| `country_code` | `MailingCountry` |

---

## Data Freshness

DataLayer's database is updated continuously. Most enterprise company records (Fortune 5000, venture-backed companies) are refreshed on a **30–60 day cycle**. Person records are refreshed when there's a detected change (new job, new LinkedIn activity) or on a **60–90 day cycle** for active contacts.

The `updated_at` field tells you when a specific record was last refreshed. Use this to prioritize re-enrichment:

```python
from datetime import datetime, timedelta, timezone

def needs_refresh(updated_at_str: str, max_age_days: int = 90) -> bool:
    if not updated_at_str:
        return True
    updated = datetime.fromisoformat(updated_at_str.replace("Z", "+00:00"))
    age = datetime.now(timezone.utc) - updated
    return age > timedelta(days=max_age_days)
```

---

## Comparison: DataLayer vs Apollo vs PDL Response Schemas

| Field | DataLayer | Apollo | People Data Labs |
|-------|-----------|--------|-----------------|
| Email status | `email_status` (4 values) | `email_status` (similar) | `work_email` (no status field) |
| Job level (normalized) | `job_level` | `seniority` | `job_title_levels` (array) |
| Persona classification | `persona` | No | No |
| Tech stack | `crm_tech`, `cms_tech`, etc. | Limited | No |
| Funding data | `total_funding_amount`, `last_funding_type` | `funding_events` (array) | `inferred_revenue` |
| Headcount by function | `*_role_count` fields | No | No |
| Growth rate | `employee_on_linkedin_growth_rate` | No | No |
| Intent signals | Via `find_intent_signals` endpoint | No | No |
| Response latency | <50ms | ~200ms | ~300ms |

Key structural differences:
- Apollo returns a richer array structure for some fields (multiple past jobs); DataLayer returns only the current/most recent role with `job_is_current`
- PDL has stronger international coverage in some regions; DataLayer has stronger signal/intent data
- DataLayer's tech stack data (8 fields covering CRM, CMS, cloud, etc.) is purpose-built for GTM targeting and more consistent than scraping raw tech tags

---

## Python: Flatten Response and Insert into Postgres

```python
import requests
import psycopg2
import json

def enrich_and_store(email: str, api_key: str, conn):
    resp = requests.post(
        "https://api.datalayer.sh/v1/enrich/person",
        headers={"X-API-Key": api_key},
        json={"email": email}
    )
    
    if resp.status_code == 404:
        return None
    
    p = resp.json()
    
    with conn.cursor() as cur:
        cur.execute("""
            INSERT INTO contacts (
                datalayer_id, email, full_name, email_status,
                job_title, job_level, job_function, job_is_current,
                company_name, persona, linkedin_url,
                city, state, country_code,
                direct_phone, cellphone, skills,
                updated_at, raw_profile
            ) VALUES (
                %s, %s, %s, %s,
                %s, %s, %s, %s,
                %s, %s, %s,
                %s, %s, %s,
                %s, %s, %s,
                %s, %s
            )
            ON CONFLICT (email) DO UPDATE SET
                full_name = EXCLUDED.full_name,
                job_title = EXCLUDED.job_title,
                job_level = EXCLUDED.job_level,
                company_name = EXCLUDED.company_name,
                email_status = EXCLUDED.email_status,
                updated_at = EXCLUDED.updated_at,
                raw_profile = EXCLUDED.raw_profile
        """, (
            p.get("id"),
            p.get("email_address"),
            p.get("full_name"),
            p.get("email_status"),
            p.get("job_title"),
            p.get("job_level"),
            p.get("job_function"),
            p.get("job_is_current"),
            p.get("company_name"),
            p.get("persona"),
            p.get("linkedin_url"),
            p.get("city"),
            p.get("state"),
            p.get("country_code"),
            p.get("direct_phone"),
            p.get("cellphone"),
            json.dumps(p.get("skills", [])),
            p.get("updated_at"),
            json.dumps(p)
        ))
    conn.commit()
    return p
```

The `raw_profile JSONB` column is useful for querying fields you haven't explicitly mapped:

```sql
-- Find all VPs in San Francisco with Salesforce experience
SELECT email, full_name, job_title, company_name
FROM contacts
WHERE job_level = 'VP'
  AND city = 'San Francisco'
  AND raw_profile->'skills' ? 'Salesforce';
```

---

## FAQ

**Q: Why does the API return `null` for some fields on well-known companies?**

Some fields require specific data collection that may not be available for every company. `ecommerce_tech` will be null for SaaS companies that don't sell direct-to-consumer. `total_funding_amount` will be null for bootstrapped or public companies where funding rounds aren't tracked the same way.

**Q: Are phone numbers mobile or work phones?**

`direct_phone` is the person's work direct line. `cellphone` is their mobile number. Both are professional numbers sourced from business records — not personal phones.

**Q: What's the difference between `employee_count_range` and `engineering_role_count`?**

`employee_count_range` is the company's total headcount (LinkedIn self-reported, bucketed into ranges). `engineering_role_count` is the number of people currently listed in engineering roles on LinkedIn — this is more precise but only covers LinkedIn-visible employees. Use the role count fields for ratio analysis; use `employee_count_range` for size segmentation.

**Q: Can I query the API by LinkedIn URL instead of domain or email?**

For company enrichment, pass the LinkedIn URL in the request body: `{"linkedin_url": "https://linkedin.com/company/stripe"}`. For person enrichment, pass it as `{"linkedin_url": "https://linkedin.com/in/username"}`. Domain and email are preferred (faster lookup), but LinkedIn URL works as a fallback.

**Q: How do I get a company's full list of technologies beyond the categorized fields?**

Use the `company_technographics` endpoint (`GET /v1/companies/:domain/technographics`) for the complete tech stack, including individual tools and vendors not covered by the categorized fields in the standard enrichment response.

---

## Get Started

Start with 10 free credits — no credit card required:

1. [Sign up at datalayer.sh](https://datalayer.sh)
2. Grab your API key from the dashboard
3. Test with a domain you know: `curl -X POST https://api.datalayer.sh/v1/enrich/company -H "X-API-Key: YOUR_KEY" -d '{"domain":"stripe.com"}'`

See the full interactive API documentation at [api.datalayer.sh/docs](https://api.datalayer.sh/docs).
