Company Search & Autocomplete
Build a professional company search experience for your application. The BoldData search API is optimized for autocomplete use cases, returning results quickly as users type.
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
- User starts typing a company name (minimum 2 characters)
- Debounce the input to avoid excessive API calls
- Call the search API with the query
- Display matching companies in a dropdown
- On selection, fetch full company details
Try Company Search Autocomplete
Don't have an API key? Get your API key →
Type at least 2 characters. Use arrow keys to navigate, Enter to select.
TypeScript Implementation
GET
/api/company/search// 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<T>(endpoint: string, params: Record<string, string | number>): Promise<T> {
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<SearchResult[]> {
if (query.length < 2) return [];
const params: Record<string, string | number> = {
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<CompanyDetails | null> {
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<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
// Example: React-style autocomplete handler
class CompanyAutocomplete {
private cache = new Map<string, SearchResult[]>();
async search(query: string, countryCode?: string): Promise<SearchResult[]> {
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<CompanyDetails | null> {
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();
React Component Example
Here's a complete React autocomplete component:
import { useState, useCallback, useRef, useEffect } from 'react';
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<Company[]>([]);
const [isOpen, setIsOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [highlighted, setHighlighted] = useState(0);
const debounceRef = useRef<ReturnType<typeof setTimeout>>();
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<HTMLInputElement>) => {
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 (
<div className="relative">
<input
type="text"
value={query}
onChange={handleChange}
onKeyDown={handleKeyDown}
onFocus={() => results.length > 0 && setIsOpen(true)}
placeholder={placeholder || 'Search for a company...'}
className="w-full rounded-lg border px-4 py-2"
/>
{loading && (
<div className="absolute right-3 top-1/2 -translate-y-1/2">
<span className="animate-spin">⟳</span>
</div>
)}
{isOpen && results.length > 0 && (
<ul className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-lg border bg-white shadow-lg">
{results.map((company, index) => (
<li
key={company.ID}
onClick={() => handleSelect(company)}
onMouseEnter={() => setHighlighted(index)}
className={`cursor-pointer px-4 py-2 ${
index === highlighted ? 'bg-blue-50' : 'hover:bg-gray-50'
}`}
>
<div className="font-medium">{company['Company Name']}</div>
<div className="text-sm text-gray-500">
{[company['City'], company['Country Name']].filter(Boolean).join(', ')}
</div>
</li>
))}
</ul>
)}
</div>
);
}
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