For a public page with a table or list, one Google Sheets formula pulls it into rows with no code. For anything the page does not show, such as headcount, industry or tech stack, do not scrape each company's site. Look the company up by domain.
This guide does both, in that order. It is the short version, with the requests and the output.
TL;DR
- Pull a public table into Google Sheets with
IMPORTHTML. About 2 minutes. - Enrich each domain with
POST /v1/companies/enrich. About 15 minutes for a first run of 10 domains, 10 records. - Size a bigger list for free with
GET /v1/companies/countbefore you spend anything.
Prerequisites
- A Google account for Sheets.
- A LeadOcean API key from app.leadocean.io. The free plan gives 1,000 records for life, no card, 5 requests a second.
curlin a terminal, or any HTTP client. Export the key:export LEADOCEAN_API_KEY=your_key.- A public page you are allowed to read. Check its terms and robots.txt first, and never scrape pages behind a login.
Step 1: Pull the table with IMPORTHTML
IMPORTHTML fetches a table or list from a URL and writes it into the sheet. Its syntax is IMPORTHTML(url, query, index): query is "table" or "list", and index counts tables from 1 (Google Docs Editors Help, October 2026).
Put this in cell A1 of a blank sheet:
=IMPORTHTML("https://example.com/members", "table", 1)If the cells stay empty, try index 2, 3 and so on. Tables rendered by JavaScript after the page loads will not appear, because the formula reads the raw HTML. For those, use a browser extension scraper, or copy the visible table by hand. The next steps work the same on any list of domains.
Step 2: Reduce it to a column of domains
Enrichment needs one key per company: the website domain. Add a column that strips the URL down.
=REGEXEXTRACT(C2, "^(?:https?://)?(?:www\.)?([^/]+)")Fill it down. You want a clean column like acme.com, one row per company, no duplicates. Use Data, Data cleanup, Remove duplicates.
Step 3: Look up one company by domain
Test one row before you run the list. POST /v1/companies/enrich takes a domain or a linkedin_url (API reference, October 2026).
curl -s -X POST https://api.leadocean.io/v1/companies/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "acme.com"}'A company we hold nothing for returns 404 and costs nothing. A hit costs one record.
Step 4: Replace site-by-site scraping with a filter
The usual reason to scrape a company's own site is to learn what it runs, such as live chat or a CRM. LeadOcean holds detected technologies for about 7.8M crawled sites, so you can search on them instead. First size the list. Counts are free.
curl -s -G https://api.leadocean.io/v1/companies/count \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "technologyCategories=Live chat" \
--data-urlencode "hqCountry=US"technologyCategories takes a category from a list of 105, such as Live chat or CRM. Check exact values with GET /v1/enums/tech_category before you guess. The response is an exact total plus breakdowns by industry, headcount band, HQ country, HQ state and NAICS.
Step 5: Pull the rows you want
When the count looks right, run the same filters as a search. The body is flat JSON, filters are arrays, and limit runs 1 to 100.
curl -s -X POST https://api.leadocean.io/v1/companies/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"technologyCategories": ["Live chat"], "hqCountry": ["US"], "limit": 10}'Each company returned is one record, so this spends 10. Page with meta.nextCursor. One search pages to 10,000 rows, then meta.depthCapped turns true. Narrow the filters instead of paging harder.
Step 6: Skip the terminal with an MCP client
If you would rather type than curl, add the LeadOcean MCP server at https://api.leadocean.io/mcp to Claude Code or another MCP client. Sign-in is OAuth, so you paste no key. Then ask:
Count US companies that use a live chat tool, by headcount band.
Do not pull any rows yet.The model calls leadocean_count_companies, which is free. To run the same calls on a schedule, see the n8n integration.
What you get
Response shape from the API reference, with placeholder values. POST /v1/companies/enrich returns a data object with these top-level groups:
{
"success": true,
"data": {
"company_data": {
"company_name": "Acme",
"company_domain": "acme.com",
"company_technology_categories": ["Live chat", "Analytics"]
},
"company_detected_technologies": {},
"company_metrics": {},
"meta": { "fetched_at": "2026-10-01", "schema": "leadocean.company.v1" }
},
"meta": { "credits": 1 }
}The Step 4 count is a real result. On 2026-10-21 the free leadocean_count_companies tool returned this for technologyCategories Live chat and hqCountry US (trimmed):
{
"total": 11359,
"totalIsExact": true,
"facets": {
"employee_range": [
{ "value": "1-10", "count": 6679 },
{ "value": "11-50", "count": 2759 },
{ "value": "51-200", "count": 977 }
],
"hq_state": [
{ "value": "California", "count": 1588 },
{ "value": "Florida", "count": 1110 },
{ "value": "Texas", "count": 1037 }
]
},
"credits": 0
}That is 11,359 companies, not people. Most are under 11 staff, so add employeeRange before you pull rows.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Sheet cell shows #N/A or stays empty | Page is rendered by JavaScript, or the index is wrong | Try other index values. Use a browser extension scraper for JS pages. |
| 401 | Missing or wrong x-api-key | Check $LEADOCEAN_API_KEY is exported in this shell. |
| 429 | Over the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After, then slow the loop. |
| 402 | Records spent. The free plan is 1,000 for life | Add a plan on pricing. |
| Empty results | Enum value is not exact, such as a lowercase category | Read GET /v1/enums and copy the string as written. |
meta.depthCapped is true | One search reached 10,000 rows | Split the search by country or headcount band. |
FAQ
Is web scraping legal?
Reading public pages is generally allowed, but terms of service, copyright and privacy law still apply. Skip login-gated pages, respect robots.txt, and do not collect personal data you have no basis to hold. This is not legal advice.
What if the page loads data with JavaScript?
IMPORTHTML sees only the raw HTML, so a JS-rendered table stays empty. Use a browser extension scraper, or find the page's data download. You only need the list of domains out of it.
How many records does this guide use?
Ten enrichments or ten search rows is 10 records. Counts, 404s and empty pages are free. The free plan holds 1,000 records.
Can I scrape emails and phones from company sites?
You can, but it is slow and the hit rate is poor. Find the people with people search, then call GET /v2/people/email/work or GET /v2/people/phone with their linkedin_url or person_id. Each v2 call costs one record, even on a miss.
See also: how to scrape LinkedIn data, scrape a website into Excel or Sheets, and personalized emails from website data.
Stop scraping sites one at a time. Look the company up by domain.
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →