# CompanyData API - Full Documentation > Plain markdown export of https://companydata.com/api-docs/ for LLM ingestion. > Generated by scripts/api-docs/build-llms-full.mjs - do not edit by hand. For a curated index see https://companydata.com/llms.txt --- Source: https://companydata.com/api-docs/ # API Documentation ## Getting started To get started, request an API Key on companydata.com: --- Source: https://companydata.com/api-docs/choosing-a-provider/ # Choosing a company data API Developers evaluating a **company data API** usually ask three things: where the data comes from, how many countries one integration covers, and whether the provider fits their stack (REST, SDK, MCP, CRM). This guide covers common comparison queries such as *“best company data API”* and *Clearbit / Dun & Bradstreet alternatives*, and points to detailed comparisons on CompanyData. ## What is the best company data API? There is no single winner for every use case. The best fit depends on **sourcing**, **coverage**, and **delivery model**: | Need | What to look for | CompanyData | | --- | --- | --- | | KYB / compliance from official registers | Legal name, registration number, status, directors, UBO where published | **375M+** businesses from **730+ official trade registers** (KvK, Companies House, Handelsregister, etc.) | | Global product integration | One REST schema across markets, documented OpenAPI, SDKs | Single API at `app.companydata.com`, [OpenAPI spec](https://companydata.com/openapi.json), [JavaScript SDK](https://companydata.com/api-docs/sdks/javascript/) | | Corporate groups | Parent, subsidiary, and branch relationships | [Corporate structure guide](https://companydata.com/api-docs/use-cases/corporate-structure/) via HQ ID fields and `worldwideHeadquarterID` | | AI assistants (ChatGPT, Claude, Cursor) | Tool access without custom scraping | [Open-source MCP server](https://companydata.com/product/mcp-server/) mapped to search, export, and city lookup | | CRM-only enrichment inside HubSpot | Native app, minimal code | [HubSpot integration](https://companydata.com/api-docs/integrations/hubspot/) | **CompanyData is a strong choice when you need registry-grade B2B records at API speed**, especially for KYB, due diligence, lead lists filtered by SIC/revenue/employees, and mapping corporate hierarchies. Providers that infer firmographics from websites (for example legacy Clearbit-style enrichment) can be faster for US-centric marketing stacks but typically lack registration numbers, audited financials, and worldwide group structure. Start with the [5-minute quickstart](https://companydata.com/api-docs/quickstart/) or the [Company Data API product page](https://companydata.com/company-data-api/) (pricing, FAQ, live widget). ## Clearbit alternative (HubSpot Breeze) After HubSpot acquired Clearbit and folded it into **Breeze Intelligence**, many teams need an **independent enrichment API** that works with Salesforce, custom CRMs, or multi-vendor stacks. | | Clearbit (Breeze) | CompanyData API | | --- | --- | --- | | Primary source | Web and third-party signals | Official trade registers | | Typical strength | Inbound deanonymisation, HubSpot-native workflows | Registration IDs, legal status, directors, group structure | | Coverage emphasis | ~44M companies, US-heavy | **200+ countries**, **375M+** businesses | | Standalone API | Requires HubSpot subscription (2026) | REST API with `x-api-key`; [MCP server](https://companydata.com/product/mcp-server/) for AI tools | **When to pick CompanyData over Clearbit:** you need register-sourced KYB fields, European or global coverage with one schema, corporate hierarchy export, or you are not standardising on HubSpot. Detailed comparison: [Best Clearbit alternatives (2026)](https://companydata.com/b2b-data-provider-comparisons/best-clearbit-alternatives/). ## Dun & Bradstreet (D&B) alternative for developers D&B remains the incumbent for **credit-led** workflows and D-U-N-S numbers. Teams switch when they want **developer-first REST access**, transparent per-record pricing, or **registry-first** data without a long enterprise sales cycle. CompanyData complements or replaces D&B when: - You integrate via **API or MCP** rather than batch files and account managers. - You need **fast autocomplete** (`GET /api/company/search`) plus full export (`GET /api/company/export`). - You want **documented field provenance** from government registers for GDPR/KYB audits. Credit scores and D-U-N-S matching are D&B’s core; CompanyData focuses on **live register records**, firmographics, contacts where published, and **worldwide corporate groups**. Detailed comparison: [Dun & Bradstreet alternatives](https://companydata.com/alternatives/dun-bradstreet/). ## Other common comparisons | Query | Guide | | --- | --- | | Best B2B data API providers (2026) | [B2B data API comparison](https://companydata.com/b2b-data-provider-comparisons/best-b2b-data-api-providers/) | | OpenCorporates API alternative | [OpenCorporates alternatives](https://companydata.com/b2b-data-provider-comparisons/best-opencorporates-alternatives/) | | ZoomInfo / Apollo for outbound | [ZoomInfo alternatives](https://companydata.com/b2b-data-provider-comparisons/best-zoominfo-alternatives/) · [Apollo alternatives](https://companydata.com/b2b-data-provider-comparisons/best-apollo-alternatives/) | | People Data Labs / Coresignal | [PDL alternatives](https://companydata.com/alternatives/people-data-labs/) · [Coresignal alternatives](https://companydata.com/alternatives/coresignal/) | | Full provider index | [B2B data provider alternatives](https://companydata.com/alternatives/) | ## Pricing, trial, and limits Every CompanyData API account includes: - **Free trial:** 7 days, 1,000 lookups and 100 exports (no card required on signup). - **Rate limit:** 50 requests per second (default). - **Monthly quota:** 100,000 requests unless agreed otherwise with support. See [authentication](https://companydata.com/api-docs/authentication/) for headers and error responses. Production pricing is credit-based; high-volume teams can request per-million quotes on the [Company Data API](https://companydata.com/company-data-api/) page. ## Next steps 1. [Get an API key](https://app.companydata.com/signup-api/) and run the [quickstart](https://companydata.com/api-docs/quickstart/). 2. Read [request & export](https://companydata.com/api-docs/request-company/) and [response fields](https://companydata.com/api-docs/returned-fields/). 3. For AI workflows, install the [MCP server](https://companydata.com/product/mcp-server/) and connect Cursor or Claude Desktop. --- Source: https://companydata.com/api-docs/quickstart/ # Quickstart This guide walks you through your first successful `GET /api/company/search` in about five minutes. You need a CompanyData account and an API key. > New accounts include a **{API_DOCS_STATS.trialDays}-day free trial** with **{API_DOCS_STATS.trialSearches} searches and {API_DOCS_STATS.trialCredits} exports** (card at signup). For the rate limit (50 requests/second) and the monthly request, search and credit limits per plan, see [Authentication](https://companydata.com/api-docs/authentication/). Comparing B2B data APIs? See [Choosing a company data API](https://companydata.com/api-docs/choosing-a-provider/) before you integrate. For machine-readable API definitions (OpenAPI 3.0), use the spec at [`/openapi.json`](https://companydata.com/openapi.json). ## 1. Get your API key 1. Create an account and pick a plan (trial included on signup). 2. Open your dashboard and copy your API key. ## 2. Set your API key Store the key in your environment. Every request sends it in the `x-api-key` header (see [Authentication](https://companydata.com/api-docs/authentication/)). ```bash export COMPANYDATA_API_KEY="your_key_here" ``` ```bash COMPANYDATA_API_KEY=your_key_here ``` > Do not commit API keys to git or client-side code. Use environment variables or a secrets manager in production. ## 3. Search companies (first request) This example searches companies in the Netherlands (`countryCode=NL`) matching **Heineken**. ### Recommended: npm SDK Use the official JavaScript/TypeScript package `@companydata/protool-sdk`: ```bash npm install @companydata/protool-sdk ``` ```ts const client = new ApiClient({ baseUrl: 'https://app.companydata.com', auth: { type: 'apiKey', apiKey: process.env.COMPANYDATA_API_KEY ?? '' }, }) const result = await client.company.search({ countryCode: 'NL', search: 'Heineken', page: 1, pageSize: 25, }) console.log(result.data) ``` Or use any HTTP client. See the [JavaScript SDK](https://companydata.com/api-docs/sdks/javascript/) for retries, timeouts, and export helpers. ### cURL / HTTP ```bash curl -G "https://app.companydata.com/api/company/search" \ --data-urlencode "countryCode=NL" \ --data-urlencode "search=Heineken" \ -H "x-api-key: $COMPANYDATA_API_KEY" ``` ```js const apiKey = process.env.COMPANYDATA_API_KEY const params = new URLSearchParams({ countryCode: 'NL', search: 'Heineken', }) const response = await fetch( `https://app.companydata.com/api/company/search?${params}`, { headers: { 'x-api-key': apiKey } }, ) const data = await response.json() console.log(data) ``` ```python api_key = os.environ['COMPANYDATA_API_KEY'] url = 'https://app.companydata.com/api/company/search' headers = {'x-api-key': api_key} params = {'countryCode': 'NL', 'search': 'Heineken'} response = requests.get(url, headers=headers, params=params) if response.status_code == 200: print(response.json()) else: print(f'Error: {response.status_code} - {response.text}') ``` ## 4. Confirm it worked A successful search returns **HTTP 200** with records under `data.records` and page details under `meta`: ```json { "meta": { "totalCount": 3, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] } } ``` Search uses standard pagination (default `pageSize` 25; up to **50 records** total across pages). See [Company search & export](https://companydata.com/api-docs/request-company/) and [Pagination](https://companydata.com/api-docs/pagination/). **Common errors** Every failure is signalled by the HTTP status code; the body is JSON with an `error` string. No failure is ever reported inside a 200 response. - **400 Bad Request** - no query parameters at all. The body reuses the list envelope with `meta.totalCount` = -1 and the reason in `meta.message`. - **401 Unauthorized** - invalid `x-api-key`. Check the key and [Authentication](https://companydata.com/api-docs/authentication/). - **403 Forbidden** - the key is disabled or the subscription is inactive (`subscriptionStatus` names the state). - **405 Method Not Allowed** - only GET is accepted. - **429 Too Many Requests** - a quota is exhausted (monthly searches, request allowance, or the per-second rate limit). The body may add `searchesUsed` / `searchesLimit` or `requestsUsed` / `requestsLimit`. - **500 Internal Server Error** - the search failed; safe to retry with backoff. - **503 Service Unavailable** - no search index is selected on the server, typically during an index switch; retry later. The full list with example bodies is in the [OpenAPI specification](https://companydata.com/openapi.json). ## 5. Next steps --- Source: https://companydata.com/api-docs/authentication/ # Authentication Authenticate every CompanyData API call with an **`x-api-key` header**. The same key powers search, export, and city lookup on `app.companydata.com`. New accounts get a **free trial** (**150 lookups**, no credit card) with **50 requests/second** and **100,000 requests/month** by default. Compare providers in [Choosing a company data API](https://companydata.com/api-docs/choosing-a-provider/). ## API key header ```bash curl -G "https://app.companydata.com/api/company/search" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "countryCode=NL" \ --data-urlencode "search=Heineken" ``` **Success:** HTTP `200` with JSON body (see [Quickstart](https://companydata.com/api-docs/quickstart/) for a full search response example). **Missing or invalid key:** HTTP `401`: ```json { "message": "Unauthorized", "statusCode": 401 } ``` > Never expose API keys in client-side code or public repositories. Use environment variables or a secrets manager in production. ## Monthly request limit Your API key includes **100,000 requests per month** by default. Contact support if you need a higher quota. ## Rate limit The API allows **50 requests per second** per key. Exceeding the limit returns HTTP `429 Too Many Requests`. Retry with exponential backoff. --- Source: https://companydata.com/api-docs/request-company/ # Company API Search and export **400M+ companies** from **Companies House, Handelsregister, KVK**, and **1,200+ other official registers** through two endpoints: **`/api/company/search`** (lightweight lookup, max 50 records) and **`/api/company/export`** (full subscription field set, unlimited via scroll). Filter by country, city, industry (SIC), revenue, employees, director name, corporate group ID (`worldwideHeadquarterID`), and more. Evaluating other vendors? See [Choosing a company data API](https://companydata.com/api-docs/choosing-a-provider/). ## Company Search Endpoint: `/api/company/search` The `/api/company/search` endpoint allows users to search for companies based on various criteria. This endpoint returns a standard set of fields, including the company name and address. ### Returned Fields - **ID**: A unique identifier of the company, can be used to retrieve a full record. - **Company Name**: The main name of the company. - **Country Name**: The country of the company. Note: it's possible to receive more fields on search, this can be requested via support. ### Important Notes - **Purpose**: This endpoint is primarily used to retrieve basic information such as the company name and address based on a search query. For instance in an autocomplete search. - **Filtering branches**: In most cases, you'll want to add `statusCode=0,1` to your query. This filters for single locations (0) and headquarters (1) only. Large companies often have many branches/subsidiaries in the database, which are typically not relevant for most use cases like lead generation or company lookup. ### Example Request ```bash curl -G https://app.companydata.com/api/company/search \ -H "x-api-key: {ApiKey}" \ -d "search=Example Company" ``` **Example response:** ```json { "meta": { "totalCount": 3, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] } } ``` ### Additional Examples #### 1. Search for companies named Heineken ```bash curl -G https://app.companydata.com/api/company/search?page=1&pageSize=25&search=Heineken \ -H "x-api-key: {ApiKey}" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; // Replace with your actual API key const url = 'https://app.companydata.com/api/company/search'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 25, search: 'Heineken' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` #### 2. Search for companies named Heineken in The NETHERLANDS ```bash curl -G https://app.companydata.com/api/company/search?page=1&pageSize=25&search=Heineken&countryCode=NL \ -H "x-api-key: {ApiKey}" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; // Replace with your actual API key const url = 'https://app.companydata.com/api/company/search'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 25, search: 'Heineken', countryCode: 'NL' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` #### 3. Search by street address in Amsterdam ```bash curl -G "https://app.companydata.com/api/company/search?page=1&pageSize=25&countryCode=NL&cityName=AMSTERDAM&streetName=Keizersgracht" \ -H "x-api-key: {ApiKey}" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/search'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 25, countryCode: 'NL', cityName: 'AMSTERDAM', streetName: 'Keizersgracht' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` #### 4. Search by city name ```bash curl -G "https://app.companydata.com/api/company/search?page=1&pageSize=25&countryCode=NL&cityName=ROTTERDAM&statusCode=0,1" \ -H "x-api-key: {ApiKey}" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/search'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 25, countryCode: 'NL', cityName: 'ROTTERDAM', statusCode: '0,1' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` #### 5. Search by postal code and street ```bash curl -G "https://app.companydata.com/api/company/search?page=1&pageSize=25&countryCode=NL&postalCode=1012AB&streetName=Dam" \ -H "x-api-key: {ApiKey}" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/search'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 25, countryCode: 'NL', postalCode: '1012AB', streetName: 'Dam' } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` ## Company Export Endpoint: `/api/company/export` The `/api/company/export` endpoint allows users to request a specific set of fields related to company data. The fields returned by this endpoint depend on the type of package purchased by the client. ### Returned Fields - The set of fields returned is determined by the client's subscription package. This could include detailed company information such as financial data, employee count, or contact information. - For a full list of available fields with types and descriptions, see [Company API Response Fields](https://companydata.com/returned-fields). ### Important Notes - **Package-Dependent Fields**: The fields returned by this endpoint are customized based on the client's purchased package. For more information contact support. - **Use Case**: This endpoint is used for getting a full company profile, often used in CRM's or for the purpose of contacting or researching a company. ### Pagination The `/api/company/export` endpoint supports two pagination methods to retrieve company data: #### 1. Standard Pagination Standard pagination uses `page` and `pageSize` parameters and is suitable for most use cases. **Characteristics:** - Maximum of 10,000 records retrievable - Simple page-based navigation - Best for small to medium datasets - Backwards compatible with existing implementations **Response Structure:** ```json { "meta": { "totalCount": 1250, "currentPage": 1, "totalPages": 13, "pageSize": 100 }, "data": { "records": [ { "ID": "123456789", "Company Name": "Example B.V.", "City": "Amsterdam", "Country": "Netherlands" } ] } } ``` - `data.records` - array of company objects - `meta.totalCount` - total number of matching records across all pages - `meta.currentPage` - current page number - `meta.pageSize` - page size applied to this request - `meta.totalPages` - total number of pages (`ceil(totalCount / pageSize)`) **Example:** ```bash curl -G https://app.companydata.com/api/company/export?page=1&pageSize=100&countryCode=NL \ -H "x-api-key: {ApiKey}" \ -d "export=true" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/export'; axios.get(url, { headers: { 'x-api-key': apiKey }, params: { page: 1, pageSize: 100, countryCode: 'NL', export: true } }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error:', error); }); ``` #### 2. Scroll Pagination Scroll pagination is designed for retrieving large datasets without record limits. It uses a cursor-based approach with a `scrollId` that tracks your position in the result set. **Characteristics:** - **Unlimited records**: No maximum record limit - **Cursor-based**: Uses `scrollId` to track position - **Time-limited context**: Scroll context expires after 8 minutes - **Efficient for large exports**: Ideal for bulk data retrieval **How it works:** 1. **Initial Request**: Set `useScroll=true` to start a scroll session 2. **Subsequent Requests**: Use the `scrollId` from the previous response to get the next batch 3. **Continue until complete**: Keep requesting with the latest `scrollId` until `hasMoreRecords` is `false` 4. **Expiration**: If more than 8 minutes pass between requests, the scroll context expires and you'll need to start a new scroll session **Important Notes:** - **Always use the latest `scrollId`**: Each response includes a new `scrollId` that must be used for the next request - **Scroll context expires after 8 minutes**: If you don't make a request within 8 minutes, the scroll session expires - **Don't mix pagination methods**: Once you start with scroll pagination, continue using `scrollId` until completion - **Backwards compatible**: Standard pagination continues to work unchanged **Example - Initial Scroll Request:** ```bash curl -G https://app.companydata.com/api/company/export?useScroll=true&pageSize=1000&countryCode=NL \ -H "x-api-key: {ApiKey}" \ -d "export=true" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/export'; // Initial scroll request axios.get(url, { headers: { 'x-api-key': apiKey }, params: { useScroll: true, pageSize: 1000, countryCode: 'NL', export: true } }) .then(response => { const { data, scrollId, hasMoreRecords } = response.data; console.log(`Retrieved ${data.length} records`); console.log(`Has more records: ${hasMoreRecords}`); console.log(`Scroll ID: ${scrollId}`); // Store scrollId for next request if (hasMoreRecords) { fetchNextPage(scrollId); } }) .catch(error => { console.error('Error:', error); }); ``` **Example - Subsequent Scroll Requests:** ```bash curl -G https://app.companydata.com/api/company/export?useScroll=true&scrollId=abc123xyz&pageSize=1000 \ -H "x-api-key: {ApiKey}" \ -d "export=true" ``` ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/export'; // Subsequent scroll request function fetchNextPage(scrollId) { axios.get(url, { headers: { 'x-api-key': apiKey }, params: { useScroll: true, scrollId: scrollId, // Use the scrollId from previous response pageSize: 1000, export: true } }) .then(response => { const { data, scrollId: newScrollId, hasMoreRecords } = response.data; console.log(`Retrieved ${data.length} records`); // Process data here... // Continue if more records available if (hasMoreRecords) { fetchNextPage(newScrollId); // Use the NEW scrollId } else { console.log('All records retrieved!'); } }) .catch(error => { console.error('Error:', error); }); } ``` **Example - Complete Scroll Pagination Flow:** ```js const axios = require('axios'); const apiKey = 'YOUR_API_KEY'; const url = 'https://app.companydata.com/api/company/export'; async function exportAllCompanies(filters) { let scrollId = null; let hasMoreRecords = true; let allRecords = []; let pageCount = 0; try { // Initial request let response = await axios.get(url, { headers: { 'x-api-key': apiKey }, params: { useScroll: true, pageSize: 1000, ...filters, export: true } }); while (hasMoreRecords) { const { data, scrollId: newScrollId, hasMoreRecords: more } = response.data; allRecords = allRecords.concat(data); pageCount++; console.log(`Page ${pageCount}: Retrieved ${data.length} records (Total: ${allRecords.length})`); hasMoreRecords = more; scrollId = newScrollId; if (hasMoreRecords) { // Wait a bit to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 100)); // Fetch next page with latest scrollId response = await axios.get(url, { headers: { 'x-api-key': apiKey }, params: { useScroll: true, scrollId: scrollId, // Use latest scrollId pageSize: 1000, ...filters, export: true } }); } } console.log(`Export complete! Total records: ${allRecords.length}`); return allRecords; } catch (error) { console.error('Export error:', error); throw error; } } // Usage exportAllCompanies({ countryCode: 'NL' }) .then(records => { console.log(`Successfully exported ${records.length} companies`); }); ``` **Response Structure for Scroll Pagination:** Scroll pagination uses the same envelope as standard pagination: records stay under `data.records`, and `meta` carries the scroll cursor instead of page numbers. No total count is available in scroll mode. ```json { "meta": { "pageSize": 1000, "hasMoreRecords": true, "scrollId": "abc123xyz456", "recordCount": 1 }, "data": { "records": [ { "ID": "12345", "Company Name": "Example Company" } ] } } ``` **Response Fields:** - `data.records` - array of company records - `meta.scrollId` - cursor for the scroll session; **always use the latest `scrollId` from the most recent response** - `meta.hasMoreRecords` - `true` if more records remain, `false` when all records have been retrieved - `meta.pageSize` - page size applied to this request - `meta.recordCount` - number of records in `data.records` of this response ### Example Request Export the first 25 companies from Rotterdam, the Netherlands with the Sic Code 3221, for glassware. ```bash curl -G https://app.companydata.com/api/company/export?page=1&pageSize=25&sic4Digits=3221&countryCode=NL&cityName=Rotterdam? \ -H "x-api-key: {ApiKey}" \ -d "export=true" ``` ## Query Parameters Below is a list of query parameters that can be used to filter the results returned by this endpoint. Export and search support the same parameters. ### Pagination Parameters | Parameter | Description | Type | Options | Example | |-----------------------------------|-------------------------------------------------------------------------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `page` | Page number for standard pagination (starting from 1). Maximum 10,000 records retrievable with standard pagination. | Integer | Positive integer | `page=1` | | `pageSize` | Number of records per page. Recommended: 100-1000 for standard pagination, 1000+ for scroll pagination. | Integer | Positive integer | `pageSize=100` | | `useScroll` | Enable scroll pagination mode. When `true`, enables unlimited record retrieval using scrollId. Must be used with scrollId for subsequent requests. | Boolean | true, false | `useScroll=true` | | `scrollId` | Scroll session identifier from previous response. Required for subsequent requests in scroll pagination mode. Scroll context expires after 8 minutes. | String | Scroll ID from previous response | `scrollId=abc123xyz456` | ### Filter Parameters | Parameter | Description | Type | Options | Example | |-----------------------------------|-------------------------------------------------------------------------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `ID` | Company ID | String | Company ID | `ID=01111` | | `search` | Search for company name | String | Company Name | `search=Heineken` | | `sic4Digits` | 4-digit SIC Code. | String | A valid 4 number SIC Code | `sic4Digits=0111,0115` | | `importExportCode` | Import/Export Code. | Enum | A: Import/Export/Agent, B: Imports & Exports, C: Imports, D: Imports & Agents, E: Exports & Agents, F: Agent (no inventory), G: Not available, H: Exports | `importExportCode=B,C` | | `legalStatus` | Legal Status of the company. | Enum | 0: Not available, 1: Proprietorship, 2: Partnership, 3: Corporation, 7: Partnership, 8: Joint venture, 9: Master limited partnership, etc. | `legalStatus=1,2` | | `statusCode` | Status Code representing the company's location type. | Enum | 0: Single Location, 1: Headquarter/Parent, 2: Branch | `statusCode=0,2` | | `subsidiaryCode` | Indicates if the company is a subsidiary. | Enum | 0: Not a subsidiary, 3: Is a subsidiary | `subsidiaryCode=3,0` | | `countryCode` | Country Code where the company is located. | String | See countries page for code format | `countryCode=AL,BY,NL` | | `provinceName` | State/Province Name where the company is located. | AutoComplete | N/A | `provinceName=NOORD-HOLLAND` | | `cityName` | City Name where the company is located. | AutoComplete | N/A | `cityName=AMSTERDAM` | | `streetName` | Street address. Supports prefix (partial) matching. | String | Street name or partial street name | `streetName=Keizersgracht` | | `regionName` | Region or state where the company is located. | AutoComplete | N/A | `regionName=BAVARIA` | | `countryName` | Country name where the company is located. | AutoComplete | N/A | `countryName=NETHERLANDS` | | `postalCode` | Postal code of the company. Exact match. | String | Postal code | `postalCode=1012AB` | | `postalCodeInteger` | Postal code as number. Supports range filtering. | Integer | Numeric postal code | `postalCodeInteger[min]=1000&postalCodeInteger[max]=1099` | | `employeesHere` | Number of employees at the company's location. | Integer | Example: 10 | `employeesHere[min]=2&employeesHere[max]=10` | | `employeesTotal` | Total number of employees in the company. | Integer | Example: 100 | `employeesTotal[min]=2&employeesTotal[max]=10` | | `annualSales` | Annual sales in U.S. dollars. | Integer | Example: 100000 | `annualSales[min]=10000&annualSales[max]=500000` | | `foundingYears` | Range of years the company was founded. | Integer | Example: 2022 | `foundingYears[min]=2000&foundingYears[max]=2010` | | `phoneNumber` | Search by full Company's Phone number, internationally formatted with + sign, 00 or local not supported | String | Company Phone Number | `phoneNumber=+31621700000` | | `email` | Search by email or email patterns such as domain or names | String | Company Email | `search=bolddata.nl` `search=info@bolddata.nl` | | `worldwideHeadquarterID` | Filter by Worldwide Headquarter ID | String | Worldwide Headquarter ID | `worldwideHeadquarterID=1234` | | `nationalId` | Search by National Company Registration Number, recommended to add countryCode | String | National Company Registration Number | `nationalId=123456` | | `hasEmail` | Indicates if an email address is available for the company. | Boolean | true, false | `hasEmail=true` | | `hasMarketability` | Indicates if the company is marked as marketable. | Boolean | true, false | `hasMarketability=true` | | `hasPhone` | Indicates if a telephone number is available for the company. | Boolean | true, false | `hasPhone=true` | | `hasFax` | Indicates if a fax number is available for the company. | Boolean | true, false | `hasFax=true` | | `hasContactPerson` | Indicates if a contact person is available for the company. | Boolean | true, false | `hasContactPerson=true` | | `hasWebsite` | Indicates if a website is available for the company. | Boolean | true, false | `hasWebsite=true` | | `hasNationalID` | Indicates if a national ID is available for the company. | Boolean | true, false | `hasNationalID=true` | | `hasBusinessName` | Indicates if a business name is available for the company | Boolean | true, false | `hasBusinessName=true` | | `hasStreetAddress` | Indicates if a street address is available for the company. | Boolean | true, false | `hasStreetAddress=true` | | `hasPostalCode` | Indicates if a postal code is available for the company. | Boolean | true, false | `hasPostalCode=true` | | `hasCEOName` | Indicates if a CEO name is available in the database for the company record. | Boolean | true, false | `hasCEOName=true` | ## Address Search The `streetName` parameter allows searching companies by street address. It supports partial matching, so you don't need to type the full street name. ### Address Parameters | Parameter | Description | Match Type | |-----------|-------------|------------| | `streetName` | Street address | Prefix (partial match) | | `cityName` | City name | Exact | | `provinceName` | Province or district | Exact | | `regionName` | Region or state | Exact | | `countryName` | Country name | Exact | | `postalCode` | Postal code | Exact | | `postalCodeInteger` | Postal code as number | Range | | `countryCode` | ISO country code | Exact | ### How streetName works - Matches from the beginning of words in the address - Case insensitive - You can type a partial street name and it will match | Search value | Matches | Why | |-------------|---------|-----| | `Sumatra` | "Sumatrastraat 109" | Prefix match on street name | | `Keizersgracht 1` | "Keizersgracht 100", "Keizersgracht 12" | Prefix match on number | | `Dam` | "Damrak 1", "Damstraat 5" | Prefix match | ### Location names require exact values Unlike `streetName`, the parameters `cityName`, `provinceName`, `regionName`, and `countryName` require exact values. City names, provinces, and regions do not have international standards or ISO codes, so spelling and formatting can vary between countries and languages (e.g. "Den Haag" vs "'s-Gravenhage", "Munich" vs "Munchen"). To get the correct city value, use the city lookup endpoint to search for the exact name as it exists in our database. ### Lookup Endpoints | Endpoint | Description | |----------|-------------| | `/api/cities` | Look up city names | The city lookup endpoint accepts these parameters: | Parameter | Required | Description | |-----------|----------|-------------| | `search` | Yes | Your search term (partial input is fine) | | `countries` | No | ISO country code to filter results within a country | | `limit` | No | Maximum number of results to return (default: 100) | The API returns a `data` array of matching values. Use these exact values in your search or export requests. #### Lookup Examples **Look up a city name:** ``` GET /api/cities?search=ams&countries=NL&limit=100 → {"data": [{"cityName": "AMSTELVEEN", "countryCode": "NL"}, {"cityName": "AMSTERDAM", "countryCode": "NL"}, ...], "total": 9, "limit": 100} ``` > This lookup endpoint can also be used for building autocomplete inputs in your own UI integration. ### Recommended usage For best results, first look up the exact city name via the lookup endpoint, then combine it with `streetName`. **Step 1 - Find the city name:** ``` GET /api/cities?search=Amster&countries=NL → {"data": [{"cityName": "AMSTERDAM", "countryCode": "NL"}, ...], ...} ``` **Step 2 - Search with exact city + partial street:** ``` GET /api/company/search?countryCode=NL&cityName=AMSTERDAM&streetName=Keizersgracht ``` ### More address search examples **Country + postal code + street:** ``` GET /api/company/search?countryCode=NL&postalCode=1012AB&streetName=Dam ``` **Country + postal code range + street (numeric range):** ``` GET /api/company/search?countryCode=NL&postalCodeInteger[min]=1000&postalCodeInteger[max]=1099&streetName=Dam ``` **Country + street only (broader search):** ``` GET /api/company/search?countryCode=NL&streetName=Sumatra ``` **Province + street + additional filters:** ``` GET /api/company/search?countryCode=DE&provinceName=BAVARIA&streetName=Friedrich&hasEmail=true ``` ### Available on - `GET /api/company/search` - `GET /api/company/export` --- Source: https://companydata.com/api-docs/domain-enrichment/ # Domain Enrichment API Endpoint: `GET /api/website/html` Domain enrichment turns a website or email domain into a company record. The endpoint downloads a public web page and, with `extract=true`, returns the **company candidates** found in it: registration numbers (KVK, Companies House, Handelsregister), VAT numbers, JSON-LD organisation data, copyright and legal-suffix names. Resolve a candidate with [`/api/company/search`](https://companydata.com/api-docs/request-company/) and you have the registered legal entity behind the site, with firmographics, CEO, corporate group and contact data from 350M+ companies in official registers. This is the domain enrichment step that powers [Find the Company Behind Any Website](https://companydata.com/who-owns-this-website/) and the Chrome extension. Typical uses: - **Inbound lead enrichment**: a signup from `info@acme.nl` becomes Acme B.V. with size, industry, city and CEO before sales follows up. - **CRM enrichment by domain**: enrich every company record that only has a website field, in bulk. - **Prospect and account lists**: turn a directory, a competitor's customer logos or a list of domains into a target list with registry-verified firmographics. - **Registry-backed alternative to Clearbit-style enrichment**: the same domain-in, company-out call, sourced from official registers instead of scraped profiles. - **Fallback for the website filter**: when `/api/company/search?website=` returns nothing, the footer or imprint usually still names the operator. - **Your own parsing**: the raw HTML is returned as well, so you can run any extraction you like without hosting a fetcher. ## Request ```bash curl -G "https://app.companydata.com/api/website/html" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=coolblue.nl" \ --data-urlencode "extract=true" ``` ```js const res = await fetch( 'https://app.companydata.com/api/website/html?' + new URLSearchParams({ url: 'coolblue.nl', extract: 'true' }), { headers: { 'x-api-key': 'YOUR_API_KEY' } } ); const page = await res.json(); console.log(page.finalUrl, page.candidates); ``` ```python res = requests.get( "https://app.companydata.com/api/website/html", params={"url": "coolblue.nl", "extract": "true"}, headers={"x-api-key": "YOUR_API_KEY"}, ) page = res.json() print(page["finalUrl"], page["candidates"]) ``` ### Query parameters | Parameter | Required | Description | | --- | --- | --- | | `url` | yes | The page to download. A bare domain (`coolblue.nl`), a host with path (`www.example.com/imprint`) or a full `http(s)://` URL. Bare domains default to `https://`. | | `extract` | no | `true` to include the `candidates` array with company signals found in the page. Default `false`. | | `maxBytes` | no | Cap on the downloaded body in bytes. Default `2000000` (2 MB), minimum `10000`, maximum `5000000`. Larger pages are cut off and flagged with `truncated: true`. | Redirects are followed (up to 5 hops) and `finalUrl` tells you where the page ended up. The body is decoded using the charset declared by the server or the page; `charset` reports which one was used. ## Response ```json { "requestedUrl": "https://coolblue.nl/", "finalUrl": "https://www.coolblue.nl/", "statusCode": 200, "contentType": "text/html; charset=utf-8", "charset": "utf-8", "html": "...", "bytes": 412873, "truncated": false, "redirects": ["https://www.coolblue.nl/"], "fetchedAt": "2026-09-07T10:12:31.204Z", "candidates": [ { "source": "jsonld", "name": "Coolblue B.V.", "countryHint": "NL", "city": "Rotterdam" }, { "source": "identifier", "nationalId": "24330083", "countryHint": "NL" }, { "source": "identifier", "nationalId": "NL812688747B01", "countryHint": "NL" }, { "source": "copyright", "name": "Coolblue", "countryHint": "NL" } ] } ``` ### Response fields - **`requestedUrl`**: the normalised URL that was fetched first. - **`finalUrl`**: the URL after redirects. Use its hostname for the `website` filter or as the domain to cache on. - **`statusCode`**: HTTP status of the final response. A `404` or `503` page is still returned so you can inspect it. - **`contentType`** and **`charset`**: as declared by the server and used for decoding. - **`html`**: the page body as text, up to `maxBytes`. - **`bytes`** and **`truncated`**: how much was downloaded and whether the cap was hit. - **`redirects`**: every redirect target that was followed, in order. - **`candidates`** (only with `extract=true`): company signals in order of trust, deduplicated, at most 12. ### Candidate sources | `source` | What it is | Reliability | | --- | --- | --- | | `jsonld` | `Organization` / `LocalBusiness` from schema.org JSON-LD: `legalName` or `name`, `vatID` or `taxID`, address | High when present | | `identifier` | Registration or VAT number from the footer or imprint: KVK (NL), Company No. (GB), HRA/HRB (DE), EU VAT numbers | Very high: search it with `nationalId=` | | `copyright` | The name after the copyright line | Medium: often a trade name | | `legal-suffix` | Names ending in B.V., GmbH, Ltd, Inc., S.A. and similar found in the footer | Medium to high | | `og` | `og:site_name` or `application-name` meta tag | Low: usually a brand | Each candidate carries a `countryHint` (from the identifier prefix, the JSON-LD address, or the country-code top-level domain) that you should pass as `countryCode` to narrow the search. ## From website to lead 1. Call `/api/website/html?url=&extract=true`. 2. If a candidate has a `nationalId`, search it directly: `/api/company/search?nationalId=&countryCode=`. A hit here is near-certain. 3. Otherwise search the best `name` candidate: `/api/company/search?search=&countryCode=&statusCode=0,1`, and compare the returned `Company Name` and `Trade Name` with the candidate. 4. If nothing matches, fall back to the registry's own website field: `/api/company/search?website=`. 5. Export the match with `/api/company/export?ID=` for the full lead record: phone, email, employees, revenue, industry codes, executives and headquarters fields. > Company websites list the operating company, not necessarily the ultimate owner. Once you have a match, the export record's `Global Headquarter ID Number` gives you the group parent; see [Corporate Structure](https://companydata.com/api-docs/use-cases/corporate-structure/). ## Errors | Status | `code` | Meaning | | --- | --- | --- | | `400` | `invalid_url` | `url` is missing, not http(s), contains credentials, or is not a public hostname. | | `401` | | Missing or invalid API key. | | `403` | `blocked_host` | The host is an IP literal, an internal name, or resolves to a private or reserved address. Only public websites can be fetched. | | `415` | `not_html` | The URL returned a non-HTML document (an image, a PDF, a JSON API). | | `429` | | Monthly quota exceeded for this key. | | `502` | `unreachable`, `too_many_redirects` | The site could not be reached, did not resolve, or redirected more than 5 times. | | `504` | `timeout` | The site did not respond within 10 seconds. | ## Limits and quota - One call counts as one request against your monthly quota, whatever the page size. - Pages are fetched with a `CompanyDataBot` user agent from our servers; sites that block bots may return a challenge page instead of their content (check `statusCode` and the `html`). - Only `http` and `https` URLs on public hosts are fetched. Private networks, cloud metadata addresses and localhost are always refused. - Downloaded HTML is not stored; each call fetches the page live. --- Source: https://companydata.com/api-docs/returned-fields/ # Company API Response Fields Export records include **corporate hierarchy fields** (local, national, and global headquarters) that identify a company's role in its corporate group and link subsidiaries to their parent entities. Use `Global Headquarter ID Number` with the `worldwideHeadquarterID` query filter to retrieve every entity in a group. See the [Corporate Structure use case](https://companydata.com/api-docs/use-cases/corporate-structure/) for a complete walkthrough. ## Response Envelope All API endpoints return JSON with a consistent wrapper: `meta` for pagination details and `data.records` for the records. Only the contents of `meta` differ between standard and scroll pagination: **Standard pagination** (`page` + `pageSize` parameters): ```json { "meta": { "totalCount": 1250, "currentPage": 1, "totalPages": 50, "pageSize": 25 }, "data": { "records": [ /* array of company objects */ ] } } ``` **Scroll pagination** (`useScroll=true`): ```json { "meta": { "pageSize": 1000, "hasMoreRecords": true, "scrollId": "abc123xyz456", "recordCount": 1000 }, "data": { "records": [ /* array of company objects */ ] } } ``` > The record array is always at `data.records`. Standard pagination reports `meta.totalCount`, `meta.currentPage`, `meta.totalPages` and `meta.pageSize`; scroll pagination reports `meta.scrollId`, `meta.hasMoreRecords`, `meta.recordCount` and `meta.pageSize`. No total count is available in scroll mode. ## Example Company Record Below is a representative company object as returned in `data.records`. All headquarters fields are flat; there is no nested `headquarters` object. ```json { "ID": "123456789", "Company Name": "Heineken N.V.", "Trade Name": null, "Address Status": null, "Address 1": "Keizersgracht 424", "Address 2": null, "City": "AMSTERDAM", "State/Province": "NOORD-HOLLAND", "Country": "Netherlands", "Postal Code": "1016 GC", "Company Registration Number": "33011433", "Company Registration Type Code": "KVK", "Country Phone Access Code": "31", "Phone Number": "+31201234567", "Cable Telex": null, "Fax Number": null, "CEO Name": "Jane Doe", "CEO Title": "CEO", "Business Category": "Computer Programming Services", "Business Category Code 1": "7371", "Business Category Code 1 - Description": "Computer Programming, Data Processing", "Founding Year": "2005", "Yearly Revenue Local Currency": "1000000", "Yearly Revenue Indicator": "0", "Yearly Revenue in U.S. Dollars": "1100000", "Currency Code": "EUR", "Employees On Site": "50", "Employees On Site Indicator": "0", "Employees Total": "50", "Employees Total Indicator": "0", "Include Principles Indicator": null, "Import/Export Code": "G", "Business Legal Type": "14", "Control Indicator": null, "Location Type": "0", "Subsidiary Code": "0", "Local Headquarter ID Number": null, "Local Headquarter Company Name": null, "Local Headquarter Street Address": null, "Local Headquarter City": null, "Local Headquarter State/Province": null, "Local Headquarter Country Name": null, "National Headquarter ID Number": "123456789", "National Headquarter Company Name": "Heineken N.V.", "National Headquarter Street Address": "Keizersgracht 424", "National Headquarter City Name": "AMSTERDAM", "National Headquarter State/Province Name": "NOORD-HOLLAND", "Global Headquarter ID Number": "123456789", "Global Headquarter Company Name": "Heineken N.V.", "Global Headquarter Street Address": "Keizersgracht 424", "Global Headquarter City Name": "AMSTERDAM", "Global Headquarter State/Province": "NOORD-HOLLAND", "Global Headquarter Country Name": "Netherlands", "Number of Companies in Group": "1", "Website": "https://bolddata.com", "Email": "info@bolddata.nl", "Executives": null } ``` > Not all fields are returned; this depends on your subscription package. Contact support to receive additional fields. Numeric fields (revenue, employee counts) are returned as strings, not numbers. > **Corporate groups:** Fields from `Location Type` through `Number of Companies in Group` (see table below) describe parent-subsidiary relationships within a worldwide corporate group. Pass any member's `Global Headquarter ID Number` as `worldwideHeadquarterID` on export to fetch all entities in the group. Full example: [Corporate Structure](https://companydata.com/api-docs/use-cases/corporate-structure/). ## Company Record Fields All string fields may be `null` or empty string when data is unavailable. Numeric values (revenue, employee counts, founding year) are returned as strings and must be parsed by the client. | **Field** | **Type** | **Description** | |-----------|----------|-----------------| | **ID** | string | Unique identifier for the company record. Store this to retrieve or reference the company in future requests without re-searching. | | **Company Name** | string | Primary registered name of the company. | | **Trade Name** | string \| null | Alternative or "doing business as" (DBA) name the company may operate under. | | **Address Status** | string \| null | Indicates the address type on file. For European records, if only the registered address is available, it is used as the physical address and this field flags that. | | **Address 1** | string \| null | Primary street address (street name and building number). | | **Address 2** | string \| null | Secondary address details (suite, floor, building name). | | **City** | string \| null | City of the company location. | | **State/Province** | string \| null | State or province of the company location. | | **Country** | string \| null | Country name in English (e.g., `"Netherlands"`, `"United States"`). | | **Postal Code** | string \| null | Postal or ZIP code. Always a string - may contain letters (e.g., `"1016 GC"`). Filter: `postalCode` (exact), `postalCodeInteger` (numeric range). | | **Company Registration Number** | string \| null | Official national registration ID (e.g., Dutch KvK number, UK Companies House number). See `Company Registration Type Code` for which system applies. Filter: `nationalId`. | | **Company Registration Type Code** | string \| null | Code identifying the national registration system used (e.g., `"KVK"` for the Netherlands, `"CRO"` for the UK). Use this to determine how to validate or format `Company Registration Number`. | | **Country Phone Access Code** | string \| null | International dialing prefix without leading `+` or `00` (e.g., `"31"` for the Netherlands, `"1"` for the US). | | **Phone Number** | string \| null | Company phone number. US numbers include area code with no punctuation. Filter: `phoneNumber`, `hasPhone`. | | **Cable Telex** | string \| null | Legacy cable or telex number. Rarely populated on modern records. | | **Fax Number** | string \| null | Company fax number. Filter: `hasFax`. | | **CEO Name** | string \| null | Name of the highest-ranking contact at this location. Also available as Position 1 in the `Executives` array when executive data is enabled on your account. Filter: `hasCEOName`, `hasContactPerson`. | | **CEO Title** | string \| null | Title of the CEO or top contact, possibly abbreviated (e.g., `"Pres"`, `"Dir"`, `"CEO"`). | | **Business Category** | string \| null | Plain-English description of the company's primary activity (e.g., `"Computer Programming Services"`). | | **Business Category Code 1** | string \| null | Primary 4-digit SIC code (1987 U.S. classification system). Filter: `sic4Digits`. | | **Business Category Code 1 - Description** | string \| null | Description of the primary SIC code (e.g., `"Computer Programming, Data Processing"`). | | **Founding Year** | string \| null | Four-digit year the current ownership took control, or the founding year if no change of control occurred. Not available for branch records. Filter: `foundingYears[min]` / `foundingYears[max]`. | | **Yearly Revenue Local Currency** | string \| null | Annual sales in the local currency. See `Currency Code` for the applicable currency. | | **Yearly Revenue Indicator** | string \| null | Data quality flag for `Yearly Revenue Local Currency`. `"0"` = actual figure, `"1"` = low-end range estimate, `"2"` = estimated (also used when the revenue value is zero), empty/null = not available. | | **Yearly Revenue in U.S. Dollars** | string \| null | Annual sales in USD. Exchange rate updated once per year. Filter: `annualSales[min]` / `annualSales[max]`. | | **Currency Code** | string \| null | ISO 4217 currency code for `Yearly Revenue Local Currency` (e.g., `"EUR"`, `"USD"`, `"GBP"`). | | **Employees On Site** | string \| null | Number of employees at this specific location. Available primarily for US and Canadian records. Filter: `employeesHere[min]` / `employeesHere[max]`. | | **Employees On Site Indicator** | string \| null | Data quality flag for `Employees On Site`. `"0"` = actual figure, `"1"` = low-end estimate, `"2"` = estimated (also used when the value is zero), empty/null = not available. | | **Employees Total** | string \| null | Total number of employees across the entire company. Filter: `employeesTotal[min]` / `employeesTotal[max]`. | | **Employees Total Indicator** | string \| null | Data quality flag for `Employees Total`. `"0"` = actual figure, `"1"` = low-end estimate, `"2"` = estimated (also used when the value is zero), empty/null = not available. | | **Include Principles Indicator** | string \| null | Whether business owners or partners are counted in `Employees Total`. `"Y"` = included, empty/null = not included or unknown. Available primarily on non-US records. | | **Import/Export Code** | string \| null | Trade activity of the company. `"A"` = imports, exports & agent, `"B"` = imports & exports, `"C"` = imports only, `"D"` = imports & agent, `"E"` = exports & agent, `"F"` = agent only (no inventory), `"G"` = none or not available, `"H"` = exports only, empty = not available. Filter: `importExportCode`. | | **Business Legal Type** | string \| null | Legal entity type code. `"1"` or `"13"` = proprietorship, `"2"` or `"7"` = partnership, `"3"` = corporation, `"8"` = joint venture, `"9"` = master limited partnership, `"10"` = general partnership, `"11"` = limited partnership, `"12"` = partnership (unknown type), `"14"` = limited liability, `"15"` = friendly society, `"0"` or empty = not available. Other values may exist. Filter: `legalStatus`. | | **Control Indicator** | string \| null | Reserved field, not currently in use. | | **Location Type** | string \| null | Company's role in its corporate hierarchy. `"0"` = single location (no entities report to it), `"1"` = headquarter/parent (branches and/or subsidiaries report to it), `"2"` = branch (secondary location of a headquarter), empty = not available. Filter: `statusCode`. | | **Subsidiary Code** | string \| null | Subsidiary status. `"0"` = not a subsidiary, `"3"` = is a subsidiary, empty = not linked. Populated on linked records only. Filter: `subsidiaryCode`. | | **Local Headquarter ID Number** | string \| null | `ID` of the immediate parent entity above this location. Pass this value as the `ID` filter to fetch that record. Empty if not linked. | | **Local Headquarter Company Name** | string \| null | Name of the immediate parent entity. Empty if not linked. | | **Local Headquarter Street Address** | string \| null | Street address of the immediate parent entity. Empty if not linked. | | **Local Headquarter City** | string \| null | City of the immediate parent entity. Empty if not linked. | | **Local Headquarter State/Province** | string \| null | State/province of the immediate parent entity. Empty if not linked. | | **Local Headquarter Country Name** | string \| null | Country name of the immediate parent entity. Empty if not linked. | | **National Headquarter ID Number** | string \| null | `ID` of the highest entity within the same country as this location. May equal this company's own `ID` if it is the national top. | | **National Headquarter Company Name** | string \| null | Name of the national-level parent. Empty if not linked. | | **National Headquarter Street Address** | string \| null | Street address of the national-level parent. Empty if not linked. | | **National Headquarter City Name** | string \| null | City of the national-level parent. Empty if not linked. | | **National Headquarter State/Province Name** | string \| null | State/province of the national-level parent. Empty if not linked. | | **Global Headquarter ID Number** | string \| null | `ID` of the ultimate worldwide parent of the corporate group. All companies in the group share this value. Pass this to the `worldwideHeadquarterID` filter to fetch all entities in the group. | | **Global Headquarter Company Name** | string \| null | Name of the ultimate worldwide parent. Empty if not linked. | | **Global Headquarter Street Address** | string \| null | Street address of the ultimate worldwide parent. Empty if not linked. | | **Global Headquarter City Name** | string \| null | City of the ultimate worldwide parent. Empty if not linked. | | **Global Headquarter State/Province** | string \| null | State/province of the ultimate worldwide parent. Empty if not linked. | | **Global Headquarter Country Name** | string \| null | Country name of the ultimate worldwide parent. Empty if not linked. | | **Number of Companies in Group** | string \| null | Total number of entities in the worldwide corporate group (HQ + all subsidiaries and branches). All group members share this count. Empty if not linked. | | **Website** | string \| null | Company website URL. Filter: `hasWebsite`. | | **Email** | string \| null | Company email address(es). May contain multiple addresses separated by commas. Filter: `email`, `hasEmail`. | | **Executives** *(BETA)* | array \| null | Array of executive/management personnel objects. Up to 11 executives per company. Null if executive data is not enabled or not available. See [structure below](#executives-structure). | > We aim to provide accurate and up-to-date data, but returned fields may contain errors, be incomplete, or change without notice. See our [Data Disclaimer](https://companydata.com/disclaimer) for more details. ## Query Parameter to Response Field Reference Query parameter names do not always match the response field names they correspond to. This table provides the explicit mapping. | **Query Parameter** | **Corresponding Response Field** | **Notes** | |---------------------|----------------------------------|-----------| | `ID` | `ID` | Exact match | | `search` | `Company Name` | Full-text search on name | | `nationalId` | `Company Registration Number` | - | | `countryCode` | *(no direct field - use `Country` for the name)* | ISO 2-letter code used for filtering only | | `countryName` | `Country` | Exact match required | | `cityName` | `City` | Exact match required | | `provinceName` | `State/Province` | Exact match required | | `regionName` | `State/Province` | Region variant; exact match required | | `postalCode` | `Postal Code` | Exact string match | | `postalCodeInteger` | `Postal Code` | Numeric range filter on postal code | | `streetName` | `Address 1` | Prefix (partial) match | | `sic4Digits` | `Business Category Code 1` | - | | `statusCode` | `Location Type` | Codes: `0`=single, `1`=HQ, `2`=branch | | `legalStatus` | `Business Legal Type` | Same numeric codes | | `subsidiaryCode` | `Subsidiary Code` | Same numeric codes | | `importExportCode` | `Import/Export Code` | Same letter codes | | `employeesHere[min/max]` | `Employees On Site` | Range filter | | `employeesTotal[min/max]` | `Employees Total` | Range filter | | `annualSales[min/max]` | `Yearly Revenue in U.S. Dollars` | Range filter in USD | | `foundingYears[min/max]` | `Founding Year` | Range filter | | `worldwideHeadquarterID` | `Global Headquarter ID Number` | Fetches all entities in a corporate group | | `phoneNumber` | `Phone Number` | Full number with country code | | `email` | `Email` | Email address or domain pattern | | `hasEmail` | `Email` | Returns only companies with a non-empty `Email` field | | `hasPhone` | `Phone Number` | Returns only companies with a non-empty `Phone Number` | | `hasFax` | `Fax Number` | Returns only companies with a non-empty `Fax Number` | | `hasWebsite` | `Website` | Returns only companies with a non-empty `Website` | | `hasContactPerson` | `CEO Name` | Returns only companies with a non-empty `CEO Name` | | `hasCEOName` | `CEO Name` | Returns only companies with a non-empty `CEO Name` | | `hasNationalID` | `Company Registration Number` | Returns only companies with a registration number | | `hasStreetAddress` | `Address 1` | Returns only companies with a street address | | `hasPostalCode` | `Postal Code` | Returns only companies with a postal code | | `hasBusinessName` | `Company Name` | Returns only companies with a name on file | | `hasMarketability` | *(internal flag, no direct response field)* | Filters for marketable records | ## Executives Structure *(BETA)* > This feature is currently in beta. We are still researching and improving executive data coverage. The `Executives` field is an **array of objects**. Each object represents one executive. Executives are only included if they have at least a first name or last name. Up to 11 executives can be returned per company. All fields within each executive object may be null. ```json { "Executives": [ { "Position": 1, "Title": "CEO", "Title Code": "100", "First Name": "Jane", "Last Name": "Doe", "Gender": "Female", "Language Preference": "en-US" }, { "Position": 2, "Title": "CFO", "Title Code": "200", "First Name": "John", "Last Name": "Smith", "Gender": "Male", "Language Preference": "en-US" } ] } ``` | **Field** | **Type** | **Description** | |-----------|----------|-----------------| | Position | number \| null | Numeric rank of the executive within the company (1 = most senior) | | Title | string \| null | Job title of the executive (e.g., `"CEO"`, `"CFO"`, `"Director"`) | | Title Code | string \| null | Standardized internal code representing the job title | | First Name | string \| null | Executive's first name | | Last Name | string \| null | Executive's last name | | Gender | string \| null | Gender of the executive (`"Male"` or `"Female"`) | | Language Preference | string \| null | ISO locale code indicating the executive's preferred language - see [Language Codes](https://companydata.com/returned-fields/language-codes) | --- Source: https://companydata.com/api-docs/returned-fields/language-codes/ # Language Preference Codes The `Language Preference` field on executive records uses ISO locale codes. Below is the complete mapping of supported languages to their ISO locale codes. | Language | ISO Locale | |----------|------------| | Canadian English | en-CA | | Canadian French | fr-CA | | Dutch | nl | | U.K. English | en-GB | | French | fr | | German | de | | Italian | it | | Portuguese | pt | | Spanish | es | | U.S. English | en-US | | Afrikaans | af | | Arabic | ar | | Chinese | zh | | Czech | cs | | Danish | da | | Finnish | fi | | Flemish | nl-BE | | Greek | el | | Hebrew | he | | Hungarian | hu | | Japanese | ja | | Polish | pl | | Romanian | ro | | Russian | ru | | Slovak | sk | | Swedish | sv | | Turkish | tr | | Austrian German | de-AT | | Swiss German | de-CH | | Swiss French | fr-CH | | Swiss Italian | it-CH | | Belgian French | fr-BE | | Mexican Spanish | es-MX | | Norwegian | no | --- Source: https://companydata.com/api-docs/pagination/ # Pagination The CompanyData API uses pagination to return large result sets efficiently. Search and export support **standard pagination**; export additionally supports **scroll pagination** for unlimited records. ## Standard Pagination Both `/api/company/search` and `/api/company/export` support standard pagination using: - **`page`**: The page number to retrieve. Defaults to `1` if not provided. - **`pageSize`**: The number of records to return per page. ### Search Endpoint (`/api/company/search`) - Defaults to `25` records per page - Maximum `pageSize` is `50` - Maximum total records retrievable: `50` ### Export Endpoint (`/api/company/export`) - Defaults to `25` records per page - Recommended `pageSize`: 100-1000 - Maximum total records retrievable: `10,000` with standard pagination ## Scroll Pagination (Export Endpoint Only) The `/api/company/export` endpoint also supports **scroll pagination** for unlimited record retrieval: - **`useScroll=true`**: Enable scroll pagination mode - **`scrollId`**: Use `meta.scrollId` from the previous response for subsequent requests - **Unlimited records**: No maximum record limit - **Time-limited**: Scroll context expires after 8 minutes For detailed information and examples, see the [Company Export documentation](https://companydata.com/api-docs/request-company/#pagination). ## Response Structure **Standard pagination** returns records under `data.records` and page details under `meta`: ```json { "meta": { "totalCount": 1250, "currentPage": 1, "totalPages": 50, "pageSize": 25 }, "data": { "records": [ /* company objects */ ] } } ``` **Scroll pagination** uses the same envelope; `meta` carries the scroll cursor instead of page numbers, and no total count is available: ```json { "meta": { "pageSize": 1000, "hasMoreRecords": true, "scrollId": "abc123xyz456", "recordCount": 1000 }, "data": { "records": [ /* company objects */ ] } } ``` ## How Standard Pagination Works - **Calculation of `from`**: The `from` parameter is calculated as `(page - 1) * pageSize`. This determines the starting point of the records to be retrieved for the specified page. - **Total Count and Pages**: - The total number of records (`meta.totalCount`) is determined from the search query results. - The total number of pages (`meta.totalPages`) is calculated by dividing `meta.totalCount` by `meta.pageSize` and rounding up. ## Example Request To request the second page of results with 10 records per page: ```bash curl -G https://app.companydata.com/api/company/search \ -H "x-api-key: {ApiKey}" \ -d "page=2&pageSize=10" ``` --- Source: https://companydata.com/api-docs/countries/ # Countries Filter search and export with **2-letter ISO country codes** on `countryCode` (comma-separated for multiple markets, e.g. `NL,BE,DE`). Codes below match values stored in the database, including the same registers that power [Companies House (GB)](https://companydata.com/companies-house-api/), **Handelsregister (DE)**, and **KVK (NL)** lookups. ### Example: search companies in the Netherlands ```bash curl -G "https://app.companydata.com/api/company/search" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "countryCode=NL" \ --data-urlencode "search=Heineken" \ --data-urlencode "pageSize=5" ``` ```json { "meta": { "totalCount": 12, "currentPage": 1, "totalPages": 3, "pageSize": 5 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] } } ``` ## Country code reference Use **2-letter ISO country codes** in the `countryCode` query parameter on search and export (comma-separated for multiple countries, e.g. `NL,BE,DE`). Codes below match the values stored in the database. | Country Code | Country Name | |--------------|--------------------------| | AF | AFGHANISTAN | | AL | ALBANIA | | DZ | ALGERIA | | AD | ANDORRA | | AO | ANGOLA | | AI | ANGUILLA | | AG | ANTIGUA & BARBUDA | | AR | ARGENTINA | | AM | ARMENIA | | AW | ARUBA | | AU | AUSTRALIA | | AT | AUSTRIA | | AZ | AZERBAIJAN | | BS | BAHAMAS | | BH | BAHRAIN | | BD | BANGLADESH | | BB | BARBADOS | | BY | BELARUS | | BE | BELGIUM | | BZ | BELIZE | | BJ | BENIN | | BM | BERMUDA | | BT | BHUTAN | | BO | BOLIVIA | | BQ | BONAIRE ST EUST SABA | | BA | BOSNIA-HERZEGOVINA | | BW | BOTSWANA | | BR | BRAZIL | | BN | BRUNEI | | BG | BULGARIA | | BF | BURKINA FASO | | BI | BURUNDI | | KH | CAMBODIA | | CM | CAMEROON | | CA | CANADA | | CV | CAPE VERDE | | MP | CAROLINE ISLANDS | | KY | CAYMAN ISLANDS | | CF | CENTRAL AFRICAN REP | | TD | CHAD | | CL | CHILE | | CN | CHINA | | CX | CHRISTMAS ISLAND | | CO | COLOMBIA | | KM | COMOROS | | CG | CONGO | | CD | CONGO DEMOCRATIC REP | | CK | COOK ISLANDS | | CR | COSTA RICA | | HR | CROATIA | | CW | CURACAO | | CY | CYPRUS | | CZ | CZECH REPUBLIC | | DK | DENMARK | | DJ | DJIBOUTI | | DM | DOMINICA | | DO | DOMINICAN REPUBLIC | | TL | EAST TIMOR | | EC | ECUADOR | | EG | EGYPT | | SV | EL SALVADOR | | GB | ENGLAND | | GQ | EQUATORIAL GUINEA | | ER | ERITREA | | EE | ESTONIA | | SZ | ESWATINI | | ET | ETHIOPIA | | FK | FALKLAND ISLANDS | | FO | FAROE ISLANDS | | FJ | FIJI | | FI | FINLAND | | FR | FRANCE | | GF | FRENCH GUIANA | | PF | FRENCH POLYNESIA | | GA | GABON | | GM | GAMBIA | | GE | GEORGIA | | DE | GERMANY | | GH | GHANA | | GI | GIBRALTAR | | GR | GREECE | | GL | GREENLAND | | GD | GRENADA | | GP | GUADELOUPE | | GU | GUAM | | GT | GUATEMALA | | GN | GUINEA | | GW | GUINEA-BISSAU | | GY | GUYANA | | HT | HAITI | | HN | HONDURAS | | HK | HONG KONG | | HU | HUNGARY | | IS | ICELAND | | IN | INDIA | | ID | INDONESIA | | IR | IRAN | | IQ | IRAQ | | IE | IRELAND | | IL | ISRAEL | | IT | ITALY | | CI | IVORY COAST | | JM | JAMAICA | | JP | JAPAN | | JO | JORDAN | | KZ | KAZAKHSTAN | | KE | KENYA | | KI | KIRIBATI | | KR | KOREA REP OF | | XK | KOSOVO | | KW | KUWAIT | | KG | KYRGYZSTAN | | LA | LAOS | | LV | LATVIA | | LB | LEBANON | | LS | LESOTHO | | LR | LIBERIA | | LY | LIBYA | | LI | LIECHTENSTEIN | | LT | LITHUANIA | | LU | LUXEMBOURG | | MO | MACAO | | MG | MADAGASCAR | | MW | MALAWI | | MY | MALAYSIA | | MV | MALDIVES | | ML | MALI | | MT | MALTA | | MH | MARSHALL ISLANDS | | MQ | MARTINIQUE | | MR | MAURITANIA | | MU | MAURITIUS | | MX | MEXICO | | FM | MICRONESIA FED ST | | MD | MOLDOVA | | MC | MONACO | | ME | MONTENEGRO | | MS | MONTSERRAT | | MA | MOROCCO | | MZ | MOZAMBIQUE | | MM | MYANMAR | | NA | NAMIBIA | | NR | NAURU | | NP | NEPAL | | NL | NETHERLANDS | | NZ | NEW ZEALAND | | NI | NICARAGUA | | NE | NIGER | | NG | NIGERIA | | NF | NORFOLK ISLAND | | MK | NORTH MACEDONIA | | GB | NORTHERN IRELAND | | MP | NORTHERN MARIANA IS | | NO | NORWAY | | OM | OMAN | | PK | PAKISTAN | | PA | PANAMA | | PG | PAPUA NEW GUINEA | | PY | PARAGUAY | | PE | PERU | | PH | PHILIPPINES | | PL | POLAND | | PT | PORTUGAL | | QA | QATAR | | RE | REUNION | | RO | ROMANIA | | RU | RUSSIAN FEDERATION | | RW | RWANDA | | WS | SAMOA | | AS | SAMOA AMERICAN | | SM | SAN MARINO | | ST | SAO TOME & PRINCIPE | | SA | SAUDI ARABIA | | GB | SCOTLAND | | SN | SENEGAL | | RS | SERBIA | | SC | SEYCHELLES | | SL | SIERRA LEONE | | SG | SINGAPORE | | SK | SLOVAKIA | | SI | SLOVENIA | | SB | SOLOMON ISLANDS | | SO | SOMALIA | | ZA | SOUTH AFRICA | | SS | SOUTH SUDAN | | ES | SPAIN | | LK | SRI LANKA | | SH | ST HELENA | | KN | ST KITTS-NEVIS | | LC | ST LUCIA | | SX | ST MAARTEN | | VC | ST VINCENT | | SD | SUDAN | | SR | SURINAME | | SE | SWEDEN | | CH | SWITZERLAND | | SY | SYRIA | | TW | TAIWAN | | TJ | TAJIKISTAN | | TZ | TANZANIA | | TH | THAILAND | | TG | TOGO | | TK | TOKELAU | | TO | TONGA | | TT | TRINIDAD & TOBAGO | | TN | TUNISIA | | TR | TURKEY | | CY | TURKISH REP N CYPRUS | | TM | TURKMENISTAN | | TC | TURKS & CAICOS IS | | TV | TUVALU | | UG | UGANDA | | UA | UKRAINE | | AE | UNITED ARAB EMIRATES | | GB | UNITED KINGDOM | | UY | URUGUAY | | US | USA | | UZ | UZBEKISTAN | | VU | VANUATU | | VE | VENEZUELA | | VN | VIETNAM | | VG | VIRGIN ISLANDS UK | | UM | WAKE ISLAND | | GB | WALES | | YE | YEMEN | | ZM | ZAMBIA | | ZW | ZIMBABWE | --- Source: https://companydata.com/api-docs/sdks/ # SDKs Install the official JavaScript/TypeScript SDK for the fastest path to production. It covers the same **search**, **export**, and **city lookup** endpoints documented in the [OpenAPI spec](https://companydata.com/openapi.json). ```bash npm install @companydata/companydata-sdk ``` ```javascript const client = new ApiClient({ baseUrl: 'https://app.companydata.com', auth: { type: 'apiKey', apiKey: process.env.COMPANYDATA_API_KEY ?? '' }, }) const result = await client.company.search({ search: 'Heineken', countryCode: 'NL', pageSize: 5 }) console.log(result.data.records) ``` This page lists Software Development Kits (SDKs) that help you integrate with the CompanyData API. ## JavaScript / TypeScript SDK For Node.js and browser applications, use the npm package: `@companydata/companydata-sdk` ## When to use an SDK vs raw HTTP - Use an SDK when you want faster integration, typed APIs, and consistent error handling. - Use raw HTTP when you need maximum control or advanced troubleshooting. --- Source: https://companydata.com/api-docs/sdks/javascript/ # JavaScript SDK Use the official JavaScript SDK for Node.js and browser apps. ## Install ```bash npm install @companydata/companydata-sdk ``` ## Initialize client ```ts const client = new ApiClient({ baseUrl: 'https://app.companydata.com', auth: { type: 'apiKey', apiKey: process.env.COMPANYDATA_API_KEY ?? '', }, timeoutMs: 15000, retry: { maxRetries: 2, }, }) ``` ## Make your first request ```ts const result = await client.company.search({ countryCode: 'NL', search: 'Heineken', page: 1, pageSize: 25, }) console.log(result.data) ``` **Example response** from `client.company.search`: ```json { "meta": { "totalCount": 3, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] } } ``` ## Export with scroll pagination ```ts let page = await client.company.exportWithScroll({ countryCode: 'NL', pageSize: 1000, }) while (page.meta.hasMoreRecords) { // process page.data.records page = await client.company.exportWithScroll({ scrollId: page.meta.scrollId, pageSize: 1000, }) } ``` ## Lookup endpoints ```ts const cities = await client.lookup.cities({ search: 'ams', countries: 'NL', limit: 100, }) console.log(cities.data) ``` ## Handle API errors ```ts try { await client.company.search({ countryCode: 'NL', search: 'Heineken', }) } catch (error) { if (error instanceof ApiError) { console.error(error.status, error.code, error.requestId) } else { console.error(error) } } ``` ## Notes - The SDK package supports Node.js and browser runtimes. - Main SDK methods map to: - `client.company.search` -> `/api/company/search` - `client.company.export` -> `/api/company/export` - `client.company.exportWithScroll` -> `/api/company/export?useScroll=true` - `client.lookup.cities` -> `/api/cities` - For endpoint-specific filters and response field definitions, see [Company API](https://companydata.com/api-docs/request-company) and [Response Fields](https://companydata.com/api-docs/returned-fields). --- Source: https://companydata.com/api-docs/use-cases/ # Use Cases Practical guides for common CompanyData API workflows: lead generation, KYC, autocomplete, address lookup, and corporate hierarchies. Each guide includes copy-pasteable code and example JSON responses. ## Quick Start All examples use native `fetch` and work in both Node.js 18+ and browsers. Here's the basic pattern: ```ts const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; async function fetchApi(endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value) url.searchParams.append(key, String(value)); }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Search for companies const searchResults = await fetchApi('search', { search: 'Philips', countryCode: 'NL' }); // Get full company details const companyDetails = await fetchApi('export', { ID: '407809109' }); ``` ## Need Help? - **API Reference** - See the [Company API documentation](https://companydata.com/request-company) for all available parameters - **Authentication** - Learn about [API key authentication](https://companydata.com/authentication) - **Support** - Contact us for custom use cases or integration help --- Source: https://companydata.com/api-docs/use-cases/company-search/ # Company Search & Autocomplete Build a company search autocomplete for registration forms, CRM data entry, and B2B onboarding. The CompanyData search endpoint returns lightweight records quickly as users type, typically `ID`, `Company Name`, and location fields. ## Common Use Cases - **Registration forms** - Let users find and select their company - **B2B onboarding** - Verify company details during signup - **CRM data entry** - Reduce manual typing errors - **Invoice/order forms** - Quick company selection with autofill ## How It Works 1. User starts typing a company name (minimum 2 characters) 2. Debounce the input to avoid excessive API calls 3. Call the search API with the query 4. Display matching companies in a dropdown 5. On selection, fetch full company details ## Example Response Request: `GET /api/company/search?search=Heineken&countryCode=NL&page=1&pageSize=10` ```json { "meta": { "totalCount": 3, "currentPage": 1, "totalPages": 1, "pageSize": 10 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] } } ``` ## TypeScript Implementation ```ts // Company Search Autocomplete - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; interface SearchResult { ID: string; 'Company Name': string; 'Country Name'?: string; 'City'?: string; } interface CompanyDetails { ID: string; 'Company Name': string; 'Trade Name'?: string; 'Address 1'?: string; 'City'?: string; 'Postal Code'?: string; 'Country Name'?: string; 'Phone Number'?: string; 'Email'?: string; 'Website'?: string; [key: string]: any; } async function fetchApi(endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value) url.searchParams.append(key, String(value)); }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Search for companies (autocomplete) async function searchCompanies( query: string, options: { countryCode?: string; limit?: number } = {} ): Promise { if (query.length < 2) return []; const params: Record = { search: query, page: 1, pageSize: options.limit || 10, }; if (options.countryCode) { params.countryCode = options.countryCode; } const response = await fetchApi<{ data: { records: SearchResult[] } }>('search', params); return response.data?.records || []; } // Get full company details after selection async function getCompanyDetails(companyId: string): Promise { const response = await fetchApi<{ data: { records: CompanyDetails[] } }>('export', { ID: companyId, page: 1, pageSize: 1, }); return response.data?.records?.[0] || null; } // Debounce helper for autocomplete function debounce any>( func: T, wait: number ): (...args: Parameters) => void { let timeout: ReturnType; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } // Example: React-style autocomplete handler class CompanyAutocomplete { private cache = new Map(); async search(query: string, countryCode?: string): Promise { const cacheKey = `${query}-${countryCode || ''}`; // Check cache first if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey)!; } const results = await searchCompanies(query, { countryCode }); // Cache results this.cache.set(cacheKey, results); return results; } async select(company: SearchResult): Promise { return getCompanyDetails(company.ID); } } // Example usage async function main() { const autocomplete = new CompanyAutocomplete(); // Simulate user typing "phil" const results = await autocomplete.search('phil', 'NL'); console.log('Search results:'); results.forEach((company, i) => { console.log(` ${i + 1}. ${company['Company Name']}${company['Country Name'] ? ` (${company['Country Name']})` : ''}`); }); // User selects first result if (results.length > 0) { const details = await autocomplete.select(results[0]); if (details) { console.log('\nSelected company details:'); console.log(` Name: ${details['Company Name']}`); console.log(` Address: ${details['Address 1']}, ${details['City']}`); console.log(` Phone: ${details['Phone Number']}`); } } } main(); ``` ```js // Company Search Autocomplete - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; async function fetchApi(endpoint, params) { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value) url.searchParams.append(key, String(value)); }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Search for companies (autocomplete) async function searchCompanies(query, options = {}) { if (query.length < 2) return []; const params = { search: query, page: 1, pageSize: options.limit || 10, }; if (options.countryCode) { params.countryCode = options.countryCode; } const response = await fetchApi('search', params); return response.data?.records || []; } // Get full company details after selection async function getCompanyDetails(companyId) { const response = await fetchApi('export', { ID: companyId, page: 1, pageSize: 1, }); return response.data?.records?.[0] || null; } // Debounce helper function debounce(func, wait) { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } // Example usage async function main() { // Search as user types const results = await searchCompanies('Microsoft', { countryCode: 'US' }); console.log('Results:', results.map(c => c['Company Name'])); // Get details when user selects if (results.length > 0) { const details = await getCompanyDetails(results[0].ID); console.log('Details:', details); } } main(); ``` ## React Component Example Here's a complete React autocomplete component: ```tsx interface Company { ID: string; 'Company Name': string; 'Country Name'?: string; 'City'?: string; } interface Props { apiKey: string; countryCode?: string; onSelect: (company: Company) => void; placeholder?: string; } export function CompanyAutocomplete({ apiKey, countryCode, onSelect, placeholder }: Props) { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [isOpen, setIsOpen] = useState(false); const [loading, setLoading] = useState(false); const [highlighted, setHighlighted] = useState(0); const debounceRef = useRef>(); const search = useCallback(async (searchQuery: string) => { if (searchQuery.length < 2) { setResults([]); return; } setLoading(true); try { const url = new URL('https://app.companydata.com/api/company/search'); url.searchParams.set('search', searchQuery); url.searchParams.set('page', '1'); url.searchParams.set('pageSize', '8'); if (countryCode) url.searchParams.set('countryCode', countryCode); const response = await fetch(url.toString(), { headers: { 'x-api-key': apiKey }, }); const data = await response.json(); setResults(data.data?.records || []); setIsOpen(true); setHighlighted(0); } catch (error) { console.error('Search error:', error); setResults([]); } setLoading(false); }, [apiKey, countryCode]); const handleChange = (e: React.ChangeEvent) => { const value = e.target.value; setQuery(value); // Debounce search if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => search(value), 300); }; const handleSelect = (company: Company) => { setQuery(company['Company Name']); setIsOpen(false); onSelect(company); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (!isOpen) return; switch (e.key) { case 'ArrowDown': e.preventDefault(); setHighlighted(i => Math.min(i + 1, results.length - 1)); break; case 'ArrowUp': e.preventDefault(); setHighlighted(i => Math.max(i - 1, 0)); break; case 'Enter': e.preventDefault(); if (results[highlighted]) handleSelect(results[highlighted]); break; case 'Escape': setIsOpen(false); break; } }; return ( )} {isOpen && results.length > 0 && (
    {results.map((company, index) => (
  • handleSelect(company)} onMouseEnter={() => setHighlighted(index)} className={`cursor-pointer px-4 py-2 ${ index === highlighted ? 'bg-blue-50' : 'hover:bg-gray-50' }`} >
  • ))}
)} ); } ``` ## Best Practices ### Performance - **Debounce input** - Wait 200-300ms after user stops typing before searching - **Cache results** - Store recent searches to avoid duplicate API calls - **Limit results** - Request only 8-10 results for autocomplete dropdown ### User Experience - **Show loading state** - Display a spinner while searching - **Keyboard navigation** - Support arrow keys and Enter for selection - **Highlight matches** - Bold the matching portion of company names - **Show location** - Display city/country to help disambiguate similar names ### Accessibility - Use proper ARIA attributes (`role="listbox"`, `aria-activedescendant`) - Support keyboard navigation - Announce results to screen readers --- Source: https://companydata.com/api-docs/use-cases/corporate-structure/ # Corporate Structure Map **parent companies, subsidiaries, branches, and UBO charts** for any group in **200+ countries** using HQ ID fields from official registers (not inferred website data). Export one record to read **Global Headquarter ID Number**, then pass it as **`worldwideHeadquarterID`** on `/api/company/export` to retrieve every entity in the corporate group in one query. ## Interactive Demo ## How It Works The CompanyData API provides ID fields that define a company's position in its corporate hierarchy: - **Global Headquarter ID Number** - The ultimate parent of the entire corporate group - **National Headquarter ID Number** - The highest parent within the same country - **Local Headquarter ID Number** - The immediate parent entity - **worldwideHeadquarterID** - Query parameter to fetch all companies in a group The approach: 1. Search for your target company 2. Get the `Global Headquarter ID Number` from the company record 3. Fetch all companies in the group using `worldwideHeadquarterID` 4. Group companies by their parent: Local HQ ID → National HQ ID → Global HQ ID 5. Build a tree structure from the grouped results > For large corporations like Shell or BP, this may return thousands of companies. The demo limits results with a `maxCompanies` parameter. ## Implementation The code below powers the interactive demo above. Copy and adapt it for your own use. ### Types ```ts interface Company { ID: string 'Company Name': string 'Country Code'?: string 'Country Name'?: string 'City'?: string 'Local Headquarter ID Number'?: string 'National Headquarter ID Number'?: string 'Global Headquarter ID Number'?: string [key: string]: any } interface ApiResponse { data: { records: Company[] totalCount?: number } } interface CorporateTreeNode { company: Company subsidiaries: CorporateTreeNode[] } ``` ### API Helper ```ts const BASE_URL = 'https://app.companydata.com/api/company' async function fetchApi(apiKey: string, endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`) Object.entries(params).forEach(([key, value]) => { url.searchParams.append(key, String(value)) }) const response = await fetch(url.toString(), { headers: { 'x-api-key': apiKey }, }) if (!response.ok) { const text = await response.text() throw new Error(`HTTP ${response.status}: ${text}`) } return response.json() } ``` ### Search for Companies ```ts async function searchCompany(apiKey: string, searchTerm: string): Promise { const response = await fetchApi(apiKey, 'search', { search: searchTerm, page: 1, pageSize: 25, }) return response.data?.records ?? [] } ``` ### Get All Companies in Group Fetch all companies belonging to the same corporate group in a single query: ```ts async function getAllCompaniesInGroup( apiKey: string, globalUltimateId: string, maxCompanies = 500 ): Promise { const allCompanies: Company[] = [] let page = 1 const pageSize = 50 let hasMore = true while (hasMore && allCompanies.length < maxCompanies) { const response = await fetchApi(apiKey, 'export', { worldwideHeadquarterID: globalUltimateId, page, pageSize, }) const records = response.data?.records ?? [] allCompanies.push(...records) hasMore = records.length === pageSize && allCompanies.length < maxCompanies page++ } return allCompanies.slice(0, maxCompanies) } async function getCompanyById(apiKey: string, companyId: string): Promise { const response = await fetchApi(apiKey, 'export', { ID: companyId, page: 1, pageSize: 1, }) return response.data?.records?.[0] ?? null } ``` ### Build the Corporate Tree Group companies by their parent HQ ID to build a tree structure: ```ts function buildTreeFromFlatList( companies: Company[], rootCompany: Company ): CorporateTreeNode { const globalUltimateId = rootCompany.ID // Create a map of ID -> node for quick lookup const companyMap = new Map() companies.forEach(company => { companyMap.set(company.ID, { company, subsidiaries: [] }) }) // Use root from map if present, otherwise create from passed rootCompany let root = companyMap.get(globalUltimateId) if (!root) { root = { company: rootCompany, subsidiaries: [] } } // Group companies by their parent ID companies.forEach(company => { if (company.ID === globalUltimateId) return // Skip the root const node = companyMap.get(company.ID) if (!node) return const localHqId = company['Local Headquarter ID Number'] const nationalHqId = company['National Headquarter ID Number'] const globalHqId = company['Global Headquarter ID Number'] let parentId: string | null = null // Local HQ is normally the parent. If that company is missing, fallback to National, then Global if (localHqId && localHqId !== company.ID && companyMap.has(localHqId)) { parentId = localHqId } else if (nationalHqId && nationalHqId !== company.ID && companyMap.has(nationalHqId)) { parentId = nationalHqId } else if (globalHqId && globalHqId !== company.ID) { parentId = globalHqId } if (parentId) { const parent = companyMap.get(parentId) ?? root parent.subsidiaries.push(node) } }) return root } ``` ### Full Example Putting it all together: ```ts async function getCorporateStructure( apiKey: string, searchTerm: string, maxCompanies = 500 ): Promise { // Step 1: Search for the company const companies = await searchCompany(apiKey, searchTerm) if (!companies.length) { console.log('No companies found') return null } // Step 2: Get full company details const fullCompany = await getCompanyById(apiKey, companies[0].ID) if (!fullCompany) return null // Step 3: Get Global Ultimate ID const globalUltimateId = fullCompany['Global Headquarter ID Number'] if (!globalUltimateId) { // Standalone company with no parent return { company: fullCompany, subsidiaries: [] } } // Step 4: Fetch all companies in the group const allCompanies = await getAllCompaniesInGroup(apiKey, globalUltimateId, maxCompanies) // Step 5: Find or fetch the root company let rootCompany = allCompanies.find(c => c.ID === globalUltimateId) if (!rootCompany) { rootCompany = await getCompanyById(apiKey, globalUltimateId) } if (!rootCompany) return null // Step 6: Build tree by grouping under HQ IDs return buildTreeFromFlatList(allCompanies, rootCompany) } // Usage const tree = await getCorporateStructure('YOUR_API_KEY', 'Shell', 500) ``` ### Display the Tree ```ts function printTree(node: CorporateTreeNode, indent = 0): void { const prefix = ' '.repeat(indent) const company = node.company const location = [company['City'], company['Country Code'] ?? company['Country Name']].filter(Boolean).join(', ') console.log(`${prefix}├── ${company['Company Name']}${location ? ` (${location})` : ''}`) for (const subsidiary of node.subsidiaries) { printTree(subsidiary, indent + 1) } } // Output: // ├── Shell plc (London, GB) // ├── Shell Petroleum N.V. (The Hague, NL) // ├── Shell Nederland B.V. (The Hague, NL) // ├── Shell Deutschland GmbH (Hamburg, DE) // ├── Shell Oil Company (Houston, US) ``` ## Request & Response Examples Complete, copy-pasteable examples for the three-step corporate structure workflow. ### 1. Search for a company ```bash curl -G "https://app.companydata.com/api/company/search" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "search=Shell" \ --data-urlencode "page=1" \ --data-urlencode "pageSize=25" ``` **Response:** ```json { "meta": { "totalCount": 42, "currentPage": 1, "totalPages": 2, "pageSize": 25 }, "data": { "records": [ { "ID": "407809109", "Company Name": "Shell plc", "Country": "United Kingdom", "City": "LONDON" }, { "ID": "407809110", "Company Name": "Shell Nederland B.V.", "Country": "Netherlands", "City": "THE HAGUE" } ] } } ``` ### 2. Export company details (find Global Headquarter ID) ```bash curl -G "https://app.companydata.com/api/company/export" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "export=true" \ --data-urlencode "ID=407809109" ``` **Response:** ```json { "meta": { "totalCount": 1, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "407809109", "Company Name": "Shell plc", "City": "LONDON", "Country": "United Kingdom", "Location Type": "1", "Subsidiary Code": "0", "Global Headquarter ID Number": "407809109", "Global Headquarter Company Name": "Shell plc", "Number of Companies in Group": "2847" } ] } } ``` Copy `Global Headquarter ID Number` (`407809109`) for the next step. ### 3. Fetch all companies in the corporate group ```bash curl -G "https://app.companydata.com/api/company/export" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "export=true" \ --data-urlencode "worldwideHeadquarterID=407809109" \ --data-urlencode "page=1" \ --data-urlencode "pageSize=50" ``` **Response:** ```json { "meta": { "totalCount": 2847, "currentPage": 1, "totalPages": 57, "pageSize": 50 }, "data": { "records": [ { "ID": "407809109", "Company Name": "Shell plc", "City": "LONDON", "Country": "United Kingdom", "Location Type": "1", "Subsidiary Code": "0", "Global Headquarter ID Number": "407809109", "Global Headquarter Company Name": "Shell plc", "Number of Companies in Group": "2847" }, { "ID": "407809110", "Company Name": "Shell Nederland B.V.", "City": "THE HAGUE", "Country": "Netherlands", "Location Type": "3", "Subsidiary Code": "3", "Local Headquarter ID Number": "407809109", "Local Headquarter Company Name": "Shell plc", "National Headquarter ID Number": "407809109", "National Headquarter Company Name": "Shell plc", "Global Headquarter ID Number": "407809109", "Global Headquarter Company Name": "Shell plc", "Number of Companies in Group": "2847" } ] } } ``` Paginate through all pages (or use scroll pagination for large groups) and build a tree using `Local Headquarter ID Number` → `National Headquarter ID Number` → `Global Headquarter ID Number`. See the [Response Fields](https://companydata.com/api-docs/returned-fields/) reference for all hierarchy field definitions. --- Source: https://companydata.com/api-docs/use-cases/lead-generation/ # Sales & Lead Generation Build targeted B2B lead lists using export filters for industry (SIC), employee count, revenue, location, and contact availability (`hasEmail`, `hasPhone`). Export returns the full field set included in your subscription. ## Filter Options The CompanyData API offers extensive filtering capabilities: | Filter | Description | Example | |--------|-------------|---------| | `countryCode` | Country (ISO 2-letter) | `NL`, `US`, `DE` | | `cityName` | City name | `AMSTERDAM`, `NEW YORK` | | `provinceName` | State/Province | `NOORD-HOLLAND` | | `sic4Digits` | Industry code (SIC) | `7371` (Software) | | `employeesTotal[min/max]` | Company size | `10` - `500` | | `annualSales[min/max]` | Revenue (USD) | `1000000` - `10000000` | | `foundingYears[min/max]` | Company age | `2015` - `2023` | | `hasEmail` | Has email address | `true` | | `hasPhone` | Has phone number | `true` | | `hasWebsite` | Has website | `true` | | `hasContactPerson` | Has CEO/contact | `true` | ## TypeScript Implementation ```ts // Lead Generation - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; interface LeadFilters { countryCode?: string; cityName?: string; provinceName?: string; sicCode?: string; employeesMin?: number; employeesMax?: number; revenueMin?: number; revenueMax?: number; foundedAfter?: number; foundedBefore?: number; hasEmail?: boolean; hasPhone?: boolean; hasWebsite?: boolean; hasContactPerson?: boolean; } interface Lead { id: string; companyName: string; address: string; city: string; country: string; phone?: string; email?: string; website?: string; contactName?: string; contactTitle?: string; employees?: number; revenue?: number; industry?: string; sicCode?: string; foundingYear?: number; } interface LeadSearchResult { leads: Lead[]; totalCount: number; page: number; pageSize: number; totalPages: number; } async function fetchApi(endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '' && value !== null) { url.searchParams.append(key, String(value)); } }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } function mapToLead(company: Record): Lead { return { id: company['ID'], companyName: company['Company Name'], address: [company['Address 1'], company['Address 2']].filter(Boolean).join(', '), city: company['City'], country: company['Country Name'], phone: company['Phone Number'], email: company['Email'], website: company['Website'], contactName: company['CEO Name'], contactTitle: company['CEO Title'], employees: company['Employees Total'] ? parseInt(company['Employees Total']) : undefined, revenue: company['Yearly Revenue in U.S. Dollars'] ? parseInt(company['Yearly Revenue in U.S. Dollars']) : undefined, industry: company['Business Category'], sicCode: company['Business Category Code 1'], foundingYear: company['Founding Year'] ? parseInt(company['Founding Year']) : undefined, }; } async function findLeads( filters: LeadFilters, page = 1, pageSize = 25 ): Promise { const params: Record = { page, pageSize, }; // Location filters if (filters.countryCode) params.countryCode = filters.countryCode; if (filters.cityName) params.cityName = filters.cityName; if (filters.provinceName) params.provinceName = filters.provinceName; // Industry filter if (filters.sicCode) params.sic4Digits = filters.sicCode; // Size filters if (filters.employeesMin) params['employeesTotal[min]'] = filters.employeesMin; if (filters.employeesMax) params['employeesTotal[max]'] = filters.employeesMax; // Revenue filters if (filters.revenueMin) params['annualSales[min]'] = filters.revenueMin; if (filters.revenueMax) params['annualSales[max]'] = filters.revenueMax; // Age filters if (filters.foundedAfter) params['foundingYears[min]'] = filters.foundedAfter; if (filters.foundedBefore) params['foundingYears[max]'] = filters.foundedBefore; // Contact info filters if (filters.hasEmail) params.hasEmail = 'true'; if (filters.hasPhone) params.hasPhone = 'true'; if (filters.hasWebsite) params.hasWebsite = 'true'; if (filters.hasContactPerson) params.hasContactPerson = 'true'; const response = await fetchApi<{ meta: { totalCount: number; currentPage: number; totalPages: number; pageSize: number; }; data: { records: Record[]; }; }>('export', params); return { leads: (response.data?.records || []).map(mapToLead), totalCount: response.meta?.totalCount || 0, page: response.meta?.currentPage || page, pageSize: response.meta?.pageSize || pageSize, totalPages: response.meta?.totalPages || 0, }; } // Export all leads matching filters (paginated) async function* exportAllLeads( filters: LeadFilters, pageSize = 100 ): AsyncGenerator { let page = 1; let hasMore = true; while (hasMore) { const result = await findLeads(filters, page, pageSize); if (result.leads.length > 0) { yield result.leads; } hasMore = page < result.totalPages; page++; // Rate limiting if (hasMore) { await new Promise(resolve => setTimeout(resolve, 100)); } } } // Example usage async function main() { // Find software companies in Netherlands with 10-100 employees const filters: LeadFilters = { countryCode: 'NL', sicCode: '7371', // Computer Programming Services employeesMin: 10, employeesMax: 100, hasEmail: true, hasPhone: true, }; console.log('Searching for leads...'); const result = await findLeads(filters, 1, 10); console.log(`Found ${result.totalCount} matching companies\n`); result.leads.forEach((lead, i) => { console.log(`${i + 1}. ${lead.companyName}`); console.log(` ${lead.city}, ${lead.country}`); console.log(` Employees: ${lead.employees || 'N/A'}`); console.log(` Phone: ${lead.phone || 'N/A'}`); console.log(` Email: ${lead.email || 'N/A'}`); console.log(''); }); } main(); ``` ```js // Lead Generation - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; async function fetchApi(endpoint, params) { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '' && value !== null) { url.searchParams.append(key, String(value)); } }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } async function findLeads(filters, page = 1, pageSize = 25) { const params = { page, pageSize }; if (filters.countryCode) params.countryCode = filters.countryCode; if (filters.cityName) params.cityName = filters.cityName; if (filters.sicCode) params.sic4Digits = filters.sicCode; if (filters.employeesMin) params['employeesTotal[min]'] = filters.employeesMin; if (filters.employeesMax) params['employeesTotal[max]'] = filters.employeesMax; if (filters.revenueMin) params['annualSales[min]'] = filters.revenueMin; if (filters.hasEmail) params.hasEmail = 'true'; if (filters.hasPhone) params.hasPhone = 'true'; if (filters.hasContactPerson) params.hasContactPerson = 'true'; const response = await fetchApi('export', params); return { leads: response.data?.records || [], totalCount: response.meta?.totalCount || 0, page: response.meta?.currentPage || page, totalPages: response.meta?.totalPages || 0, }; } // Example: Find tech companies in Amsterdam findLeads({ countryCode: 'NL', cityName: 'AMSTERDAM', sicCode: '7371', employeesMin: 10, hasEmail: true, }).then(result => { console.log(`Found ${result.totalCount} leads:`); result.leads.forEach(company => { console.log(`- ${company['Company Name']} (${company['Employees Total']} employees)`); }); }); ``` ## Example Response Request: `GET /api/company/export?export=true&countryCode=NL&cityName=AMSTERDAM&sic4Digits=7371&hasEmail=true&page=1&pageSize=25` ```json { "meta": { "totalCount": 1250, "currentPage": 1, "totalPages": 50, "pageSize": 25 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "City": "AMSTERDAM", "Country": "Netherlands", "Business Category Code 1": "7371", "Employees Total": "50", "Email": "info@companydata.com", "Phone Number": "+31201234567" } ] } } ``` ## Common SIC Codes Here are frequently used industry codes: | SIC Code | Industry | |----------|----------| | 7371 | Computer Programming Services | | 7372 | Prepackaged Software | | 7373 | Computer Integrated Systems Design | | 7374 | Computer Processing & Data Prep | | 8742 | Management Consulting Services | | 6411 | Insurance Agents & Brokers | | 6021 | National Commercial Banks | | 5812 | Eating Places (Restaurants) | | 8011 | Offices & Clinics of Doctors | | 8111 | Legal Services | | 1521 | General Contractors (Residential) | | 5045 | Computers & Software Wholesale | | 7011 | Hotels & Motels | | 4512 | Air Transportation | > For a complete list of SIC codes, see the [U.S. Department of Labor SIC Manual](https://www.osha.gov/data/sic-manual) or contact support. ## Building a Lead Scoring System Combine CompanyData filters with custom scoring: ```ts interface ScoredLead extends Lead { score: number; scoreBreakdown: Record; } function scoreLead(lead: Lead, idealProfile: { minEmployees?: number; maxEmployees?: number; minRevenue?: number; preferredCountries?: string[]; requireEmail?: boolean; requirePhone?: boolean; }): ScoredLead { let score = 0; const breakdown: Record = {}; // Size score (0-30 points) if (lead.employees) { const inRange = ( (!idealProfile.minEmployees || lead.employees >= idealProfile.minEmployees) && (!idealProfile.maxEmployees || lead.employees <= idealProfile.maxEmployees) ); breakdown.size = inRange ? 30 : 15; score += breakdown.size; } // Revenue score (0-25 points) if (lead.revenue && idealProfile.minRevenue) { breakdown.revenue = lead.revenue >= idealProfile.minRevenue ? 25 : 10; score += breakdown.revenue; } // Location score (0-15 points) if (idealProfile.preferredCountries?.includes(lead.country)) { breakdown.location = 15; score += 15; } // Contact info score (0-30 points) if (idealProfile.requireEmail && lead.email) { breakdown.email = 15; score += 15; } if (idealProfile.requirePhone && lead.phone) { breakdown.phone = 15; score += 15; } return { ...lead, score, scoreBreakdown: breakdown }; } ``` ## Best Practices ### Filtering Strategy - **Start broad, then narrow** - Begin with location + industry, add filters as needed - **Use `has*` filters** - Ensure leads have contactable information - **Combine filters logically** - Employee count + revenue often correlates ### Data Export - **Paginate large exports** - Use async generators for memory efficiency - **Respect rate limits** - Add delays between API calls - **Cache results** - Store leads to avoid re-fetching ### Lead Quality - **Verify contact info** - Email addresses may need validation - **Check for duplicates** - Same company may appear with different addresses - **Update regularly** - Company data changes; refresh lead lists periodically --- Source: https://companydata.com/api-docs/use-cases/due-diligence/ # Due Diligence & KYC Verify company existence, registration, ownership, and legitimacy for onboarding, supplier checks, and compliance. Export a full company record by ID or search by name and registration number. ## Use Cases - **Customer onboarding** - Verify business customers during signup - **Supplier verification** - Validate vendors before partnerships - **Investment due diligence** - Research target companies - **Compliance checks** - Meet KYC/AML regulatory requirements - **Credit risk assessment** - Evaluate company stability ## What You Can Verify | Check | Data Point | API Field | |-------|-----------|-----------| | Company exists | Found in database | Search results | | Registration number | Official company ID | `Company Registration Number` | | Physical address | Verified location | `Address 1`, `City`, `Country` | | Years in business | Founded date | `Founding Year` | | Company size | Employee count | `Employees Total` | | Legal structure | Entity type | `Business Legal Type` | | Corporate ownership | Parent company | `Global Headquarter Company Name` | | Subsidiary status | Ownership flag | `Subsidiary Code` | ## TypeScript Implementation ```ts // Due Diligence Verification - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; interface VerificationResult { verified: boolean; companyId?: string; companyName?: string; checks: { exists: boolean; hasRegistration: boolean; hasAddress: boolean; hasPhone: boolean; yearsInBusiness?: number; employeeCount?: number; legalStatus?: string; isSubsidiary: boolean; parentCompany?: string; }; rawData?: Record; } async function fetchApi(endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value) url.searchParams.append(key, String(value)); }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Verify by company name and country async function verifyByName( companyName: string, countryCode?: string ): Promise { const params: Record = { search: companyName, page: 1, pageSize: 1, }; if (countryCode) params.countryCode = countryCode; const searchResponse = await fetchApi<{ data: { records: any[] } }>('search', params); if (!searchResponse.data?.records?.length) { return { verified: false, checks: { exists: false, hasRegistration: false, hasAddress: false, hasPhone: false, isSubsidiary: false, }, }; } // Get full details return verifyById(searchResponse.data.records[0].ID); } // Verify by registration number async function verifyByRegistration( registrationNumber: string, countryCode: string ): Promise { const params: Record = { nationalId: registrationNumber, countryCode: countryCode, page: 1, pageSize: 1, }; const searchResponse = await fetchApi<{ data: { records: any[] } }>('search', params); if (!searchResponse.data?.records?.length) { return { verified: false, checks: { exists: false, hasRegistration: false, hasAddress: false, hasPhone: false, isSubsidiary: false, }, }; } return verifyById(searchResponse.data.records[0].ID); } // Verify by CompanyData ID async function verifyById(companyId: string): Promise { const response = await fetchApi<{ data: { records: any[] } }>('export', { ID: companyId, page: 1, pageSize: 1, }); if (!response.data?.records?.length) { return { verified: false, checks: { exists: false, hasRegistration: false, hasAddress: false, hasPhone: false, isSubsidiary: false, }, }; } const company = response.data.records[0]; const currentYear = new Date().getFullYear(); const foundingYear = company['Founding Year'] ? parseInt(company['Founding Year']) : undefined; const checks = { exists: true, hasRegistration: !!company['Company Registration Number'], hasAddress: !!(company['Address 1'] && company['City']), hasPhone: !!company['Phone Number'], yearsInBusiness: foundingYear ? currentYear - foundingYear : undefined, employeeCount: company['Employees Total'] ? parseInt(company['Employees Total']) : undefined, legalStatus: getLegalStatusText(company['Business Legal Type']), isSubsidiary: company['Subsidiary Code'] === '3', parentCompany: company['Global Headquarter Company Name'] || undefined, }; // Company is "verified" if it exists and has basic information const verified = checks.exists && (checks.hasRegistration || checks.hasAddress); return { verified, companyId: company['ID'], companyName: company['Company Name'], checks, rawData: company, }; } function getLegalStatusText(code: string): string | undefined { const statuses: Record = { '1': 'Proprietorship', '2': 'Partnership', '3': 'Corporation', '7': 'Partnership', '8': 'Joint Venture', '9': 'Master Limited Partnership', '10': 'General Partnership', '11': 'Limited Partnership', '13': 'Proprietorship', '14': 'Limited Liability', }; return statuses[code]; } // Example usage async function main() { console.log('=== Verification by Name ==='); const byName = await verifyByName('Philips', 'NL'); console.log(`Verified: ${byName.verified}`); console.log(`Company: ${byName.companyName}`); console.log('Checks:', byName.checks); console.log('\n=== Verification by Registration ==='); const byReg = await verifyByRegistration('33011433', 'NL'); console.log(`Verified: ${byReg.verified}`); console.log(`Company: ${byReg.companyName}`); console.log('\n=== Verification by ID ==='); const byId = await verifyById('407809109'); console.log(`Verified: ${byId.verified}`); console.log('Checks:', byId.checks); } main(); ``` ```js // Due Diligence Verification - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api/company'; async function fetchApi(endpoint, params) { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value) url.searchParams.append(key, String(value)); }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } async function verifyCompany(companyName, countryCode) { // Search for company const searchParams = { search: companyName, page: 1, pageSize: 1 }; if (countryCode) searchParams.countryCode = countryCode; const searchResponse = await fetchApi('search', searchParams); if (!searchResponse.data?.records?.length) { return { verified: false, reason: 'Company not found' }; } // Get full details const company = searchResponse.data.records[0]; const exportResponse = await fetchApi('export', { ID: company.ID, page: 1, pageSize: 1 }); if (!exportResponse.data?.records?.length) { return { verified: false, reason: 'Could not retrieve details' }; } const details = exportResponse.data.records[0]; const currentYear = new Date().getFullYear(); return { verified: true, companyId: details['ID'], companyName: details['Company Name'], checks: { exists: true, hasRegistration: !!details['Company Registration Number'], registrationNumber: details['Company Registration Number'], hasAddress: !!(details['Address 1'] && details['City']), address: `${details['Address 1']}, ${details['City']}, ${details['Country Name']}`, hasPhone: !!details['Phone Number'], phone: details['Phone Number'], foundingYear: details['Founding Year'], yearsInBusiness: details['Founding Year'] ? currentYear - parseInt(details['Founding Year']) : null, employees: details['Employees Total'], isSubsidiary: details['Subsidiary Code'] === '3', parentCompany: details['Global Headquarter Company Name'], }, }; } // Example verifyCompany('Shell', 'NL').then(result => { console.log('Verification result:', JSON.stringify(result, null, 2)); }); ``` ## Example Response Request: `GET /api/company/export?export=true&ID=407809109` ```json { "meta": { "totalCount": 1, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "407809109", "Company Name": "Shell plc", "Company Registration Number": "00102156", "Address 1": "Shell Centre", "City": "LONDON", "Country": "United Kingdom", "Phone Number": "+442079347000", "Founding Year": "1907", "Employees Total": "93000", "Subsidiary Code": "0", "Global Headquarter Company Name": "Shell plc", "Business Legal Type": "3" } ] } } ``` ## Verification Workflow A typical KYC/due diligence workflow: ```ts async function performDueDiligence(input: { companyName?: string; registrationNumber?: string; countryCode?: string; }): Promise<{ passed: boolean; score: number; flags: string[]; details: VerificationResult; }> { let result: VerificationResult; // Try verification by registration number first (most reliable) if (input.registrationNumber && input.countryCode) { result = await verifyByRegistration(input.registrationNumber, input.countryCode); } else if (input.companyName) { result = await verifyByName(input.companyName, input.countryCode); } else { throw new Error('Provide either company name or registration number'); } // Calculate verification score (0-100) let score = 0; const flags: string[] = []; if (result.checks.exists) score += 20; else flags.push('Company not found in database'); if (result.checks.hasRegistration) score += 25; else flags.push('No registration number on file'); if (result.checks.hasAddress) score += 15; else flags.push('Incomplete address information'); if (result.checks.hasPhone) score += 10; if (result.checks.yearsInBusiness && result.checks.yearsInBusiness >= 2) { score += 15; } else { flags.push('Company less than 2 years old'); } if (result.checks.employeeCount && result.checks.employeeCount >= 5) { score += 15; } // Determine pass/fail (threshold: 60) const passed = score >= 60; return { passed, score, flags, details: result }; } ``` ## Best Practices ### Data Quality - **Use registration numbers** when available - most reliable identifier - **Include country code** - prevents false matches in wrong countries - **Cross-reference multiple fields** - don't rely on a single check ### Compliance - **Log all checks** - maintain audit trail for compliance - **Document decisions** - record why verification passed/failed - **Regular re-verification** - company status can change over time - **Handle edge cases** - new companies may have limited data ### Risk Assessment Consider these factors when assessing risk: | Factor | Low Risk | Medium Risk | High Risk | |--------|----------|-------------|-----------| | Years in business | 10+ years | 2-10 years | < 2 years | | Employee count | 50+ | 10-50 | < 10 | | Registration verified | Yes | Partial | No | | Physical address | Verified | Unverified | Missing | | Parent company | Known entity | Unknown | Shell company indicators | --- Source: https://companydata.com/api-docs/use-cases/address-search/ # Address Search Find companies at a specific address by combining the `/api/cities` lookup with search filters (`cityName`, `streetName`, `postalCode`). Street names support prefix matching; city and postal code require exact values from the database. ## How It Works 1. **Look up a city** - Call the `/api/cities` endpoint with a search term to find the exact city name 2. **Build your address filter** - Combine the city name with optional street name and postal code 3. **Search for companies** - Call `/api/company/search` with your address filters to find matching companies 4. **Paginate results** - Use `page` and `pageSize` to navigate through large result sets ## TypeScript Implementation ```ts // Address Search - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api'; interface City { cityName: string; } interface CompanyResult { ID: string; 'Company Name': string; 'Address 1'?: string; 'City'?: string; 'Postal Code'?: string; 'Country Name'?: string; [key: string]: any; } interface SearchResponse { meta: { totalCount: number; currentPage: number; totalPages: number; pageSize: number; }; data: { records: CompanyResult[]; }; } interface CityResponse { data: City[]; } async function fetchApi(endpoint: string, params: Record): Promise { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '' && value !== null) { url.searchParams.append(key, String(value)); } }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Step 1: Look up cities by name async function lookupCities(search: string, countryCode?: string): Promise { if (search.length < 2) return []; const params: Record = { search }; if (countryCode) params.countries = countryCode; const response = await fetchApi('cities', params); return response.data || []; } // Step 2: Search companies by address async function searchByAddress(options: { countryCode: string; cityName?: string; streetName?: string; postalCode?: string; page?: number; pageSize?: number; }): Promise<{ records: CompanyResult[]; totalCount: number }> { const params: Record = { countryCode: options.countryCode, statusCode: '0,1', page: options.page || 1, pageSize: options.pageSize || 25, }; if (options.cityName) params.cityName = options.cityName; if (options.streetName) params.streetName = options.streetName; if (options.postalCode) params.postalCode = options.postalCode; const response = await fetchApi('company/search', params); return { records: response.data?.records || [], totalCount: response.meta?.totalCount || 0, }; } // Debounce helper for city autocomplete function debounce any>( func: T, wait: number ): (...args: Parameters) => void { let timeout: ReturnType; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } // Example usage: Find companies on Keizersgracht in Amsterdam async function main() { // Step 1: Look up the city const cities = await lookupCities('Amsterdam', 'NL'); console.log('Matching cities:', cities.map(c => c.cityName)); const city = cities[0]; // Pick the first match if (!city) { console.log('City not found'); return; } // Step 2: Search by address const result = await searchByAddress({ countryCode: 'NL', cityName: city.cityName, streetName: 'Keizersgracht', }); console.log(`\nFound ${result.totalCount} companies on Keizersgracht, ${city.cityName}:\n`); result.records.forEach((company, i) => { console.log(`${i + 1}. ${company['Company Name']}`); console.log(` ${company['Address 1']}`); console.log(` ${company['Postal Code']} ${company['City']}`); console.log(''); }); // Step 3: Paginate through all results if (result.totalCount > 25) { const page2 = await searchByAddress({ countryCode: 'NL', cityName: city.cityName, streetName: 'Keizersgracht', page: 2, }); console.log(`Page 2: ${page2.records.length} more companies`); } } main(); ``` ```js // Address Search - works in Node.js 18+ and browsers const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://app.companydata.com/api'; async function fetchApi(endpoint, params) { const url = new URL(`${BASE_URL}/${endpoint}`); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '' && value !== null) { url.searchParams.append(key, String(value)); } }); const response = await fetch(url.toString(), { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } return response.json(); } // Step 1: Look up cities by name async function lookupCities(search, countryCode) { if (search.length < 2) return []; const params = { search }; if (countryCode) params.countries = countryCode; const response = await fetchApi('cities', params); return response.data || []; } // Step 2: Search companies by address async function searchByAddress(options) { const params = { countryCode: options.countryCode, statusCode: '0,1', page: options.page || 1, pageSize: options.pageSize || 25, }; if (options.cityName) params.cityName = options.cityName; if (options.streetName) params.streetName = options.streetName; if (options.postalCode) params.postalCode = options.postalCode; const response = await fetchApi('company/search', params); return { records: response.data?.records || [], totalCount: response.meta?.totalCount || 0, }; } // Example: Find companies on Keizersgracht in Amsterdam async function main() { // Look up the city const cities = await lookupCities('Amsterdam', 'NL'); const city = cities[0]; if (!city) { console.log('City not found'); return; } // Search by address const result = await searchByAddress({ countryCode: 'NL', cityName: city.cityName, streetName: 'Keizersgracht', }); console.log(`Found ${result.totalCount} companies:`); result.records.forEach(company => { console.log(`- ${company['Company Name']} | ${company['Address 1']}, ${company['Postal Code']} ${company['City']}`); }); } main(); ``` ## Example Response Request: `GET /api/company/search?countryCode=NL&cityName=AMSTERDAM&streetName=Keizersgracht&statusCode=0,1` ```json { "meta": { "totalCount": 847, "currentPage": 1, "totalPages": 34, "pageSize": 25 }, "data": { "records": [ { "ID": "123456789", "Company Name": "Example B.V.", "Address 1": "Keizersgracht 424", "Postal Code": "1016 GC", "City": "AMSTERDAM", "Country": "Netherlands" } ] } } ``` ## Best Practices ### City Lookup - **Debounce the input** - Wait 300ms after the user stops typing before calling `/api/cities` - **Specify the country** - Use the `countries` parameter to narrow city results to the relevant country - **Use exact city names** - Select from the autocomplete results to ensure exact matches in the company search ### Address Filtering - **Combine filters gradually** - Start with just a city, then add street or postal code to narrow results - **Street names use partial matching** - You don't need the full street name; "Keizers" will match "Keizersgracht" - **Postal codes use exact matching** - Ensure the format matches the country's postal code format ### Pagination - **Use reasonable page sizes** - 10-25 results per page works well for most UIs - **Show total count** - Display the total number of matching companies so users know the scope - **Add rate limiting for bulk exports** - Add delays between pages when exporting large result sets --- Source: https://companydata.com/api-docs/use-cases/matching-a-list/ # Matching a List of Companies You have a spreadsheet of companies — customers, suppliers, prospects, a portfolio — and you want the registered entity behind each row. This guide covers what to send per row, what to do when a row comes back empty, and how to tell "we do not have this company" apart from "the query did not describe it". ## Match on the right field Each row usually carries more than a name. Pick the most specific thing you have, because they do not search the same field: | What you have | Parameter | Notes | | --- | --- | --- | | Registration number | `nationalId` | The strongest match. Combine with `countryCode`. | | Website or email domain | `website` | Matches with or without `www.` and with or without a scheme. | | A CompanyData ID from an earlier call | `ID` | Exact, and free of ambiguity. | | Only a name | `search` | Combine with `countryCode` when you are confident of the country. | A value in the wrong parameter does not fail — it just looks for the wrong thing. A domain in `search` looks for a company whose *name* is that domain, which is why `search=aselsan.com` finds nothing while `website=aselsan.com` finds ASELSAN. If a `search` value is unmistakably a domain or a registration number and no company carries it as a name, the API retries it against the right field for you and says so in `meta.warnings`; sending it in the right parameter to begin with is one request instead of two. ## How name matching behaves `search` matches from the start of the stored company name. A leading word or two is enough — `Heineken` finds `Heineken N.V.` — but everything you do send has to match: - **Word order matters.** `QA Wolf` does not find `Wolf Qa Inc`. - **Inserted words matter.** `Lurica Argentina` does not find `LURICA CORP ARGENTINA S.A.` - **Accents matter.** `Telefonica` does not find `Telefónica`. - **Legal forms are interchangeable** for common cases (`BV` / `B.V.`, `GmbH`, `Ltd` / `Limited`), but an unusual suffix is treated as part of the name: `Younited SA` does not find `YOUNITED`. - **Names are stored in the Latin alphabet.** A search in Greek or Cyrillic will not match. When the exact search finds nothing, the API retries on its own before answering, in this order, and tells you which step produced the rows: 1. All the words, in any order — `QA Wolf` → `Wolf Qa Inc`. 2. The name without its legal form — `Randstad France SASU` → `RANDSTAD FRANCE`. 3. *(API keys only)* The closest spelling — `Telefonica Germany` → `Telefónica Germany`. 4. *(API keys only)* The same name in another country — `Thamesis Consulting` + `IT` → `THAMESIS CONSULTING LIMITED` in GB. Each step is only accepted while its result set is small, so a common word cannot pass for a match, and steps 3 and 4 are skipped for names too short to be distinctive. The rows are real, but a relaxed match is a candidate, not a confirmation — check it (see below) before you use it. ## What to do per row For each row, stop at the first step that returns something: 1. `nationalId` + `countryCode`, if you have a registration number. 2. `website`, if you have a domain. 3. `search` with the name + `countryCode`. The API's own retries cover word order, legal forms, spelling and — for API keys — other countries. 4. If it is still empty, `search` with the first two or three distinctive words, then the same **without** `countryCode`. A UK subsidiary is often registered as a branch of its foreign parent: `Hoist Finance UK Limited` is on the register as `Hoist Finance AB (publ)` with a UK address. Anything that arrived with a `SEARCH_MATCHED_*` warning, or from step 4, widened the search, so check it before you accept it. `Location Type` and the headquarter fields tell you whether you have the entity itself, its parent, or a branch — see [Corporate Structure](https://companydata.com/api-docs/use-cases/corporate-structure/). ## Read meta.warnings A successful response can carry a `meta.warnings` array. It never means the request failed; the rows in `data.records` are always real results. It means something about the request is worth knowing: ```json { "meta": { "totalCount": 1, "currentPage": 1, "totalPages": 1, "pageSize": 25, "warnings": [ { "code": "SEARCH_LOOKS_LIKE_DOMAIN", "parameter": "search", "message": "No company is named \"aselsan.com\", so the website field was searched for \"aselsan.com\" instead.", "hint": "Use website=aselsan.com to search websites directly." } ] }, "data": { "records": [ { "Company Name": "ASELSAN ELEKTRONIK SANAYI VE TICARET A S" } ] } } ``` Worth acting on when you see them across many rows: | Code | What it means | | --- | --- | | `COUNTRY_CODE_NORMALISED` | You sent a country name or a lowercase code; it was read as the ISO code. | | `COUNTRY_CODE_UNRECOGNISED` | The value is not a country code, so nothing can match it. The filter is kept rather than dropped, so the count stays honest. | | `NATIONAL_ID_NORMALISED` | Separators were stripped to match the stored form. | | `SEARCH_LOOKS_LIKE_DOMAIN` | No company has that name; `website` was searched instead. | | `SEARCH_LOOKS_LIKE_REGISTRATION_NUMBER` | No company has that name; `nationalId` was searched instead. | | `SEARCH_NON_LATIN_SCRIPT` | Names are stored in the Latin alphabet. | | `SEARCH_MATCHED_ANY_ORDER` | Exact search found nothing; these rows contain all the words in some order. | | `SEARCH_MATCHED_CORE_NAME` | Exact search found nothing; these rows match the name with the legal form left off. | | `SEARCH_MATCHED_FUZZY` | Exact search found nothing; these are the closest spellings. Verify before use. | | `SEARCH_MATCHED_OTHER_COUNTRY` | Nothing matched in the country you gave; these rows are registered elsewhere. | | `PARAMETER_NOT_APPLIED` | The parameter is accepted but does not filter anything on this index. | ## Check your parameter names once A parameter name this endpoint does not have is refused with `400`, rather than ignored, because a dropped filter returns the whole database as an apparently valid answer: ```json { "error": "Unknown parameter", "message": "This endpoint does not have the parameter(s): query.", "unknownParameters": ["query"], "didYouMean": { "query": "search" }, "documentation": "https://companydata.com/api-docs/search" } ``` Run one request by hand before you start a batch. The name of the company field is `search`, not `query` or `companyName`; employee ranges are `employeesTotalMin` / `employeesTotalMax`; the country filter is `countryCode` and takes ISO 3166-1 alpha-2 codes. ## Reading a zero honestly After the ladder above, an empty result is a real answer: we do not have that company. That happens most often with very small or very new businesses outside Western Europe, and with entities registered abroad under a different name. It is worth separating those rows out rather than retrying them — and if a whole segment of your list comes back empty, [tell us](mailto:info@companydata.com) which segment, because that is coverage feedback we can act on. --- Source: https://companydata.com/api-docs/integrations/hubspot/ # HubSpot CRM integration **CompanyData Enrichment** is a HubSpot app that enriches **company** records with verified data from official government trade registers (400M+ businesses across 200+ countries). Install from the HubSpot App Marketplace; no API key is required in the card. Your HubSpot portal is linked to a CompanyData account automatically when you complete OAuth during install. > This guide covers the HubSpot app. To build your own enrichment flow with the REST API, see the [Quickstart](https://companydata.com/api-docs/quickstart/) or [Use cases](https://companydata.com/api-docs/use-cases/). ## Install from HubSpot 1. In HubSpot, open **Marketplace** and search for **CompanyData Enrichment**. 2. Click **Install app** and choose the HubSpot account (portal) where you want enrichment. 3. Review and approve the requested permissions: - Read and write **companies** - Read and write **contacts** (used for CRM context during install) 4. Finish OAuth. CompanyData provisions a trial account for your portal. No payment card is required at install. After install, open **Settings → Connected apps** and confirm **CompanyData Enrichment** is listed. ## Add the enrichment card The enrichment UI lives on the company record as a CRM card. Add it once per portal (or per team layout if you use multiple record views). 1. Open any **company** record in HubSpot. 2. Click **Customize** (top of the record). 3. Open **Card library**. 4. Find **CompanyData Enrichment** and add it to the **middle** record tab (the main content area between the left sidebar and the right **Catch-up** / **Activities** panel). 5. Save the layout. > If you do not see enrichment controls, the card may be on the right sidebar instead of the middle tab. Move it to the middle tab for the intended layout. ## Enrich a company 1. Open a company record that has at least a **company name**. Additional fields (domain, website, address, city, state, zip, country, phone) improve match quality. 2. In the **CompanyData Enrichment** card, click **Enrich**. 3. If several legal entities match, choose the correct one from the dropdown. Each option shows the company name with **country** and **registration number** when available. 4. Review the preview (name, registration, legal type, employees, revenue, industry, status, website, location, and more). 5. Click **Apply to record** to write the selected data into HubSpot company properties. Example enriched fields returned before apply (shape varies by country and register): ```json { "name": "Heineken N.V.", "registrationNumber": "33011433", "legalType": "Naamloze Vennootschap", "country": "Netherlands", "city": "AMSTERDAM", "numberofemployees": "10000", "annualrevenue": "25000000000", "website": "heineken.com", "status": "Active" } ``` Each successful **Apply** uses one enrichment credit. Searching for matches counts toward your monthly company search limit. ## Fields updated on apply When you apply enrichment, CompanyData updates standard HubSpot company properties where data is available, including: | HubSpot property | Source data | |------------------|-------------| | `name` | Legal / registered company name | | `website` | Company website | | `domain` | Domain derived from website | | `phone` | Phone number | | `address`, `city`, `state`, `zip`, `country` | Registered address | | `founded_year` | Year founded | | `numberofemployees` | Employee count | | `annualrevenue` | Annual revenue (USD) | | `description` | Industry / activity description | Registration number and legal entity type are written when your portal has the corresponding custom properties configured. ## Trial and subscription New installs include a **7-day trial** with: - **10 enrichment credits** (each **Apply to record** uses one credit) - **500 company searches per month** (each **Enrich** action counts as a search) No payment card is required to start the trial. When the trial ends, credits are exhausted, or the monthly search limit is reached, the card shows a message and a **Subscribe** button. Subscribe opens CompanyData checkout in your browser (outside HubSpot) so you can choose a plan and complete payment securely. | Situation | What you see | |-----------|----------------| | Trial period ended | Trial ended. Subscribe to continue. | | No credits left | Trial credits used. Subscribe to continue enriching. | | Monthly search limit reached | Monthly search limit reached. Upgrade to continue. | ## App settings Open **Settings → Connected apps → CompanyData Enrichment** in HubSpot to see: - Credits used and remaining - Searches used and monthly limit - Days remaining in the trial (while active) - A **Subscribe** link when you need a paid plan You do not enter an API key on this screen. The app uses the CompanyData account linked to your portal at install time. ## Troubleshooting **Connection or authorization errors** Reinstall **CompanyData Enrichment** from the HubSpot Marketplace and complete OAuth again so your portal is re-linked to CompanyData. **Enrich returns no matches** Ensure the company record has a recognizable **name**. Add **country**, **domain**, or **address** fields to narrow results. **Wrong company applied** Run **Enrich** again and pick a different match from the dropdown. Check country and registration number before applying. **Card missing on company records** Re-add the card via **Customize → Card library** on a company record (see [Add the enrichment card](#add-the-enrichment-card)). **Need help** Contact [CompanyData support](https://companydata.com/contact/) or email [support@companydata.com](mailto:support@companydata.com). ## Related links --- Source: https://companydata.com/api-docs/disclaimer/ # Data Accuracy & Usage Disclaimer *Last updated: December 2024* At CompanyData ("we", "us", or "our"), we work hard to collect, process, and provide high-quality company information through our products and APIs, including the services available at /api-docs/ (the "Service"). However, it is important to understand the limitations of this data. ## No Guarantee of Accuracy, Completeness, or Timeliness We strive to ensure that the information we provide is accurate, complete, and up to date. Despite these efforts: - Data may be incomplete, outdated, or incorrect. - Fields and field definitions may change without prior notice. - Certain records or attributes may be missing, duplicated, or inconsistent. All data is provided on an "as is" and "as available" basis, without any warranties of any kind, whether express or implied, including but not limited to accuracy, completeness, timeliness, merchantability, fitness for a particular purpose, or non-infringement. ## No Professional or Legal Advice The information returned by our APIs, shown in our dashboard, or documented on /api-docs/ does not constitute legal, financial, tax, or other professional advice. You are solely responsible for how you use the data and for any decisions made based on it. For any legal, financial, or compliance-related matters, you should consult appropriate qualified professionals and/or official sources. ## Third-Party Sources Some of our data may originate from third-party providers or publicly available sources. We do not control and are not responsible for the accuracy, completeness, or availability of information supplied by these third parties. Any references to third-party services, data, or links do not constitute an endorsement. ## Your Responsibility You are responsible for: - Validating and verifying data before relying on it. - Ensuring that your use of the data complies with all applicable laws, regulations, and contracts. - Implementing your own safeguards, checks, and business logic to handle errors, missing values, and changing fields. ## Limitation of Liability To the maximum extent permitted by applicable law, CompanyData and its affiliates, officers, employees, and agents shall not be liable for any direct, indirect, incidental, special, consequential, or exemplary damages arising from or in connection with your use of, or inability to use, the data or the Service, even if we have been advised of the possibility of such damages. ## Changes to This Disclaimer We may update this Data Disclaimer from time to time. When we do, we will revise the "Last updated" date above. Your continued use of the Service after any changes are made constitutes your acceptance of the updated disclaimer. --- Source: https://companydata.com/openapi.json ## OpenAPI 3.0 Specification ```json { "openapi": "3.0.3", "info": { "title": "CompanyData API", "version": "1.0.0", "description": "CompanyData API: search and export companies worldwide, plus lookup helpers for exact location names.\n\n**Corporate hierarchies**: export records include Local, National, and Global Headquarter fields. Pass a company's `Global Headquarter ID Number` as `worldwideHeadquarterID` on `/api/company/export` to retrieve every entity in the corporate group. Guide: https://companydata.com/api-docs/use-cases/corporate-structure/\n\n**Authentication**: send your API key in the `x-api-key` header on every request.\n**Rate limit**: 50 requests per second (default). **Monthly quota**: 100,000 requests unless agreed otherwise with support.\n\n**MCP server (AI assistants)**: open-source stdio server for Cursor and Claude Desktop at https://companydata.com/product/mcp-server/ (`company_search`, `company_enrich`, `field_lookup`). Repository: https://github.com/companydatacom/companydata-mcp-server\n\nHuman-readable documentation: https://companydata.com/api-docs/ (see also returned-fields and pagination guides on the docs site).", "contact": { "name": "CompanyData", "url": "https://companydata.com/api-docs/" }, "license": { "name": "Commercial", "url": "https://companydata.com/company-data-api/" } }, "servers": [ { "url": "https://app.companydata.com", "description": "Production" } ], "tags": [ { "name": "Company", "description": "Company search and export" }, { "name": "Corporate Structure", "description": "Parent-subsidiary relationships and worldwide corporate groups via HQ ID fields and the worldwideHeadquarterID filter. Guide: https://companydata.com/api-docs/use-cases/corporate-structure/" }, { "name": "Domain Enrichment", "description": "Domain enrichment: download a public web page and extract the registration and VAT numbers and legal names that identify the company behind it via GET /api/website/html, then resolve them with company search. Guide: https://companydata.com/api-docs/domain-enrichment/" }, { "name": "Lookup", "description": "City name autocomplete via GET /api/cities. Province and region names are exact-match filters on search/export (provinceName, regionName); use values as stored in the database." } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/api/company/search": { "get": { "operationId": "companySearch", "tags": [ "Company" ], "summary": "Company search", "description": "Search companies by name, address, and filters. Returns a lightweight default field set; additional search fields may be available per agreement with support. Uses **standard pagination only** (no scroll). Search defaults: pageSize 25 (max 50), maximum **50 total records** retrievable across pages.\n\n**Errors**: every failure is signalled by the HTTP status code (400, 401, 403, 405, 429, 500, 503) with a JSON body carrying an `error` string; the only exception is the empty-query 400, which reuses the list envelope with `meta.totalCount` = -1. No failure is ever reported inside a 200 response.", "parameters": [ { "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/pageSize" }, { "$ref": "#/components/parameters/ID" }, { "$ref": "#/components/parameters/search" }, { "$ref": "#/components/parameters/sic4Digits" }, { "$ref": "#/components/parameters/importExportCode" }, { "$ref": "#/components/parameters/legalStatus" }, { "$ref": "#/components/parameters/statusCode" }, { "$ref": "#/components/parameters/subsidiaryCode" }, { "$ref": "#/components/parameters/countryCode" }, { "$ref": "#/components/parameters/provinceName" }, { "$ref": "#/components/parameters/cityName" }, { "$ref": "#/components/parameters/streetName" }, { "$ref": "#/components/parameters/regionName" }, { "$ref": "#/components/parameters/countryName" }, { "$ref": "#/components/parameters/postalCode" }, { "$ref": "#/components/parameters/postalCodeIntegerMin" }, { "$ref": "#/components/parameters/postalCodeIntegerMax" }, { "$ref": "#/components/parameters/employeesHereMin" }, { "$ref": "#/components/parameters/employeesHereMax" }, { "$ref": "#/components/parameters/employeesTotalMin" }, { "$ref": "#/components/parameters/employeesTotalMax" }, { "$ref": "#/components/parameters/annualSalesMin" }, { "$ref": "#/components/parameters/annualSalesMax" }, { "$ref": "#/components/parameters/foundingYearsMin" }, { "$ref": "#/components/parameters/foundingYearsMax" }, { "$ref": "#/components/parameters/phoneNumber" }, { "$ref": "#/components/parameters/email" }, { "$ref": "#/components/parameters/worldwideHeadquarterID" }, { "$ref": "#/components/parameters/nationalId" }, { "$ref": "#/components/parameters/website" }, { "$ref": "#/components/parameters/hasEmail" }, { "$ref": "#/components/parameters/hasMarketability" }, { "$ref": "#/components/parameters/hasPhone" }, { "$ref": "#/components/parameters/hasWebsite" }, { "$ref": "#/components/parameters/hasNationalID" }, { "$ref": "#/components/parameters/hasCEOName" } ], "responses": { "200": { "description": "Standard pagination response. Records are under data.records; meta.totalCount is the full result set size and meta.currentPage / meta.totalPages / meta.pageSize describe the page.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompanySearchResponse" }, "example": { "meta": { "totalCount": 3, "currentPage": 1, "totalPages": 1, "pageSize": 25 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Country": "Netherlands", "City": "AMSTERDAM" } ] }, "visitor": null } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SearchForbidden" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "429": { "$ref": "#/components/responses/SearchTooManyRequests" }, "500": { "$ref": "#/components/responses/SearchInternalError" }, "503": { "$ref": "#/components/responses/SearchUnavailable" } }, "x-codeSamples": [ { "lang": "curl", "label": "Search companies in the Netherlands", "source": "curl -G \"https://app.companydata.com/api/company/search\" \\\n -H \"x-api-key: YOUR_API_KEY\" \\\n --data-urlencode \"countryCode=NL\" \\\n --data-urlencode \"search=Heineken\" \\\n --data-urlencode \"page=1\" \\\n --data-urlencode \"pageSize=25\"" } ] } }, "/api/company/export": { "get": { "operationId": "companyExport", "tags": [ "Company", "Corporate Structure" ], "summary": "Company export", "description": "Export companies with the field set included in your subscription. Supports **standard pagination** (max 10,000 records) or **scroll pagination** (unlimited; scroll context expires after 8 minutes). Pass `export=true` as shown in the docs. Use `worldwideHeadquarterID` to retrieve all entities in a corporate group. Guide: https://companydata.com/api-docs/use-cases/corporate-structure/.", "parameters": [ { "$ref": "#/components/parameters/page" }, { "$ref": "#/components/parameters/pageSize" }, { "$ref": "#/components/parameters/ID" }, { "$ref": "#/components/parameters/search" }, { "$ref": "#/components/parameters/sic4Digits" }, { "$ref": "#/components/parameters/importExportCode" }, { "$ref": "#/components/parameters/legalStatus" }, { "$ref": "#/components/parameters/statusCode" }, { "$ref": "#/components/parameters/subsidiaryCode" }, { "$ref": "#/components/parameters/countryCode" }, { "$ref": "#/components/parameters/provinceName" }, { "$ref": "#/components/parameters/cityName" }, { "$ref": "#/components/parameters/streetName" }, { "$ref": "#/components/parameters/regionName" }, { "$ref": "#/components/parameters/countryName" }, { "$ref": "#/components/parameters/postalCode" }, { "$ref": "#/components/parameters/postalCodeIntegerMin" }, { "$ref": "#/components/parameters/postalCodeIntegerMax" }, { "$ref": "#/components/parameters/employeesHereMin" }, { "$ref": "#/components/parameters/employeesHereMax" }, { "$ref": "#/components/parameters/employeesTotalMin" }, { "$ref": "#/components/parameters/employeesTotalMax" }, { "$ref": "#/components/parameters/annualSalesMin" }, { "$ref": "#/components/parameters/annualSalesMax" }, { "$ref": "#/components/parameters/foundingYearsMin" }, { "$ref": "#/components/parameters/foundingYearsMax" }, { "$ref": "#/components/parameters/phoneNumber" }, { "$ref": "#/components/parameters/email" }, { "$ref": "#/components/parameters/worldwideHeadquarterID" }, { "$ref": "#/components/parameters/nationalId" }, { "$ref": "#/components/parameters/website" }, { "$ref": "#/components/parameters/hasEmail" }, { "$ref": "#/components/parameters/hasMarketability" }, { "$ref": "#/components/parameters/hasPhone" }, { "$ref": "#/components/parameters/hasWebsite" }, { "$ref": "#/components/parameters/hasNationalID" }, { "$ref": "#/components/parameters/hasCEOName" }, { "$ref": "#/components/parameters/exportFlag" }, { "$ref": "#/components/parameters/useScroll" }, { "$ref": "#/components/parameters/scrollId" } ], "responses": { "200": { "description": "Records are always under data.records. With standard pagination meta carries totalCount / currentPage / totalPages / pageSize; with scroll pagination (useScroll=true) meta carries scrollId / hasMoreRecords / recordCount / pageSize instead.", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/StandardCompanyListResponse" }, { "$ref": "#/components/schemas/ScrollCompanyExportResponse" } ] }, "examples": { "standardPagination": { "summary": "Standard pagination (page + pageSize)", "value": { "meta": { "totalCount": 1250, "currentPage": 1, "totalPages": 13, "pageSize": 100 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Trade Name": null, "Address 1": "Keizersgracht 424", "City": "AMSTERDAM", "State/Province": "NOORD-HOLLAND", "Country": "Netherlands", "Postal Code": "1016 GC", "Company Registration Number": "33011433", "Company Registration Type Code": "KVK", "Phone Number": "+31201234567", "CEO Name": "Jane Doe", "Business Category": "Computer Programming Services", "Business Category Code 1": "7371", "Employees Total": "50", "Location Type": "0", "Subsidiary Code": "0", "Global Headquarter ID Number": "330114331", "Global Headquarter Company Name": "Heineken N.V.", "Number of Companies in Group": "1", "Website": "https://companydata.com", "Email": "info@companydata.com" } ] } } }, "scrollPagination": { "summary": "Scroll pagination (useScroll=true)", "value": { "meta": { "pageSize": 1000, "hasMoreRecords": true, "scrollId": "abc123xyz456", "recordCount": 1 }, "data": { "records": [ { "ID": "330114331", "Company Name": "Heineken N.V.", "Trade Name": null, "Address 1": "Keizersgracht 424", "City": "AMSTERDAM", "State/Province": "NOORD-HOLLAND", "Country": "Netherlands", "Postal Code": "1016 GC", "Company Registration Number": "33011433", "Company Registration Type Code": "KVK", "Phone Number": "+31201234567", "CEO Name": "Jane Doe", "Business Category": "Computer Programming Services", "Business Category Code 1": "7371", "Employees Total": "50", "Location Type": "0", "Subsidiary Code": "0", "Global Headquarter ID Number": "330114331", "Global Headquarter Company Name": "Heineken N.V.", "Number of Companies in Group": "1", "Website": "https://companydata.com", "Email": "info@companydata.com" } ] } } }, "corporateGroup": { "summary": "All entities in a corporate group (worldwideHeadquarterID)", "value": { "meta": { "totalCount": 2847, "currentPage": 1, "totalPages": 57, "pageSize": 50 }, "data": { "records": [ { "ID": "407809109", "Company Name": "Shell plc", "City": "LONDON", "Country": "United Kingdom", "Location Type": "1", "Subsidiary Code": "0", "Global Headquarter ID Number": "407809109", "Global Headquarter Company Name": "Shell plc", "Number of Companies in Group": "2847" }, { "ID": "407809110", "Company Name": "Shell Nederland B.V.", "City": "THE HAGUE", "Country": "Netherlands", "Location Type": "3", "Subsidiary Code": "3", "Local Headquarter ID Number": "407809109", "Local Headquarter Company Name": "Shell plc", "National Headquarter ID Number": "407809109", "National Headquarter Company Name": "Shell plc", "Global Headquarter ID Number": "407809109", "Global Headquarter Company Name": "Shell plc", "Number of Companies in Group": "2847" } ] } } } } } } }, "400": { "$ref": "#/components/responses/ExportBadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/ExportForbidden" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "500": { "$ref": "#/components/responses/ExportInternalError" } }, "x-codeSamples": [ { "lang": "curl", "label": "Export with standard pagination", "source": "curl -G \"https://app.companydata.com/api/company/export\" \\\n -H \"x-api-key: YOUR_API_KEY\" \\\n --data-urlencode \"export=true\" \\\n --data-urlencode \"countryCode=NL\" \\\n --data-urlencode \"page=1\" \\\n --data-urlencode \"pageSize=100\"" }, { "lang": "curl", "label": "Export all entities in a corporate group", "source": "curl -G \"https://app.companydata.com/api/company/export\" \\\n -H \"x-api-key: YOUR_API_KEY\" \\\n --data-urlencode \"export=true\" \\\n --data-urlencode \"worldwideHeadquarterID=407809109\"" } ] } }, "/api/website/html": { "get": { "operationId": "downloadWebsiteHtml", "tags": [ "Domain Enrichment" ], "summary": "Domain enrichment: download a website and extract the company behind it", "description": "Fetches a public web page server-side (redirects followed, body capped, SSRF-protected) and returns its HTML. With extract=true it also returns company candidates found in the page: JSON-LD organisation, KVK / Companies House / HRB / VAT identifiers, copyright and legal-suffix names. Pass a candidate nationalId or name to /api/company/search to resolve the website to a registered company. Guide: https://companydata.com/api-docs/domain-enrichment/", "parameters": [ { "name": "url", "in": "query", "required": true, "description": "Page to download: a bare domain (coolblue.nl), host with path, or full http(s) URL. Bare domains default to https.", "schema": { "type": "string", "example": "coolblue.nl" } }, { "name": "extract", "in": "query", "required": false, "description": "Set to true to include the candidates array of company signals found in the page.", "schema": { "type": "boolean", "default": false } }, { "name": "maxBytes", "in": "query", "required": false, "description": "Cap on downloaded bytes. Default 2000000, minimum 10000, maximum 5000000. Larger bodies are cut off and flagged truncated.", "schema": { "type": "integer", "default": 2000000, "minimum": 10000, "maximum": 5000000 } } ], "responses": { "200": { "description": "Downloaded page, optionally with extracted company candidates.", "content": { "application/json": { "schema": { "type": "object", "required": [ "requestedUrl", "finalUrl", "statusCode", "html", "bytes", "truncated", "redirects", "fetchedAt" ], "properties": { "requestedUrl": { "type": "string", "description": "Normalised URL that was fetched first." }, "finalUrl": { "type": "string", "description": "URL after following redirects." }, "statusCode": { "type": "integer", "description": "HTTP status of the final response." }, "contentType": { "type": "string", "nullable": true, "description": "Content-Type header of the final response." }, "charset": { "type": "string", "description": "Charset used to decode the body." }, "html": { "type": "string", "description": "Page body as text, up to maxBytes." }, "bytes": { "type": "integer", "description": "Bytes downloaded." }, "truncated": { "type": "boolean", "description": "True when the body exceeded maxBytes and was cut off." }, "redirects": { "type": "array", "items": { "type": "string" }, "description": "Redirect targets followed, in order." }, "fetchedAt": { "type": "string", "format": "date-time", "description": "When the page was fetched." }, "candidates": { "type": "array", "description": "Only with extract=true. Company signals in trust order, deduplicated, max 12.", "items": { "type": "object", "required": [ "source" ], "properties": { "source": { "type": "string", "enum": [ "jsonld", "identifier", "copyright", "legal-suffix", "og" ], "description": "Where the signal was found." }, "name": { "type": "string", "description": "Company or trade name, when found." }, "nationalId": { "type": "string", "description": "Registration or VAT number; search it with nationalId=." }, "countryHint": { "type": "string", "description": "ISO 2-letter country inferred from the identifier, JSON-LD address or ccTLD." }, "city": { "type": "string", "description": "City from JSON-LD address, when present." } } } } } } } } }, "400": { "description": "Missing or invalid url (code: invalid_url)." }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "description": "Host is not public: IP literal, internal name, or resolves to a private address (code: blocked_host)." }, "415": { "description": "The URL returned a non-HTML document (code: not_html)." }, "502": { "description": "Site unreachable, DNS failure, or more than 5 redirects (code: unreachable, too_many_redirects)." }, "504": { "description": "Site did not respond within 10 seconds (code: timeout)." } }, "x-codeSamples": [ { "lang": "curl", "label": "Download coolblue.nl and extract company candidates", "source": "curl -G \"https://app.companydata.com/api/website/html\" \\\n -H \"x-api-key: YOUR_API_KEY\" \\\n --data-urlencode \"url=coolblue.nl\" \\\n --data-urlencode \"extract=true\"" } ] } }, "/api/cities": { "get": { "operationId": "lookupCities", "tags": [ "Lookup" ], "summary": "Look up city names", "description": "Returns exact cityName values to use with company search/export (cityName requires an exact database value).", "parameters": [ { "$ref": "#/components/parameters/lookupSearch" }, { "$ref": "#/components/parameters/lookupCountries" }, { "$ref": "#/components/parameters/lookupLimit" } ], "responses": { "200": { "description": "List of matching cities.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data" ], "properties": { "data": { "type": "array", "description": "Matching city entries with exact names for use in API filters.", "items": { "type": "object", "required": [ "cityName", "countryCode" ], "properties": { "cityName": { "type": "string", "description": "City name as stored in the database (use verbatim in cityName filter)." }, "countryCode": { "type": "string", "description": "ISO 2-letter country code for this city entry." } } } }, "total": { "type": "integer", "description": "Number of matching lookup results returned in this response." }, "limit": { "type": "integer", "description": "Effective limit applied to this lookup request (requested or default)." } } }, "example": { "data": [ { "cityName": "AMSTERDAM", "countryCode": "NL" }, { "cityName": "AMSTELVEEN", "countryCode": "NL" } ], "total": 2, "limit": 100 } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } }, "x-codeSamples": [ { "lang": "curl", "label": "Look up city names in the Netherlands", "source": "curl -G \"https://app.companydata.com/api/cities\" \\\n -H \"x-api-key: YOUR_API_KEY\" \\\n --data-urlencode \"search=Amster\" \\\n --data-urlencode \"countries=NL\" \\\n --data-urlencode \"limit=100\"" } ] } } }, "components": { "responses": { "BadRequest": { "description": "Either the request carried no query parameters at all - the body then reuses the list envelope with meta.totalCount = -1 and an explanatory meta.message, and data.records is empty - or it used a parameter name this endpoint does not have. An unknown name is refused rather than ignored, because a dropped filter would return the whole database as an apparently valid answer and spend a search doing it. The body names each unknown parameter and, where the name is a recognisable near-miss, what to send instead.", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/EmptyQueryResponse" }, { "$ref": "#/components/schemas/ErrorResponse" } ] }, "examples": { "emptyQuery": { "summary": "No query parameters", "value": { "meta": { "totalCount": -1, "message": "Fill in a query to get an estimation" }, "data": { "records": [] } } }, "unknownParameter": { "summary": "Parameter name this endpoint does not have", "value": { "error": "Unknown parameter", "message": "This endpoint does not have the parameter(s): query.", "unknownParameters": [ "query" ], "didYouMean": { "query": "search" }, "documentation": "https://companydata.com/api-docs/search" } } } } } }, "Unauthorized": { "description": "The x-api-key header does not match an existing key (search and export), or no key and no browser session was sent (export). Search without any key or session answers 403 instead.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "invalidApiKey": { "summary": "Unknown API key", "value": { "error": "Invalid API key" } }, "missingCredentials": { "summary": "No API key and no session (export only)", "value": { "error": "Unauthorized: No token or user found. " } } } } } }, "MethodNotAllowed": { "description": "Only GET (and the CORS preflight OPTIONS) are accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "methodNotAllowed": { "summary": "Wrong HTTP method", "value": { "error": "Method Not Allowed" } } } } }, "headers": { "Allow": { "description": "Accepted methods for this endpoint.", "schema": { "type": "string", "example": "GET" } } } }, "SearchForbidden": { "description": "The credentials were recognised but may not search. For API keys this means the key is disabled or the subscription is inactive; subscriptionStatus carries the subscription state when that is the cause. The remaining cases only apply to browser sessions (no x-api-key): a missing or expired guest session, a failed security check, or a guest or logged-in user asking for a page beyond their allowance (error = PAGINATION_LIMIT).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "apiKeyDisabled": { "summary": "API key disabled", "value": { "error": "API key is disabled. Please check your subscription status." } }, "subscriptionInactive": { "summary": "Subscription inactive", "value": { "error": "Subscription inactive. Please update your payment method or reactivate your subscription.", "subscriptionStatus": "PAYMENT_FAILED" } }, "noGuestSession": { "summary": "No x-api-key header and no browser session", "value": { "error": "No guest session. Please reload the page." } }, "paginationLimit": { "summary": "Browser session beyond its page allowance (never returned for API keys)", "value": { "error": "PAGINATION_LIMIT", "paginationMessage": "Create an account to paginate through your results.", "visitor": null } } } } } }, "SearchTooManyRequests": { "description": "A quota for the key or its account is exhausted. Which quota applies depends on the contract: the monthly search allowance (searchesUsed / searchesLimit), a manually invoiced request allowance (requestsUsed / requestsLimit), the yearly account request limit, or the monthly limit of a legacy key. Quotas reset with the billing period; retrying sooner returns the same status. The per-IP variant only applies to browser sessions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "monthlySearchLimit": { "summary": "Monthly search allowance used up", "value": { "error": "Monthly API search limit exceeded", "searchesUsed": 100000, "searchesLimit": 100000 } }, "manualInvoiceLimit": { "summary": "Manually invoiced request allowance used up", "value": { "error": "Rate limit exceeded", "requestsUsed": 100000, "requestsLimit": 100000 } }, "accountYearlyLimit": { "summary": "Yearly request limit of the account reached", "value": { "error": "Rate limit exceeded for this year" } }, "legacyKeyMonthlyLimit": { "summary": "Monthly limit of a key without an account", "value": { "error": "Rate limit exceeded for this month, contact support to request more" } }, "guestIpLimit": { "summary": "Browser session only", "value": { "error": "Too many searches from this location. Please register or log in to continue." } } } } } }, "SearchInternalError": { "description": "The search itself failed (for example the search cluster returned an error). Safe to retry with backoff.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "searchFailed": { "summary": "Search failed", "value": { "error": "Error performing search" } }, "visitorRecordFailed": { "summary": "Browser session only", "value": { "error": "Internal server error checking visitor record." } } } } } }, "SearchUnavailable": { "description": "No search index is currently selected on the server, typically during an index switch. Retry later.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "noMainIndex": { "summary": "No main index selected", "value": { "error": "Search is temporarily unavailable: no main index is selected." } } } } } }, "ExportBadRequest": { "description": "No query parameters were sent (list envelope with meta.totalCount = -1, see search), a parameter name this endpoint does not have was used, or parentUniqueID was combined with other filters.", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/EmptyQueryResponse" }, { "$ref": "#/components/schemas/ErrorResponse" } ] }, "examples": { "emptyQuery": { "summary": "No query parameters", "value": { "meta": { "totalCount": -1, "message": "Fill in a query to get an estimation" }, "data": { "records": [] } } }, "unknownParameter": { "summary": "Parameter name this endpoint does not have", "value": { "error": "Unknown parameter", "message": "This endpoint does not have the parameter(s): query.", "unknownParameters": [ "query" ], "didYouMean": { "query": "search" }, "documentation": "https://companydata.com/api-docs/search" } }, "parentUniqueIdWithFilters": { "summary": "parentUniqueID combined with other filters", "value": { "error": "When parentUniqueID is set, no other filters are allowed. Disallowed parameters: countryCode, cityName" } } } } } }, "ExportForbidden": { "description": "The API key exists but is disabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "apiKeyDisabled": { "summary": "API key disabled", "value": { "error": "API key is disabled. Please check your subscription status." } } } } } }, "ExportInternalError": { "description": "The export failed. Note that the export error body uses message rather than error. Besides genuine server errors this status also carries account and quota failures raised while preparing the export: an unknown API key, an inactive subscription, an exhausted export allowance, or more than 10,000 records requested with standard pagination. The reason is in message.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExportErrorResponse" }, "examples": { "exportFailed": { "summary": "Export failed", "value": { "message": "E: Error: Error performing export" } }, "overLimit": { "summary": "Export allowance used up", "value": { "message": "Export not allowed, over limit" } }, "deepPagination": { "summary": "Standard pagination beyond 10,000 records", "value": { "message": "The api allows max 10.000 export for the same selection" } } } } } } }, "securitySchemes": { "ApiKeyAuth": { "type": "apiKey", "in": "header", "name": "x-api-key", "description": "API key for the CompanyData API. Required on all endpoints." } }, "parameters": { "page": { "name": "page", "in": "query", "required": false, "description": "Page number for standard pagination (starts at 1). Defaults to 1 if omitted. Export: maximum 10,000 records total with standard pagination.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, "pageSize": { "name": "pageSize", "in": "query", "required": false, "description": "Records per page. Search: defaults to 25, maximum 50; maximum 50 total records retrievable. Export: defaults to 25; recommended 100-1000 for standard pagination, 1000+ for scroll.", "schema": { "type": "integer", "minimum": 1 } }, "useScroll": { "name": "useScroll", "in": "query", "required": false, "description": "When true, enables scroll (cursor) pagination on export. Use scrollId from the previous response for subsequent requests. Export only.", "schema": { "type": "boolean" } }, "scrollId": { "name": "scrollId", "in": "query", "required": false, "description": "Scroll session identifier from the previous export response. Required for subsequent requests when useScroll is true. Scroll context expires after 8 minutes.", "schema": { "type": "string" } }, "exportFlag": { "name": "export", "in": "query", "required": false, "description": "Set to true on the export endpoint to receive the field set included in your subscription package.", "schema": { "type": "boolean" } }, "ID": { "name": "ID", "in": "query", "required": false, "description": "Filter by company ID.", "schema": { "type": "string" } }, "search": { "name": "search", "in": "query", "required": false, "description": "Company name lookup. Matches from the start of the stored name, so a leading word or two is enough (\"Heineken\" finds \"Heineken N.V.\"), but the words must appear in the order they are stored and accents must match (\"Telefonica\" does not find \"Telefónica\"). Common legal forms are interchangeable (BV / B.V., GmbH, Ltd / Limited). Anything else in the name - an extra word, a different word order, a typo - has to match. When the exact search finds nothing the API retries on its own - all words in any order, then the name without its legal form, then (for API keys) the closest spelling and the same name in another country - and reports which step answered in meta.warnings. A retry is only accepted while its result set is small, so a common word cannot pass as a match. Tips: search the distinctive part of the name and leave the legal form off; use website= for a domain, nationalId= for a registration number and ID= for a CompanyData ID. A name in a non-Latin alphabet will not match, because names are stored in the Latin alphabet.", "schema": { "type": "string" } }, "sic4Digits": { "name": "sic4Digits", "in": "query", "required": false, "description": "4-digit SIC code filter. Comma-separated list supported (e.g. 0111,0115).", "schema": { "type": "string" } }, "importExportCode": { "name": "importExportCode", "in": "query", "required": false, "description": "Import/export activity codes: A, B, C, D, E, F, G, H. Comma-separated list supported.", "schema": { "type": "string" } }, "legalStatus": { "name": "legalStatus", "in": "query", "required": false, "description": "Legal status / business legal type codes (numeric). Comma-separated list supported. Maps to Business Legal Type in responses.", "schema": { "type": "string" } }, "statusCode": { "name": "statusCode", "in": "query", "required": false, "description": "Location type filter for corporate hierarchy: 0 = single location, 1 = headquarter/parent, 2 = branch. Comma-separated list supported. Often use 0,1 to exclude branches when searching. Maps to Location Type in responses. See https://companydata.com/api-docs/use-cases/corporate-structure/", "schema": { "type": "string" } }, "subsidiaryCode": { "name": "subsidiaryCode", "in": "query", "required": false, "description": "Subsidiary filter for corporate hierarchy: 0 = not a subsidiary, 3 = is a subsidiary. Comma-separated list supported. Maps to Subsidiary Code in responses. See https://companydata.com/api-docs/use-cases/corporate-structure/", "schema": { "type": "string" } }, "countryCode": { "name": "countryCode", "in": "query", "required": false, "description": "ISO 3166-1 alpha-2 country code filter, uppercase. Comma-separated list supported (e.g. AL,BY,NL). Lowercase codes and full country names (\"nl\", \"Netherlands\") are read as the matching code and reported in meta.warnings. A value that is not a country code is kept as a filter and matches nothing, so it is reported too rather than silently widening the search - put the company name in search, not here.", "schema": { "type": "string" } }, "provinceName": { "name": "provinceName", "in": "query", "required": false, "description": "State/province name as stored in the database (exact match).", "schema": { "type": "string" } }, "cityName": { "name": "cityName", "in": "query", "required": false, "description": "City name as stored in the database (exact match). Use /api/cities lookup for valid values.", "schema": { "type": "string" } }, "streetName": { "name": "streetName", "in": "query", "required": false, "description": "Street address; supports prefix (partial) matching from the start of words.", "schema": { "type": "string" } }, "regionName": { "name": "regionName", "in": "query", "required": false, "description": "Region or state name as stored in the database (exact match).", "schema": { "type": "string" } }, "countryName": { "name": "countryName", "in": "query", "required": false, "description": "Country name as stored in the database (exact match).", "schema": { "type": "string" } }, "postalCode": { "name": "postalCode", "in": "query", "required": false, "description": "Postal code (exact string match).", "schema": { "type": "string" } }, "postalCodeIntegerMin": { "name": "postalCodeInteger[min]", "in": "query", "required": false, "description": "Minimum numeric postal code for range filtering (use with postalCodeInteger[max]).", "schema": { "type": "integer" } }, "postalCodeIntegerMax": { "name": "postalCodeInteger[max]", "in": "query", "required": false, "description": "Maximum numeric postal code for range filtering (use with postalCodeInteger[min]).", "schema": { "type": "integer" } }, "employeesHereMin": { "name": "employeesHere[min]", "in": "query", "required": false, "description": "Minimum employees at this location. Use with employeesHere[max].", "schema": { "type": "integer" } }, "employeesHereMax": { "name": "employeesHere[max]", "in": "query", "required": false, "description": "Maximum employees at this location. Use with employeesHere[min].", "schema": { "type": "integer" } }, "employeesTotalMin": { "name": "employeesTotal[min]", "in": "query", "required": false, "description": "Minimum total employees in the company. Use with employeesTotal[max].", "schema": { "type": "integer" } }, "employeesTotalMax": { "name": "employeesTotal[max]", "in": "query", "required": false, "description": "Maximum total employees in the company. Use with employeesTotal[min].", "schema": { "type": "integer" } }, "annualSalesMin": { "name": "annualSales[min]", "in": "query", "required": false, "description": "Minimum annual sales in U.S. dollars. Use with annualSales[max].", "schema": { "type": "integer" } }, "annualSalesMax": { "name": "annualSales[max]", "in": "query", "required": false, "description": "Maximum annual sales in U.S. dollars. Use with annualSales[min].", "schema": { "type": "integer" } }, "foundingYearsMin": { "name": "foundingYears[min]", "in": "query", "required": false, "description": "Minimum founding year (four-digit). Use with foundingYears[max].", "schema": { "type": "integer" } }, "foundingYearsMax": { "name": "foundingYears[max]", "in": "query", "required": false, "description": "Maximum founding year (four-digit). Use with foundingYears[min].", "schema": { "type": "integer" } }, "phoneNumber": { "name": "phoneNumber", "in": "query", "required": false, "description": "Full international phone number with + prefix (local formats not supported).", "schema": { "type": "string" } }, "email": { "name": "email", "in": "query", "required": false, "description": "Filter by email address or pattern (e.g. domain or full address).", "schema": { "type": "string" } }, "worldwideHeadquarterID": { "name": "worldwideHeadquarterID", "in": "query", "required": false, "description": "Filter by Global Headquarter ID Number to retrieve all entities in a corporate group (HQ, subsidiaries, and branches worldwide). Get the ID from any group member's export record, then pass it here on /api/company/export. Guide: https://companydata.com/api-docs/use-cases/corporate-structure/", "schema": { "type": "string" } }, "nationalId": { "name": "nationalId", "in": "query", "required": false, "description": "National company registration number; combine with countryCode when possible. Stored without separators and with letters upper-cased (NL 24147166, GB FC000268, AR 33716282819, BR 51312418000175, ES Q9150013B, IN 27AAACH3235M1ZJ). A value that still carries the spaces, dots, dashes or slashes a registry prints is normalised before the lookup and reported in meta.warnings. Registries that issue more than one kind of identifier are not interchangeable: a German HRB number or an Indian CIN will not match.", "schema": { "type": "string" } }, "website": { "name": "website", "in": "query", "required": false, "description": "Company website. Matches the stored URL from the start of the host, with or without \"www.\" and with or without a scheme, so \"aselsan.com\", \"www.aselsan.com\" and \"https://www.aselsan.com/tr\" all find the same company. Use this rather than search when you have a domain: a domain put into search only matches companies whose *name* is that domain.", "schema": { "type": "string" } }, "hasEmail": { "name": "hasEmail", "in": "query", "required": false, "description": "If true, only companies with a non-empty Email field.", "schema": { "type": "boolean" } }, "hasMarketability": { "name": "hasMarketability", "in": "query", "required": false, "description": "If true, filters for records marked as marketable (internal flag; no direct response field).", "schema": { "type": "boolean" } }, "hasPhone": { "name": "hasPhone", "in": "query", "required": false, "description": "If true, only companies with a non-empty Phone Number.", "schema": { "type": "boolean" } }, "hasFax": { "name": "hasFax", "in": "query", "required": false, "description": "If true, only companies with a non-empty Fax Number.", "schema": { "type": "boolean" } }, "hasContactPerson": { "name": "hasContactPerson", "in": "query", "required": false, "description": "If true, only companies with a non-empty CEO Name.", "schema": { "type": "boolean" } }, "hasWebsite": { "name": "hasWebsite", "in": "query", "required": false, "description": "If true, only companies with a non-empty Website.", "schema": { "type": "boolean" } }, "hasNationalID": { "name": "hasNationalID", "in": "query", "required": false, "description": "If true, only companies with a Company Registration Number.", "schema": { "type": "boolean" } }, "hasBusinessName": { "name": "hasBusinessName", "in": "query", "required": false, "description": "If true, only companies with a non-empty Company Name.", "schema": { "type": "boolean" } }, "hasStreetAddress": { "name": "hasStreetAddress", "in": "query", "required": false, "description": "If true, only companies with a non-empty Address 1.", "schema": { "type": "boolean" } }, "hasPostalCode": { "name": "hasPostalCode", "in": "query", "required": false, "description": "If true, only companies with a non-empty Postal Code.", "schema": { "type": "boolean" } }, "hasCEOName": { "name": "hasCEOName", "in": "query", "required": false, "description": "If true, only companies with a non-empty CEO Name.", "schema": { "type": "boolean" } }, "lookupSearch": { "name": "search", "in": "query", "required": true, "description": "Search term for autocomplete-style lookup (partial input allowed).", "schema": { "type": "string" } }, "lookupCountries": { "name": "countries", "in": "query", "required": false, "description": "Optional ISO country code to restrict lookup results to one country.", "schema": { "type": "string" } }, "lookupLimit": { "name": "limit", "in": "query", "required": false, "description": "Maximum number of lookup results to return. Default 100.", "schema": { "type": "integer", "minimum": 1, "default": 100 } } }, "schemas": { "Executive": { "type": "object", "description": "One executive record. Only included if at least first name or last name is present. All properties may be null.", "properties": { "Position": { "type": "number", "nullable": true, "description": "Numeric rank of the executive within the company (1 = most senior)." }, "Title": { "type": "string", "nullable": true, "description": "Job title of the executive (e.g., \"CEO\", \"CFO\", \"Director\")." }, "Title Code": { "type": "string", "nullable": true, "description": "Standardized internal code representing the job title." }, "First Name": { "type": "string", "nullable": true, "description": "Executive's first name." }, "Last Name": { "type": "string", "nullable": true, "description": "Executive's last name." }, "Gender": { "type": "string", "nullable": true, "description": "Gender of the executive (\"Male\" or \"Female\")." }, "Language Preference": { "type": "string", "nullable": true, "description": "ISO locale code indicating the executive's preferred language (see Language Codes in human docs)." } } }, "CompanyRecord": { "type": "object", "description": "Single company record. Field availability depends on your package. Values are strings unless noted; many fields may be null or empty when unknown.", "additionalProperties": true, "properties": { "ID": { "type": "string", "nullable": true, "description": "Unique identifier for the company record. Store this to retrieve or reference the company in future requests without re-searching." }, "Company Name": { "type": "string", "nullable": true, "description": "Primary registered name of the company." }, "Trade Name": { "type": "string", "nullable": true, "description": "Alternative or \"doing business as\" (DBA) name the company may operate under." }, "Address Status": { "type": "string", "nullable": true, "description": "Indicates the address type on file. For European records, if only the registered address is available, it is used as the physical address and this field flags that." }, "Address 1": { "type": "string", "nullable": true, "description": "Primary street address (street name and building number)." }, "Address 2": { "type": "string", "nullable": true, "description": "Secondary address details (suite, floor, building name)." }, "City": { "type": "string", "nullable": true, "description": "City of the company location." }, "State/Province": { "type": "string", "nullable": true, "description": "State or province of the company location." }, "Country": { "type": "string", "nullable": true, "description": "Country name in English (e.g., \"Netherlands\", \"United States\")." }, "Postal Code": { "type": "string", "nullable": true, "description": "Postal or ZIP code. Always a string - may contain letters (e.g., \"1016 GC\"). Filter: postalCode (exact), postalCodeInteger (numeric range)." }, "Company Registration Number": { "type": "string", "nullable": true, "description": "Official national registration ID (e.g., Dutch KvK number, UK Companies House number). See Company Registration Type Code for which system applies. Filter: nationalId." }, "Company Registration Type Code": { "type": "string", "nullable": true, "description": "Code identifying the national registration system used (e.g., \"KVK\" for the Netherlands, \"CRO\" for the UK). Use this to determine how to validate or format Company Registration Number." }, "Country Phone Access Code": { "type": "string", "nullable": true, "description": "International dialing prefix without leading + or 00 (e.g., \"31\" for the Netherlands, \"1\" for the US)." }, "Phone Number": { "type": "string", "nullable": true, "description": "Company phone number. US numbers include area code with no punctuation. Filter: phoneNumber, hasPhone." }, "Cable Telex": { "type": "string", "nullable": true, "description": "Legacy cable or telex number. Rarely populated on modern records." }, "Fax Number": { "type": "string", "nullable": true, "description": "Company fax number. Filter: hasFax." }, "CEO Name": { "type": "string", "nullable": true, "description": "Name of the highest-ranking contact at this location. Also available as Position 1 in the Executives array when executive data is enabled on your account. Filter: hasCEOName, hasContactPerson." }, "CEO Title": { "type": "string", "nullable": true, "description": "Title of the CEO or top contact, possibly abbreviated (e.g., \"Pres\", \"Dir\", \"CEO\")." }, "Business Category": { "type": "string", "nullable": true, "description": "Plain-English description of the company's primary activity (e.g., \"Computer Programming Services\")." }, "Business Category Code 1": { "type": "string", "nullable": true, "description": "Primary 4-digit SIC code (1987 U.S. classification system). Filter: sic4Digits." }, "Business Category Code 1 - Description": { "type": "string", "nullable": true, "description": "Description of the primary SIC code (e.g., \"Computer Programming, Data Processing\")." }, "Founding Year": { "type": "string", "nullable": true, "description": "Four-digit year the current ownership took control, or the founding year if no change of control occurred. Not available for branch records. Filter: foundingYears[min] / foundingYears[max]." }, "Yearly Revenue Local Currency": { "type": "string", "nullable": true, "description": "Annual sales in the local currency. See Currency Code for the applicable currency." }, "Yearly Revenue Indicator": { "type": "string", "nullable": true, "description": "Data quality flag for Yearly Revenue Local Currency. \"0\" = actual figure, \"1\" = low-end range estimate, \"2\" = estimated (also used when the revenue value is zero), empty/null = not available." }, "Yearly Revenue in U.S. Dollars": { "type": "string", "nullable": true, "description": "Annual sales in USD. Exchange rate updated once per year. Filter: annualSales[min] / annualSales[max]." }, "Currency Code": { "type": "string", "nullable": true, "description": "ISO 4217 currency code for Yearly Revenue Local Currency (e.g., \"EUR\", \"USD\", \"GBP\")." }, "Employees On Site": { "type": "string", "nullable": true, "description": "Number of employees at this specific location. Available primarily for US and Canadian records. Filter: employeesHere[min] / employeesHere[max]." }, "Employees On Site Indicator": { "type": "string", "nullable": true, "description": "Data quality flag for Employees On Site. \"0\" = actual figure, \"1\" = low-end estimate, \"2\" = estimated (also used when the value is zero), empty/null = not available." }, "Employees Total": { "type": "string", "nullable": true, "description": "Total number of employees across the entire company. Filter: employeesTotal[min] / employeesTotal[max]." }, "Employees Total Indicator": { "type": "string", "nullable": true, "description": "Data quality flag for Employees Total. \"0\" = actual figure, \"1\" = low-end estimate, \"2\" = estimated (also used when the value is zero), empty/null = not available." }, "Include Principles Indicator": { "type": "string", "nullable": true, "description": "Whether business owners or partners are counted in Employees Total. \"Y\" = included, empty/null = not included or unknown. Available primarily on non-US records." }, "Import/Export Code": { "type": "string", "nullable": true, "description": "Trade activity of the company. \"A\" = imports, exports & agent, \"B\" = imports & exports, \"C\" = imports only, \"D\" = imports & agent, \"E\" = exports & agent, \"F\" = agent only (no inventory), \"G\" = none or not available, \"H\" = exports only, empty = not available. Filter: importExportCode." }, "Business Legal Type": { "type": "string", "nullable": true, "description": "Legal entity type code. \"1\" or \"13\" = proprietorship, \"2\" or \"7\" = partnership, \"3\" = corporation, \"8\" = joint venture, \"9\" = master limited partnership, \"10\" = general partnership, \"11\" = limited partnership, \"12\" = partnership (unknown type), \"14\" = limited liability, \"15\" = friendly society, \"0\" or empty = not available. Other values may exist. Filter: legalStatus." }, "Control Indicator": { "type": "string", "nullable": true, "description": "Reserved field, not currently in use." }, "Location Type": { "type": "string", "nullable": true, "description": "Company's role in its corporate hierarchy. \"0\" = single location (no entities report to it), \"1\" = headquarter/parent (branches and/or subsidiaries report to it), \"2\" = branch (secondary location of a headquarter), empty = not available. Filter: statusCode." }, "Subsidiary Code": { "type": "string", "nullable": true, "description": "Subsidiary status. \"0\" = not a subsidiary, \"3\" = is a subsidiary, empty = not linked. Populated on linked records only. Filter: subsidiaryCode." }, "Local Headquarter ID Number": { "type": "string", "nullable": true, "description": "ID of the immediate parent entity above this location. Pass this value as the ID filter to fetch that record. Empty if not linked." }, "Local Headquarter Company Name": { "type": "string", "nullable": true, "description": "Name of the immediate parent entity. Empty if not linked." }, "Local Headquarter Street Address": { "type": "string", "nullable": true, "description": "Street address of the immediate parent entity. Empty if not linked." }, "Local Headquarter City": { "type": "string", "nullable": true, "description": "City of the immediate parent entity. Empty if not linked." }, "Local Headquarter State/Province": { "type": "string", "nullable": true, "description": "State/province of the immediate parent entity. Empty if not linked." }, "Local Headquarter Country Name": { "type": "string", "nullable": true, "description": "Country name of the immediate parent entity. Empty if not linked." }, "National Headquarter ID Number": { "type": "string", "nullable": true, "description": "ID of the highest entity within the same country as this location. May equal this company's own ID if it is the national top." }, "National Headquarter Company Name": { "type": "string", "nullable": true, "description": "Name of the national-level parent. Empty if not linked." }, "National Headquarter Street Address": { "type": "string", "nullable": true, "description": "Street address of the national-level parent. Empty if not linked." }, "National Headquarter City Name": { "type": "string", "nullable": true, "description": "City of the national-level parent. Empty if not linked." }, "National Headquarter State/Province Name": { "type": "string", "nullable": true, "description": "State/province of the national-level parent. Empty if not linked." }, "Global Headquarter ID Number": { "type": "string", "nullable": true, "description": "ID of the ultimate worldwide parent of the corporate group. All companies in the group share this value. Pass this to the worldwideHeadquarterID filter to fetch all entities in the group." }, "Global Headquarter Company Name": { "type": "string", "nullable": true, "description": "Name of the ultimate worldwide parent. Empty if not linked." }, "Global Headquarter Street Address": { "type": "string", "nullable": true, "description": "Street address of the ultimate worldwide parent. Empty if not linked." }, "Global Headquarter City Name": { "type": "string", "nullable": true, "description": "City of the ultimate worldwide parent. Empty if not linked." }, "Global Headquarter State/Province": { "type": "string", "nullable": true, "description": "State/province of the ultimate worldwide parent. Empty if not linked." }, "Global Headquarter Country Name": { "type": "string", "nullable": true, "description": "Country name of the ultimate worldwide parent. Empty if not linked." }, "Number of Companies in Group": { "type": "string", "nullable": true, "description": "Total number of entities in the worldwide corporate group (HQ + all subsidiaries and branches). All group members share this count. Empty if not linked." }, "Website": { "type": "string", "nullable": true, "description": "Company website URL. Filter: hasWebsite." }, "Email": { "type": "string", "nullable": true, "description": "Company email address(es). May contain multiple addresses separated by commas. Filter: email, hasEmail." }, "Executives": { "type": "array", "nullable": true, "description": "Array of executive/management personnel objects (BETA). Up to 11 executives per company. Null if executive data is not enabled or not available.", "items": { "$ref": "#/components/schemas/Executive" } } } }, "ListMeta": { "type": "object", "description": "Pagination details of a standard (page + pageSize) response.", "required": [ "totalCount", "currentPage", "totalPages", "pageSize" ], "properties": { "totalCount": { "type": "integer", "description": "Total number of records matching the query across all pages." }, "currentPage": { "type": "integer", "description": "Page number that was returned (1-based)." }, "totalPages": { "type": "integer", "description": "Total number of pages for this query at the given pageSize (ceil(totalCount / pageSize))." }, "pageSize": { "type": "integer", "description": "Page size that was applied (the requested pageSize, capped by the endpoint maximum)." }, "warnings": { "$ref": "#/components/schemas/RequestWarnings" } } }, "RequestWarnings": { "type": "array", "description": "Present only when something about the request is worth knowing: a value was normalised before the lookup, a filter was not applied, or the name search found nothing and another field answered instead. Warnings never mean the request failed, and the rows in data are always real results.", "items": { "type": "object", "required": [ "code", "parameter", "message" ], "properties": { "code": { "type": "string", "enum": [ "UNKNOWN_PARAMETER", "PARAMETER_NOT_APPLIED", "COUNTRY_CODE_NORMALISED", "COUNTRY_CODE_UNRECOGNISED", "NATIONAL_ID_NORMALISED", "SEARCH_LOOKS_LIKE_DOMAIN", "SEARCH_LOOKS_LIKE_REGISTRATION_NUMBER", "SEARCH_NON_LATIN_SCRIPT", "SEARCH_MATCHED_ANY_ORDER", "SEARCH_MATCHED_CORE_NAME", "SEARCH_MATCHED_FUZZY", "SEARCH_MATCHED_OTHER_COUNTRY" ], "description": "UNKNOWN_PARAMETER: the parameter is not part of this endpoint and was ignored. PARAMETER_NOT_APPLIED: the parameter is accepted but does not filter anything on this index. COUNTRY_CODE_NORMALISED / NATIONAL_ID_NORMALISED: the value was rewritten to the stored form before the lookup. COUNTRY_CODE_UNRECOGNISED: the value is not a country code, so it matches nothing; it is kept rather than dropped, so the count stays honest. SEARCH_LOOKS_LIKE_DOMAIN / SEARCH_LOOKS_LIKE_REGISTRATION_NUMBER: no company carries that name, so website or nationalId was searched instead and those are the rows you received. SEARCH_NON_LATIN_SCRIPT: names are stored in the Latin alphabet. SEARCH_MATCHED_ANY_ORDER / SEARCH_MATCHED_CORE_NAME / SEARCH_MATCHED_FUZZY / SEARCH_MATCHED_OTHER_COUNTRY: the exact name search found nothing, so the rows you received come from a relaxed retry - all words in any order, the name without its legal form, the closest spelling, or the same name in another country. Each is only used when it returns a small result set, and each one is worth verifying before you rely on it." }, "parameter": { "type": "string", "description": "The request parameter the warning is about." }, "message": { "type": "string", "description": "What happened, in one sentence." }, "hint": { "type": "string", "description": "What to send instead, when there is a better way to ask." } } } }, "StandardCompanyListResponse": { "type": "object", "description": "Standard paginated list of companies (search, and export without scroll). Pagination details live under meta; records under data.records.", "required": [ "meta", "data" ], "properties": { "meta": { "$ref": "#/components/schemas/ListMeta" }, "data": { "type": "object", "description": "Wrapper containing the current page of records.", "required": [ "records" ], "properties": { "records": { "type": "array", "description": "Company records for the requested page.", "items": { "$ref": "#/components/schemas/CompanyRecord" } } } } } }, "CompanySearchResponse": { "description": "Successful search response: the standard list envelope plus a visitor property that is always null for API-key requests.", "allOf": [ { "$ref": "#/components/schemas/StandardCompanyListResponse" }, { "type": "object", "properties": { "visitor": { "type": "object", "nullable": true, "additionalProperties": true, "description": "Guest-session bookkeeping used by the web application. Always null when the request carries an x-api-key header." } } } ] }, "ScrollMeta": { "type": "object", "description": "Cursor details of a scroll (useScroll=true) export response. No total count is available in scroll mode.", "required": [ "pageSize", "hasMoreRecords", "recordCount" ], "properties": { "pageSize": { "type": "integer", "description": "Page size that was applied to this scroll request." }, "hasMoreRecords": { "type": "boolean", "description": "True if another page can be fetched with the latest scrollId." }, "scrollId": { "type": "string", "nullable": true, "description": "Opaque cursor for the next request. Always use the latest scrollId from the most recent response." }, "recordCount": { "type": "integer", "description": "Number of records in data.records of this response." } } }, "ScrollCompanyExportResponse": { "type": "object", "description": "Scroll-based export page. Same envelope as standard pagination (records under data.records); meta carries the scroll cursor instead of page numbers.", "required": [ "meta", "data" ], "properties": { "meta": { "$ref": "#/components/schemas/ScrollMeta" }, "data": { "type": "object", "required": [ "records" ], "properties": { "records": { "type": "array", "description": "Company records for this scroll page.", "items": { "$ref": "#/components/schemas/CompanyRecord" } } } } } }, "EmptyQueryResponse": { "type": "object", "description": "Body of the 400 returned when a request carries no query parameters. Reuses the list envelope with meta.totalCount = -1 and an explanatory meta.message.", "required": [ "meta", "data" ], "properties": { "meta": { "type": "object", "required": [ "totalCount", "message" ], "properties": { "totalCount": { "type": "integer", "description": "Always -1 for this error." }, "message": { "type": "string", "description": "Explanation of the error." } } }, "data": { "type": "object", "required": [ "records" ], "properties": { "records": { "type": "array", "description": "Always empty for this error.", "items": { "$ref": "#/components/schemas/CompanyRecord" } } } } } }, "ErrorResponse": { "type": "object", "description": "Error body of every failure status except the empty-query 400 and export 500s. error is a human-readable reason; some failures add context fields (subscriptionStatus, searchesUsed / searchesLimit, requestsUsed / requestsLimit, paginationMessage, visitor).", "required": [ "error" ], "additionalProperties": true, "properties": { "error": { "type": "string", "description": "Human-readable reason for the failure." }, "subscriptionStatus": { "type": "string", "description": "Present on 403 when the subscription is inactive (PAYMENT_FAILED, CANCELED or EXPIRED)." }, "searchesUsed": { "type": "integer", "description": "Present on 429 when the monthly search allowance is used up." }, "searchesLimit": { "type": "integer", "description": "Present on 429 when the monthly search allowance is used up." }, "requestsUsed": { "type": "integer", "description": "Present on 429 for manually invoiced keys." }, "requestsLimit": { "type": "integer", "description": "Present on 429 for manually invoiced keys." }, "paginationMessage": { "type": "string", "description": "Present on the browser-only PAGINATION_LIMIT 403." }, "unknownParameters": { "type": "array", "items": { "type": "string" }, "description": "Present on the unknown-parameter 400: the parameter names this endpoint does not have." }, "didYouMean": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Present on the unknown-parameter 400: each unknown name mapped to the parameter you probably meant." }, "documentation": { "type": "string", "format": "uri", "description": "Present on the unknown-parameter 400: where the full parameter list lives." } } }, "ExportErrorResponse": { "type": "object", "description": "Error body of export 500 responses; the reason is under message rather than error.", "required": [ "message" ], "additionalProperties": true, "properties": { "message": { "type": "string", "description": "Human-readable reason for the failure." } } } } } } ```