TL;DR
The ZoomInfo API gives contracted customers access to ZoomInfo's proprietary company, contact, intent, and enrichment data. ZoomInfo does not publish a simple self-serve API price. If your job is to turn public company websites into custom records, ScrapeGraphAI offers a different route: define one schema, extract each site, and save the normalized results. The companion Google Colab runs the complete workflow.
Search for the ZoomInfo API and two questions come up quickly: what data does it return, and how much does access cost? The first answer is public. The second requires a sales conversation.
ZoomInfo's official Enterprise API page lists search, enrichment, subscriptions, bulk, website visitor, and compliance APIs. Its pricing page describes flexible packages and asks visitors to request pricing. I checked both pages and the current ZoomInfo API documentation on August 12, 2026. None published a self-serve price for API access.
If you need ZoomInfo's proprietary contact database or buyer intent data, use ZoomInfo. If you need current facts published on company websites, you can build a custom company-intelligence API with ScrapeGraphAI. These products obtain their data in different ways.
What the ZoomInfo API provides
ZoomInfo sells access to a maintained B2B database. The Enterprise API product page groups its APIs into four jobs:
- Search and enrich company and contact records in the ZoomInfo database.
- Search or enrich large record sets and subscribe to changes.
- Identify website visitors through WebSights APIs.
- Process privacy and opt-out requests through its compliance API.
That is much broader than scraping a company homepage. A contracted customer can use ZoomInfo data to fill CRM records, search for contacts, monitor changes, or feed a go-to-market system. The official documentation also covers app creation, OAuth, client credentials, rate limits, pagination, batching, and bulk operations.
ZoomInfo's API is therefore a good fit when your application depends on data that the companies themselves do not publish on their sites. Direct phone numbers, verified contact records, proprietary buyer intent, and ZoomInfo's internal company identifiers belong in this category.
ZoomInfo API pricing and access
There is no public table that maps a ZoomInfo API request to a fixed dollar amount. ZoomInfo's pricing page offers packages across contact data and go-to-market applications, then routes the buyer to a pricing form. The Enterprise API page uses a request-demo flow as well.
That means a serious cost estimate needs a quote that matches your use case. Before that call, write down:
- which company, contact, intent, WebSights, or compliance endpoints you need
- how many records you expect to search, enrich, or refresh
- whether you need bulk delivery or subscriptions to data changes
- which systems will consume the data
Avoid treating third-party price estimates as an official ZoomInfo API price. Contract scope, record volume, product modules, and negotiated terms can change the number. The only defensible current answer is that API access is sales-led and the public pricing page does not quote a fixed API rate.
To get credentials, begin with ZoomInfo's sales and account process, then follow the Create App and Client Credentials Flow documentation for the app type approved on your account. This is the direct answer to "how to get a ZoomInfo API key": access starts with the appropriate ZoomInfo package, not a public developer key generator.
ZoomInfo API vs ScrapeGraphAI
ScrapeGraphAI does not reproduce the ZoomInfo database. It turns permitted public web pages into structured data that matches your schema.
| Requirement | ZoomInfo API | ScrapeGraphAI |
|---|---|---|
| Proprietary company and contact database | Yes | No |
| Private direct dials or verified personal emails | Available within licensed ZoomInfo data | No public-page extraction can guarantee them |
| Buyer intent data | ZoomInfo product data | Only signals visibly published on the web |
| Custom fields from company websites | Limited to the available ZoomInfo API model | Yes, define them in a JSON or Pydantic schema |
| Product, pricing, careers, and positioning pages | Not the main job | Direct extraction target |
| Output shape | ZoomInfo endpoint response | Your schema |
| Access | Contract and approved credentials | Self-serve API key |
The ScrapeGraphAI use case is custom public company intelligence. A developer can extract product names, positioning, customer segments, pricing links, careers links, API availability, or any other fact visible on the source page. The same schema works across sites, so the downstream CSV or CRM import does not need a parser for every layout.
Compared with ZoomInfo, the public-web dataset covers fewer kinds of data but gives you more control over the fields. It cannot include ZoomInfo's proprietary records. It can follow the pages and refresh schedule that matter to your application.
Install the SDK and get an API key
Create a free key in the ScrapeGraphAI dashboard, then install the current Python SDK and Pydantic:
pip install "scrapegraph-py>=2.1.0" "pydantic>=2"The Python SDK documentation covers the client in more detail. The same API is available through the JavaScript and TypeScript SDK or a direct HTTP request. The code below asks for the key at runtime, which makes it safe to paste into a notebook without storing the credential in the file.
Define one public company schema
The fields below have a useful property: a company can publish every one of them on its own site. There is no guessed revenue, employee count, funding history, or private contact data.
from pydantic import BaseModel, Field
class CompanyProfile(BaseModel):
company_name: str = Field(description="Company or product name shown on the page")
domain: str = Field(description="Canonical domain of the source website")
description: str = Field(description="One factual sentence describing the company")
industry: str = Field(description="Primary market or software category")
products: list[str] = Field(description="Up to eight products or product areas named on the page")
target_customers: list[str] = Field(description="Customer types named or described on the page")
has_public_api: bool = Field(description="Whether the page links to or advertises a public API")
has_enterprise_plan: bool = Field(description="Whether the page advertises an enterprise offering")
pricing_url: str | None = Field(description="Absolute pricing page URL if linked, otherwise null")
careers_url: str | None = Field(description="Absolute careers page URL if linked, otherwise null")The schema is intentionally compact. Add fields only when you know which page can support them. For example, open_roles belongs in a second extraction against the careers page, not in a homepage schema that tries to infer hiring activity from a navigation link.
Extract a company website into JSON
This block is self-contained. Paste it into a Python file or Colab cell, enter your key, and run it.
import json
from getpass import getpass
from pydantic import BaseModel, Field
from scrapegraph_py import ScrapeGraphAI
class CompanyProfile(BaseModel):
company_name: str = Field(description="Company or product name shown on the page")
domain: str = Field(description="Canonical domain of the source website")
description: str = Field(description="One factual sentence describing the company")
industry: str = Field(description="Primary market or software category")
products: list[str] = Field(description="Up to eight products or product areas named on the page")
target_customers: list[str] = Field(description="Customer types named or described on the page")
has_public_api: bool = Field(description="Whether the page links to or advertises a public API")
has_enterprise_plan: bool = Field(description="Whether the page advertises an enterprise offering")
pricing_url: str | None = Field(description="Absolute pricing page URL if linked, otherwise null")
careers_url: str | None = Field(description="Absolute careers page URL if linked, otherwise null")
api_key = getpass("ScrapeGraphAI API key: ")
sgai = ScrapeGraphAI(api_key=api_key)
source_url = "https://sentry.io/"
response = sgai.extract(
(
"Build a public company-intelligence record from this page. Use only facts "
"visible on the page or links present on it. Do not guess employee counts, "
"revenue, funding, private contact details, or headquarters. Return at most "
"eight product names."
),
url=source_url,
schema=CompanyProfile.model_json_schema(),
mode="normal",
)
if response.status != "success":
raise RuntimeError(response.error)
profile = CompanyProfile.model_validate(response.data.json_data)
record = {**profile.model_dump(), "source_url": source_url}
print(json.dumps(record, indent=2))I ran this exact extraction against Sentry on August 12, 2026. The response identified Sentry as an application-monitoring company, returned eight product areas, found its pricing and careers pages, and marked both the public API and enterprise offering as present.
{
"company_name": "Sentry",
"domain": "sentry.io",
"description": "Sentry provides an application monitoring platform that helps developers fix problems without compromising on velocity.",
"industry": "Application monitoring",
"products": [
"Error Monitoring",
"Logs",
"Session Replay",
"Metrics",
"Tracing",
"Agent Tracing",
"Profiling",
"Size Analysis"
],
"target_customers": [
"Developers",
"Startups",
"Enterprises",
"Software teams",
"Web developers",
"Mobile developers",
"Game developers"
],
"has_public_api": true,
"has_enterprise_plan": true,
"pricing_url": "https://sentry.io/pricing/",
"careers_url": "https://sentry.io/careers/",
"source_url": "https://sentry.io/"
}Notice that the application, not the model, attaches source_url. The caller already knows the source. Keeping that value outside the extraction schema prevents an otherwise correct record from failing because a page does not print its own URL in the visible content.
Discover official company websites first
If a CRM export already contains domains, skip this section. Start with the extraction loop below.
When you only have company names, ScrapeGraphAI Search can resolve the official homepages into a typed list. I tested this query with Sentry and Supabase. It returned https://sentry.io and https://supabase.com and excluded directories, news pages, and social profiles.
import os
from pydantic import BaseModel, Field
from scrapegraph_py import ScrapeGraphAI
class CompanySource(BaseModel):
company_name: str = Field(description="Official company name")
homepage_url: str = Field(description="Official company homepage URL")
match_reason: str = Field(description="Why this is the official company website")
class CompanySourceList(BaseModel):
companies: list[CompanySource]
sgai = ScrapeGraphAI(api_key=os.environ["SGAI_API_KEY"])
response = sgai.search(
"official websites for Sentry and Supabase",
num_results=6,
prompt=(
"Return exactly the official homepage for Sentry and the official homepage "
"for Supabase. Exclude directories, social profiles, news articles, and review sites."
),
schema=CompanySourceList.model_json_schema(),
)
if response.status != "success":
raise RuntimeError(response.error)
sources = CompanySourceList.model_validate(response.data.json_data)
for company in sources.companies:
print(company.company_name, company.homepage_url)Search is useful for discovery, not as a mandatory step on every refresh. Save each approved canonical domain. Subsequent jobs can extract those known URLs directly, which avoids rediscovering the same company and makes the dataset repeatable.
Enrich a company list with the same schema
The batch version changes only the list of URLs. extract_company returns one normalized dictionary per site, while Pydantic rejects a response that does not match the schema.
import os
from datetime import datetime, timezone
import pandas as pd
from pydantic import BaseModel, Field
from scrapegraph_py import ScrapeGraphAI
class CompanyProfile(BaseModel):
company_name: str = Field(description="Company or product name shown on the page")
domain: str = Field(description="Canonical domain of the source website")
description: str = Field(description="One factual sentence describing the company")
industry: str = Field(description="Primary market or software category")
products: list[str] = Field(description="Up to eight products or product areas named on the page")
target_customers: list[str] = Field(description="Customer types named or described on the page")
has_public_api: bool = Field(description="Whether the page links to or advertises a public API")
has_enterprise_plan: bool = Field(description="Whether the page advertises an enterprise offering")
pricing_url: str | None = Field(description="Absolute pricing page URL if linked, otherwise null")
careers_url: str | None = Field(description="Absolute careers page URL if linked, otherwise null")
sgai = ScrapeGraphAI(api_key=os.environ["SGAI_API_KEY"])
company_urls = ["https://sentry.io/", "https://supabase.com/"]
def extract_company(source_url: str) -> dict:
response = sgai.extract(
(
"Build a public company-intelligence record from this page. Use only facts "
"visible on the page or links present on it. Do not guess employee counts, "
"revenue, funding, private contact details, or headquarters. Return at most "
"eight product names."
),
url=source_url,
schema=CompanyProfile.model_json_schema(),
mode="normal",
)
if response.status != "success":
raise RuntimeError(f"{source_url}: {response.error}")
profile = CompanyProfile.model_validate(response.data.json_data)
return {
**profile.model_dump(),
"source_url": source_url,
"collected_at": datetime.now(timezone.utc).isoformat(),
}
records = [extract_company(url) for url in company_urls]
frame = pd.DataFrame(records)
frame.to_csv("company_intelligence.csv", index=False)
frameThe live Supabase result used the same schema and returned Postgres Database, Authentication, Edge Functions, Storage, Realtime, Vector, Data APIs, and Cron as product areas. It also found the pricing and careers URLs. No Supabase-specific parser or prompt was needed.
One schema across company sites is the maintenance advantage. The page markup can differ completely while your CSV columns remain stable. When the business question changes, update the schema instead of rewriting selectors for every domain.
For a broader pipeline that mixes directories and contact pages, the lead generation scraping guide covers CRM ingestion. If funding data is the main requirement, the Crunchbase scraper comparison explains the extra licensing and access constraints around that source.
Turn public pages into useful signals
A company profile is more useful when it answers a routing question. The two Boolean fields in this example already support a basic developer-tools segment: companies with a public API and an enterprise offer.
You can extend the pattern with separate, source-specific jobs:
- Extract plan names and prices from
pricing_url. - Extract role titles and locations from
careers_url. - Extract integration names from an integrations or marketplace page.
- Extract product launches from a changelog or company blog.
Keep the source URL and collection time on every record. A pricing page describes the page when you collected it, not a permanent fact. The same is true for jobs and product positioning. With snapshots, you can compare runs and decide which changes deserve a CRM update or an alert.
Do not collect personal data simply because a page exposes it. Choose fields that serve a defined business purpose, follow the site's terms, respect access controls, and handle deletion or opt-out requests where the law or your policy requires it. ScrapeGraphAI's built-in fetching removes infrastructure work; it does not decide whether a particular use is permitted.
Which API should you choose?
Choose the ZoomInfo API when your workflow depends on licensed ZoomInfo records, especially proprietary contacts, direct phone data, intent signals, WebSights, or subscription updates from its database. Ask ZoomInfo for a quote based on the endpoints and record volume you actually need.
Choose ScrapeGraphAI when the source is a permitted public website and the hard part is normalizing many different layouts into your own model. Product catalogs, pricing pages, careers pages, integration directories, and company positioning all fit this approach.
Some systems need both. ZoomInfo can provide a licensed company or contact record, while public-web extraction adds fields that are specific to your market and absent from a standard vendor schema. Keep source attribution on each field so the two datasets do not become indistinguishable inside the CRM.
The complete public-web workflow is ready in the Google Colab notebook. It installs the SDK, asks for your API key without saving it, discovers official domains, extracts Sentry and Supabase, and writes company_intelligence.csv.