Blitz Brand Lab · Sections

The production components, fed with the site’s own data

49 sections in 8 groups. Each one is the real component, rendered full width with the JSON of a real page, so what you see here is what ships. The card above each specimen says which file renders it, which JSON kind produces it, which templates call it and what it requires.

49 SECTIONS · 8 GROUPSREAL COMPONENTS · REAL JSONFULL WIDTH, OWN CONTAINERNO SYNTHETIC LAYOUT
01One kind, one component

A JSON kind maps to a component per template: split is CodeSplit on a product, UseCaseSplit on a use case, HubSplit on a hub.

02Fallbacks are not layouts

A missing asset or href switches a component to a degraded variant without an error. The contract lists what each one requires.

03Own container, own padding

Every section carries its .container and its padding-top. Nothing here is wrapped, so the geometry is the one of the live page.

04Never two card bands in a row

A rail, a matrix or a split follows a grid. Two consecutive grids read as one long grid.

01
Openings

Heroes: one per template, never two on a page

Four heroes, one H1 each. The framed hero carries a MotionScene in its proof frame; the centered heroes carry copy and two CTAs.

4 SECTIONS

ProductHero

components/product/ProductHero.astro

The centered product hero: tag, H1 with an accent segment, subtitle, two buttons. No KPI band below it since the September build (the stats field of the JSON is dormant).

Notputting a proof frame in it. A product opens on its logos band, the proof comes with the first section.

Used by
T2 Product
Contract
  • h1 (one per page)
  • subtitle
  • cta + secondaryCta ({label, href}); the template adds the data-cta ids
  • tag optional

Any market, mapped

Company search API with custom targeting

Search companies by industry, size, location, keywords, revenue, web traffic, SIC/NAICS codes, and signals to build smarter account lists.

JSON · content/products/company-search.json › hero
{
  "tag": "Any market, mapped",
  "h1": "Company search API with **custom targeting**",
  "subtitle": "Search companies by industry, size, location, keywords, revenue, web traffic, SIC/NAICS codes, and signals to build smarter account lists.",
  "primaryCta": {
    "label": "Get 1,000 free records",
    "href": "https://app.blitz-api.ai"
  },
  "secondaryCta": {
    "label": "API Docs",
    "href": "https://docs.blitz-api.ai/api-reference/company-search/company-search"
  }
}
See it on a live page →

UseCaseHero

components/usecase/UseCaseHero.astro

The framed hero (top border, hero, bottom border) with the MotionScene of the slug in its proof frame. Framed pages are the playbook pages.

Notrendering it without a scene: the media zone stays empty and the hero becomes a centered text block.

Used by
T3 Use caseT-hub DevelopersT-hub Integrations
Contract
  • h1, subtitle, primaryCta, secondaryCta
  • scene: a DiagramSpec from motion/specs/<dir>/<slug>.json
  • sceneLabel: the mono eyebrow of the proof frame (UC_META crumb, « DEVELOPER TOOLKIT »…)
By use case

Build your TAM with B2B data

Create target account lists using company filters, job signals, enrichment, and decision-maker discovery from one API infrastructure.

WORKFLOWBUILD TAM
LIVEBLITZ API · V2
JSON · content/use-cases/build-tam.json › hero (+ specs/use-cases/build-tam.json)
{
  "tag": "By use case",
  "h1": "Build your TAM with **B2B data**",
  "subtitle": "Create target account lists using company filters, job signals, enrichment, and decision-maker discovery from one API infrastructure.",
  "primaryCta": {
    "label": "Start building",
    "href": "https://app.blitz-api.ai"
  },
  "secondaryCta": {
    "label": "Contact us",
    "href": "/contact"
  }
}
See it on a live page →

PersonaHero

components/persona/PersonaHero.astro

The persona hero: « B2B data APIs for » on one line, the persona in accent on the next.

Used by
T4 Team
Contract
  • title + accent: the template splits the H1 after « for » or on **markers**
  • subtitle
  • cta, secondaryCta

B2B data APIs for
GTM Engineers

Use BlitzAPI to create search, enrichment, validation, and signal workflows directly inside your GTM stack.

JSON · content/teams/gtm-engineers.json › hero
{
  "tag": "Built for GTM Engineers",
  "h1": "B2B data APIs for GTM Engineers",
  "subtitle": "Use BlitzAPI to create search, enrichment, validation, and signal workflows directly inside your GTM stack.",
  "primaryCta": {
    "label": "Get 1,000 free records",
    "href": "https://app.blitz-api.ai"
  },
  "secondaryCta": {
    "label": "Contact us",
    "href": "/contact"
  }
}
See it on a live page →

PageHero

components/sections/PageHero.astro

The standard hero of internal pages when there is no product, playbook or persona to frame.

Notthe left variant with a bare .container: it carries the section envelope geometry (--container-inner + --pad-inner-s), otherwise its text sits on the left rail.

Used by
T-pricingT-contact
Contract
  • h1, tag, subtitle
  • align: center (pricing) or left (contact)
  • compact removes the bottom breathing
  • stats optional
Pricing

Same possibilities.
Different scales.

One API key, the whole data layer: companies, people, verified emails, phone numbers and job postings. Plans differ by speed, fair use policy and support, never by data.

Astro · pricing.astro
<PageHero tag={COPY.hero.tag} h1={COPY.hero.h1} subtitle={COPY.hero.subtitle} align="center" compact />
See it on a live page →
02
Bands and dividers

The bands that pace the page

Trust, logos, the hatched divider, the offer, the signed conversion band. They are shared by every directory and never redrawn locally.

6 SECTIONS

TrustedBand (product)

components/product/TrustedBand.astro

The « Trusted by the best GTM teams » band right under a product hero. 130px, uniform on the 15 products.

Used by
T2 Product
Contract
  • no props: reads the client logos of config
  • 298px bordered text cell + marquee with lateral fades

Trusted by the best GTM teams

Astro
<TrustedBand />  {/* components/product/TrustedBand.astro */}
See it on a live page →

TrustedBand (use case)

components/usecase/TrustedBand.astro

The same trust signal for the framed pages: a centered hook instead of the bordered cell.

Used by
T3 Use case
Contract
  • no props
  • centered hook with an accent span + 48s linear marquee

Trusted by the best GTM teams

Astro
<TrustedBand />  {/* components/usecase/TrustedBand.astro */}
See it on a live page →

LogoBand

components/sections/LogoBand.astro

The client marquee alone, for pages whose hero already carries its own proof.

Used by
T1 HomeT4 TeamT-hubT-aboutT-contact
Contract
  • no props (compact and preview flags)
  • 13 client logos, 48s marquee
Astro
<LogoBand />
See it on a live page →

SectionDivider

components/sections/SectionDivider.astro

The hatched band that separates the opening from the body, and the body from the closing. 92 → 60 → 50 → 44px across the breakpoints.

Notcompensating the next section by adjacency (:global(.A + .B)): those rules were dead in the cascade on 15 pages.

Used by
T1 HomeT2 ProductT3 Use caseT4 TeamT-hub
Contract
  • no props
  • the section after it opens with --pad-18xl on its own (self-sufficient sections rule)
Astro
<SectionDivider />

OfferBand

components/sections/OfferBand.astro

The pricing anchor: the strip states the entry price before a decision on landing pages. The band variant left the home on 2026-10-14 and is kept for a future page. No date.

Notwriting a figure in the component or the JSON. The offer changes in one file.

Used by
T1 Home (band)T-pricing (strip)every directory page before the CTA (strip)
Contract
  • variant: strip | band
  • href optional (strip links to /pricing)
  • every figure comes from config/pricing.ts (PRICING_BAND, PLANS)

PRICING

Every endpoint, on every plan.

Start at $99 a month with 10 requests per second per endpoint. Scale to $499 for 25 requests per second per endpoint and a fair use policy 25 times higher. Beyond that, Build Your Infra is fully custom.

1,000 FREE RECORDS, NO CARD

START: $99/MONTH
SCALE: $499/MONTH
BUILD YOUR INFRA: $999+
VERIFIED EMAILS + US PHONES
Astro
<OfferBand variant="strip" href="/pricing" />   {/* directory pages, before the CTA */}
<OfferBand variant="band" />                     {/* home */}
See it on a live page →

SignatureBand

components/sections/SignatureBand.astro

The signed conversion band before the FAQ: one brand signature per family, a line, the button to the API docs.

Used by
T2 Product (FAMILY_BANDS)T3 Use case (USECASE_BANDS)T4 Team (TEAM_BANDS)T-hub (HUB_BANDS)
Contract
  • signature: a BlitzSignatureId
  • eyebrow, text
  • href: the docsUrl of the page
  • proof: false when the page already shows that signature

SEARCH & DISCOVERY

Every search endpoint shares the same filters, the same pagination, and the same response shape.

API Docs
TIER 1VP SalesTIER 2Sales DirectorTIER 3Head of GrowthTIER 3Head of Growth
TS · config/signature-bands.ts › FAMILY_BANDS.search
{
  "signature": "cascade",
  "eyebrow": "SEARCH & DISCOVERY",
  "text": "Every search endpoint shares the same filters, the same pagination, and the same response shape."
}

<SignatureBand {...FAMILY_BANDS[data.family]} href={data.docsUrl} />
See it on a live page →
03
Editorial splits

Text on the rail, proof on the side

A split develops one argument. The column scale descends (title 28, intro 18, bullets 16), the CTA sits on a rule, and the proof frame carries one asset.

5 SECTIONS

FeatureLead

components/product/FeatureLead.astrokind: features (first section)

The editorial opening of a page that has no split: a lead column and a numbered list of capabilities with their asset. A page never opens on a grid.

Nota features section later in the page: from position 2 on, features render as GrayCards.

Used by
T2 Product (opening B)T-hub
Contract
  • title, subtitle
  • items[]: title, text, asset (4 items typical)

A record built like an API response, not a copy of a profile page

A profile URL is the smallest honest input in B2B data: no name collisions, no domain guessing, exactly one person.

Typed fields, not text blobs

Experience comes back as experiences[], one object per position with start and end dates, a job_is_current flag, the job description and the work location. Education, skills and certifications are arrays you can map straight into a schema. Nothing left to parse.

Current by design

The record reflects where the person works now. Job changes are detected, and stale records are removed from live circulation, so yesterday's title does not ship as today's answer.

Keys that survive your joins

The record carries the person's canonical LinkedIn URL, and every position carries the employer's LinkedIn URL, LinkedIn ID and domain: stable handles for resolving the same person and the same company across a CRM, a warehouse and a vendor export, however differently each of them spells the name.

An output that chains

This is the profile-first counterpart to the reverse lookups: use it when you already hold a LinkedIn URL and want the whole career, not a single contact point. Pass the same URL to Email Enrichment or Phone Enrichment when you also need a way to reach the person.

JSON · content/products/profile-enrichment.json › sections[0] (features)
{
  "kind": "features",
  "title": "A record built like an API response, not a copy of a profile page",
  "subtitle": "A profile URL is the smallest honest input in B2B data: no name collisions, no domain guessing, exactly one person.",
  "items": [
    {
      "title": "Typed fields, not text blobs",
      "text": "Experience comes back as experiences[], one object per position with start and end dates, a job_is_current flag, the job description and the work location. Education, skills and certifications are arrays you can map straight into a schema. Nothing left to parse.",
      "asset": "json-object"
    },
    {
      "title": "Current by design",
      "text": "The record reflects where the person works now. Job changes are detected, and stale records are removed from live circulation, so yesterday's title does not ship as today's answer.",
      "asset": "person-record"
    },
    {
      "title": "Keys that survive your joins",
      "text": "The record carries the person's canonical LinkedIn URL, and every position carries the employer's LinkedIn URL, LinkedIn ID and domain: stable handles for resolving the same person and the same company across a CRM, a warehouse and a vendor export, however differently each of them spells the name.",
      "asset": "resolver"
    },
    {
      "title": "An output that chains",
      "text": "This is the profile-first counterpart to the reverse lookups: use it when you already hold a LinkedIn URL and want the whole career, not a single contact point. Pass the same URL to Email Enrichment or Phone Enrichment when you also need a way to reach the person.",
      "asset": "enrichment-chain"
    }
  ]
}
See it on a live page →

CodeSplit

components/product/CodeSplit.astrokind: split

The product overview: eyebrow, accented title, vertical-bar bullets, the API Docs button, and the dark panel that receives the asset.

Notchanging the 72px before the button or the 556×448 panel: the hubs share these values, changing them misaligns the site.

Used by
T2 Product (opening A)
Contract
  • title, subtitle, eyebrow (family label)
  • bullets: items with text and no href
  • docs: the item with an href, else docsUrl
  • asset: the proof in the dark 556×448 panel

Search

Explore any market with custom targeting

Search companies by firmographics, location, keywords, traffic, revenue, ad activity, and signals to build smarter account lists from one request.

Search companies by industry, size, location, and keywords

Build TAMs and ABM lists from custom targeting

SearchLIVE
200 OKAPPLICATION/JSON
JSON · content/products/company-search.json › sections[0] (split)
{
  "kind": "split",
  "title": "Explore any market with custom targeting",
  "subtitle": "Search companies by firmographics, location, keywords, traffic, revenue, ad activity, and signals to build smarter account lists from one request.",
  "asset": "search-query",
  "items": [
    {
      "text": "Search companies by industry, size, location, and keywords"
    },
    {
      "text": "Build TAMs and ABM lists from custom targeting"
    }
  ]
}
See it on a live page →

ProductTabs

components/product/ProductTabs.astrokind: showcase

Several capabilities of one API, one at a time, without replacing the explanation with a big product capture.

Used by
T2 Product (opening A)
Contract
  • title
  • tabs[]: title, text, asset (one pane per tab)
  • accessible tabs, no-JS safe: every pane renders

From company search to market intelligence

Define markets

Build precise company universes. Start with any market and narrow it using company size, industry, location, keywords, revenue, web traffic, ad spend, SIC codes, or NAICS codes.

01Define marketsBLITZ API · V2
200 OKAPPLICATION/JSON

Detect signals

Find companies showing movement. Use company data and market signals to identify accounts that are growing, investing, hiring, advertising, or moving into a new phase.

02Detect signalsBLITZ API · V2
200 OKAPPLICATION/JSON

Build lists

Turn targeting into account lists. Create clean company datasets for outbound, ABM, territory planning, CRM enrichment, or market mapping without starting from static databases.

03Build listsBLITZ API · V2
200 OKAPPLICATION/JSON
JSON · content/products/company-search.json › sections[1] (showcase)
{
  "kind": "showcase",
  "title": "From company search to **market intelligence**",
  "items": [
    {
      "title": "Define markets",
      "text": "Build precise company universes. Start with any market and narrow it using company size, industry, location, keywords, revenue, web traffic, ad spend, SIC codes, or NAICS codes.",
      "asset": "filter-set"
    },
    {
      "title": "Detect signals",
      "text": "Find companies showing movement. Use company data and market signals to identify accounts that are growing, investing, hiring, advertising, or moving into a new phase.",
      "asset": "signal"
    },
    {
      "title": "Build lists",
      "text": "Turn targeting into account lists. Create clean company datasets for outbound, ABM, territory planning, CRM enrichment, or market mapping without starting from static databases.",
      "asset": "dataset",
      "assetValue": "65M+",
      "assetLabel": "COMPANIES"
    }
  ]
}
See it on a live page →

UseCaseSplit

components/usecase/UseCaseSplit.astrokind: split

Three variants derived from the content, never chosen by a prop: bullets, conversion band, showcase. The second split of a use case is the conversion band.

Nota second split without an href item: it silently falls back to « points » and the page shows two rows of the same shape.

Used by
T3 Use case (positions 1 and 4)
Contract
  • asset REQUIRED (or signature)
  • « points »: items with text = bullets, proof on the right
  • « cta »: exactly one item {title, href}, proof on the left
  • showcase: proof only

Create target account lists from better data

Build a TAM that your sales and growth teams can actually use: filtered, enriched, prioritized, and ready for CRM or outbound workflows.

Identify companies that match your ICP.
Add job and growth signals to your TAM.
Prioritize accounts before outreach.
ICP FILTERSLIVE
BLITZ API · V2
GTM LOOP · LIVE

Build a TAM your team can actually use

A static company list is not enough. BlitzAPI helps you create a target account market using company data, hiring signals, enrichment, and decision-maker discovery, so your team can focus on the accounts most likely to matter.

JSON · content/use-cases/build-tam.json › sections[0] and sections[3] (split)
{
  "kind": "split",
  "title": "Create target account lists **from better data**",
  "subtitle": "Build a TAM that your sales and growth teams can actually use: filtered, enriched, prioritized, and ready for CRM or outbound workflows.",
  "asset": "filter-set",
  "items": [
    {
      "text": "Identify companies that match your ICP."
    },
    {
      "text": "Add job and growth signals to your TAM."
    },
    {
      "text": "Prioritize accounts before outreach."
    }
  ]
}

{
  "kind": "split",
  "title": "Build a TAM your team can actually use",
  "subtitle": "A static company list is not enough. BlitzAPI helps you create a target account market using company data, hiring signals, enrichment, and decision-maker discovery, so your team can focus on the accounts most likely to matter.",
  "asset": "gtm-loop",
  "items": [
    {
      "title": "Start building your TAM",
      "href": "https://app.blitz-api.ai"
    }
  ]
}
See it on a live page →

HubSplit

components/hub/HubSplit.astrokind: split

The editorial split of the two hubs, extracted from CodeSplit: same geometry, a proof frame with a head and a foot line.

Used by
T-hub
Contract
  • eyebrow, title, subtitle
  • bullets: items with text (title deduced from a heading map)
  • links: items with href
  • asset + proof.head / proof.foot

AI-native tooling

BlitzAPI inside your AI tools

The BlitzAPI MCP server gives Claude, Cursor or VS Code one tool per API endpoint, the documentation and built-in GTM skills. Ask for data in a chat, or have your assistant write the script that pulls it: same endpoints, same parameters, same responses.

MCP server URL

Plug https://api.blitz-api.ai/mcp into Claude, Cursor, VS Code or any MCP-compatible tool, then sign in with your BlitzAPI account or send your API key.

Fewer hallucinations

Your assistant reads the actual API reference before it calls an endpoint, instead of guessing endpoints and parameter names.

BLITZAPI → YOUR AI TOOLSLIVE
HTTP 200$ curl -X POSTapi.blitz-api.ai/mcp
ONE TOOL PER ENDPOINT · DOCS · SKILLS
JSON · data/pages/developers.json › split
{
  "kind": "split",
  "eyebrow": "AI-native tooling",
  "title": "BlitzAPI inside your **AI tools**",
  "subtitle": "The BlitzAPI MCP server gives Claude, Cursor or VS Code one tool per API endpoint, the documentation and built-in GTM skills. Ask for data in a chat, or have your assistant write the script that pulls it: same endpoints, same parameters, same responses.",
  "items": [
    {
      "text": "Plug https://api.blitz-api.ai/mcp into Claude, Cursor, VS Code or any MCP-compatible tool, then sign in with your BlitzAPI account or send your API key."
    },
    {
      "text": "Your assistant reads the actual API reference before it calls an endpoint, instead of guessing endpoints and parameter names."
    },
    {
      "title": "BlitzAPI MCP server",
      "href": "/mcp"
    },
    {
      "title": "MCP setup guide",
      "href": "https://docs.blitz-api.ai/guide/integrations/MCP"
    }
  ],
  "asset": "terminal-command",
  "proof": {
    "head": "BLITZAPI → YOUR AI TOOLS",
    "status": "LIVE",
    "label": "api.blitz-api.ai/mcp",
    "alt": "Terminal command adding the BlitzAPI MCP server to an AI tool",
    "foot": "ONE TOOL PER ENDPOINT · DOCS · SKILLS"
  }
}
See it on a live page →
04
Grids, rails and matrices

One grammar of destination cells

Equal cells, 1px rules drawn by the gap, the destination type in mono deduced from the href by one shared table (lib/destination.ts, no fallback label), arrow visible at rest, whole cell clickable. No text block is vertically centered.

10 SECTIONS

GrayCards

components/product/GrayCards.astrokind: features · usecases · apis · steps

The gray cards (surface-alt, radius 4px) shared by three roles: capabilities, destinations with a rolling « Learn more », numbered steps.

Nottwo card bands in a row. A rail or a matrix replaces the second one.

Used by
T2 Product (features after position 1, Related APIs)
Contract
  • kind: plain | links | numbered
  • columns: 3 for 3 or 6 items, 2 for 2 or 4
  • links: every item carries an href; linkLabel optional

Built for smarter account discovery

Explore markets, identify high-fit accounts, and use signals to prioritize where your team should focus first.

Explore markets

Search any segment by combining firmographic, geographic, keyword, traffic, revenue, and classification filters.

Prioritize accounts

Use signals like growth, hiring, traffic, ad activity, and ICP fit to focus on companies with stronger potential.

Build workflows

Turn company results into people search, enrichment, validation, CRM sync, ABM, or outbound workflows.

From URL to typed record

One endpoint, POST /v2/enrichment/person (Person Enrichment in the API reference), turns a profile link into structured JSON.

01

Send the URL

POST the person's LinkedIn URL as person_linkedin_url. That is the whole request body; no name, no company, no guessing on your side.

02

We resolve the person

The URL is matched against 550M+ profiles. Every response carries found: on true, person holds the full profile; on false, person is null and nothing is counted.

03

Read the record

The full record lands in typed fields: identity, headline, about, location and connections count, then experiences[], education[], skills[] and certifications[], each one ready to map into a schema.

04

Write it back, or chain it

Pass the LinkedIn URL straight into email or phone enrichment, or write the record back to your CRM and join on the employer identifiers carried by each position.

JSON · content/products/company-search.json › features (plain) · relatedProducts (links) · steps (numbered)
{
  "kind": "features",
  "title": "Built for smarter account **discovery**",
  "subtitle": "Explore markets, identify high-fit accounts, and use signals to prioritize where your team should focus first.",
  "items": [
    {
      "title": "Explore markets",
      "text": "Search any segment by combining firmographic, geographic, keyword, traffic, revenue, and classification filters.",
      "icon": "/icons/Work-Flow.svg",
      "asset": "filter-set"
    },
    {
      "title": "Prioritize accounts",
      "text": "Use signals like growth, hiring, traffic, ad activity, and ICP fit to focus on companies with stronger potential.",
      "icon": "/icons/Data.svg",
      "asset": "icp-score"
    },
    {
      "title": "Build workflows",
      "text": "Turn company results into people search, enrichment, validation, CRM sync, ABM, or outbound workflows.",
      "icon": "/icons/Design.svg",
      "asset": "router"
    }
  ]
}

// links: Related APIs, derived from relatedProducts[]
[
  {
    "title": "Company Search API",
    "text": "Search and discover companies by industry, size, location, keywords, and signals. Build markets, identify accounts, and create better prospecting workflows.",
    "href": "/products/company-search"
  },
  {
    "title": "Profile Enrichment API",
    "text": "Turn one LinkedIn profile URL into the complete person record: typed JSON with headline, location, each role and its employer identifiers, education and skills.",
    "href": "/products/profile-enrichment"
  }
]
See it on a live page →

PlaybookGrid

components/usecase/PlaybookGrid.astrokind: features

The capability map: a closed matrix where every cell is a destination, its type in mono deduced from the href.

Used by
T3 Use caseT-hub (« Where to go next »)
Contract
  • items[]: title, text, href on EVERY item
  • the destination type comes from lib/destination.ts, the table shared with UseCaseMatrix and NextStep; an href it cannot type THROWS, there is no « Explore » fallback
  • currentPath: the cell of the current page becomes « You are here »
  • columns: 2 or 3

Built for every data workflow

Everything required to discover companies, identify buyers, enrich records, detect signals, and activate data from a single infrastructure layer.

You are hereBuild TAMCreate dynamic markets using companies, people, jobs and signals.
JSON · content/use-cases/build-tam.json › sections[2] (features)
{
  "kind": "features",
  "title": "Built for every **data workflow**",
  "subtitle": "Everything required to discover companies, identify buyers, enrich records, detect signals, and activate data from a single infrastructure layer.",
  "items": [
    {
      "title": "Build TAM",
      "text": "Create dynamic markets using companies, people, jobs and signals.",
      "href": "/use-cases/build-tam"
    },
    {
      "title": "Find Decision Makers",
      "text": "Identify decision makers and key stakeholders across every account.",
      "href": "/use-cases/find-decision-makers"
    },
    {
      "title": "Jobs Intelligence",
      "text": "Turn millions of job postings into signals and market intelligence.",
      "href": "/products/jobs-search"
    },
    {
      "title": "Enrich Records",
      "text": "Transform incomplete records into complete, actionable profiles.",
      "href": "/use-cases/profile-enrichment"
    },
    {
      "title": "Identity Resolution",
      "text": "Resolve domains, emails, LinkedIn URLs, and matching keys across fragmented datasets.",
      "href": "/use-cases/data-resolution"
    },
    {
      "title": "AI-Native Infrastructure",
      "text": "Plug agents and AI workflows into the data layer through the MCP server, Skills and typed SDKs.",
      "href": "/developers"
    }
  ]
}
See it on a live page →

ConnectorMatrix

components/hub/ConnectorMatrix.astrokind: showcase

The orchestration tools that call BlitzAPI. GUIDE cells link, HTTP MODULE cells say the truth.

Used by
T-hub Integrations
Contract
  • items[]: title, text, href only when a guide exists
  • logos read from src/logos/integrations/<slug>.svg, monogram fallback
  • linkLabel

Works with your stack

The tools your team already uses, one data engine behind them.

ClayGuidePaste a cURL command into Clay's HTTP API column and build high-volume enrichment waterfalls in Clay without writing code.n8nGuideChain waterfall search, lead enrichment, and CRM sync into autonomous n8n workflows with the HTTP Request node.MakeGuideAdd BlitzAPI to thousands of app scenarios with the HTTP module: Airtable, HubSpot, Google Sheets, and more.
ZapierHTTP moduleTrigger enrichment from any Zap with a webhook step and write results back to your connected apps.
AirtableHTTP moduleEnrich bases with company, contact, and email data through automations or your orchestration tool of choice.
Google SheetsHTTP moduleTurn spreadsheets into enrichment pipelines via Make, n8n, or a simple script.
HubSpotHTTP moduleHubSpot data enrichment on workflow triggers: refresh company records, add decision makers, and re-verify emails.
SalesforceHTTP modulePower flows and buttons with live enrichment: resolve, refresh, and complete records on demand.
PipedriveHTTP moduleKeep deals moving with enriched organizations and verified contact data.
SmartleadHTTP moduleFeed campaigns with validated emails and enriched personalization fields.
InstantlyHTTP moduleLoad clean, verified prospect lists straight into your sending infrastructure.
LemlistHTTP moduleLaunch campaigns on enriched contacts with emails checked before the first send.
JSON · data/pages/integrations.json › showcase
{
  "kind": "showcase",
  "title": "Works with **your stack**",
  "subtitle": "The tools your team already uses, one data engine behind them.",
  "linkLabel": "Guide",
  "items": [
    {
      "title": "Clay",
      "text": "Paste a cURL command into Clay's HTTP API column and build high-volume enrichment waterfalls in Clay without writing code.",
      "href": "https://docs.blitz-api.ai/guide/integrations/clay",
      "logo": "clay"
    },
    {
      "title": "n8n",
      "text": "Chain waterfall search, lead enrichment, and CRM sync into autonomous n8n workflows with the HTTP Request node.",
      "href": "https://docs.blitz-api.ai/guide/integrations/n8n",
      "logo": "n8n"
    },
    {
      "title": "Make",
      "text": "Add BlitzAPI to thousands of app scenarios with the HTTP module: Airtable, HubSpot, Google Sheets, and more.",
      "href": "https://docs.blitz-api.ai/guide/integrations/make",
      "logo": "make"
    },
    {
      "title": "Zapier",
      "text": "Trigger enrichment from any Zap with a webhook step and write results back to your connected apps.",
      "logo": "zapier"
    },
    {
      "title": "Airtable",
      "text": "Enrich bases with company, contact, and email data through automations or your orchestration tool of choice.",
      "logo": "airtable"
    },
    {
      "title": "Google Sheets",
      "text": "Turn spreadsheets into enrichment pipelines via Make, n8n, or a simple script.",
      "logo": "google-sheets"
    },
    {
      "title": "HubSpot",
      "text": "HubSpot data enrichment on workflow triggers: refresh company records, add decision makers, and re-verify emails.",
      "logo": "hubspot"
    },
    {
      "title": "Salesforce",
      "text": "Power flows and buttons with live enrichment: resolve, refresh, and complete records on demand.",
      "logo": "salesforce"
    },
    {
      "title": "Pipedrive",
      "text": "Keep deals moving with enriched organizations and verified contact data.",
      "logo": "pipedrive"
    },
    {
      "title": "Smartlead",
      "text": "Feed campaigns with validated emails and enriched personalization fields.",
      "logo": "smartlead"
    },
    {
      "title": "Instantly",
      "text": "Load clean, verified prospect lists straight into your sending infrastructure.",
      "logo": "instantly"
    },
    {
      "title": "Lemlist",
      "text": "Launch campaigns on enriched contacts with emails checked before the first send.",
      "logo": "lemlist"
    }
  ]
}
See it on a live page →

UseCaseRail

components/sections/UseCaseRail.astrokind: usecases · showcase

The editorial rail: one full row per destination instead of a second grid of cards.

Used by
T2 ProductT-hub Developers
Contract
  • items[]: title, text, href
  • no destination type: a numbered row, with the arrow only on a row that links
JSON · content/products/company-search.json › usecases
{
  "kind": "usecases",
  "title": "What teams build with Company Search",
  "items": [
    {
      "title": "Decision Makers Identification",
      "text": "Find the right people inside target accounts and map the stakeholders who matter most.",
      "href": "/use-cases/find-decision-makers"
    },
    {
      "title": "Build TAM",
      "text": "Create dynamic target markets by combining company data, people data, jobs, and signals.",
      "href": "/use-cases/build-tam"
    },
    {
      "title": "Data Resolution & Matching",
      "text": "Connect company names, domains, LinkedIn URLs, websites, and identifiers into cleaner datasets.",
      "href": "/use-cases/data-resolution"
    },
    {
      "title": "For Revenue Teams",
      "text": "Give revenue teams one data layer to find, enrich, and activate their next best accounts.",
      "href": "/teams/revops"
    }
  ]
}
See it on a live page →

WorkflowSteps

components/sections/WorkflowSteps.astrokind: steps

A sequence on a rail, numbered. The one component every template shares for « how it works ».

Used by
T2 ProductT3 Use caseT-hubT-contact
Contract
  • title, subtitle (the subtitle carries the endpoint on a product)
  • items[]: title, text

From URL to typed record

One endpoint, POST /v2/enrichment/person (Person Enrichment in the API reference), turns a profile link into structured JSON.

  1. Send the URL

    POST the person's LinkedIn URL as person_linkedin_url. That is the whole request body; no name, no company, no guessing on your side.

  2. We resolve the person

    The URL is matched against 550M+ profiles. Every response carries found: on true, person holds the full profile; on false, person is null and nothing is counted.

  3. Read the record

    The full record lands in typed fields: identity, headline, about, location and connections count, then experiences[], education[], skills[] and certifications[], each one ready to map into a schema.

  4. Write it back, or chain it

    Pass the LinkedIn URL straight into email or phone enrichment, or write the record back to your CRM and join on the employer identifiers carried by each position.

JSON · content/products/profile-enrichment.json › steps
{
  "kind": "steps",
  "title": "From URL to typed record",
  "subtitle": "One endpoint, POST /v2/enrichment/person (Person Enrichment in the API reference), turns a profile link into structured JSON.",
  "items": [
    {
      "title": "Send the URL",
      "text": "POST the person's LinkedIn URL as person_linkedin_url. That is the whole request body; no name, no company, no guessing on your side."
    },
    {
      "title": "We resolve the person",
      "text": "The URL is matched against 550M+ profiles. Every response carries found: on true, person holds the full profile; on false, person is null and nothing is counted."
    },
    {
      "title": "Read the record",
      "text": "The full record lands in typed fields: identity, headline, about, location and connections count, then experiences[], education[], skills[] and certifications[], each one ready to map into a schema."
    },
    {
      "title": "Write it back, or chain it",
      "text": "Pass the LinkedIn URL straight into email or phone enrichment, or write the record back to your CRM and join on the employer identifiers carried by each position."
    }
  ]
}
See it on a live page →

RelatedApis

components/usecase/RelatedApis.astrokind: apis · usecases

The endpoints that execute the playbook: title and proof on the left, every endpoint unfolded on the right.

Notan accordion. Below six entries everything is unfolded, and the destination link exists at rest.

Used by
T3 Use case
Contract
  • asset REQUIRED
  • endpoint: shown in the P1 frame (UC_META of the template)
  • items[]: title, href, text with \n separated bullets
JSON · content/use-cases/build-tam.json › apis (+ UC_META endpoint)
{
  "kind": "apis",
  "title": "APIs that power this playbook",
  "asset": "dataset",
  "items": [
    {
      "title": "Company Search",
      "text": "Search by industry, size, country, keywords, and market filters.\nCreate clean company lists from your ICP criteria.\nUse results as the foundation for enrichment and scoring.",
      "href": "/products/company-search"
    },
    {
      "title": "Jobs Signal",
      "text": "Track jobs by company and month.\nFilter by job title, description, location, and date posted.\nSpot growth, expansion, and team-building signals.",
      "href": "/products/jobs-signal"
    },
    {
      "title": "Waterfall ICP",
      "text": "Search each account with your ICP hierarchy.\nReturn the best available decision maker first.\nSend matched contacts to enrichment or outbound.",
      "href": "/products/waterfall-icp"
    }
  ]
}
See it on a live page →

CodeTabs

components/hub/CodeTabs.astrokind: code

The same call in cURL, Python and TypeScript, or the same server set up in each client. A developers page shows code.

Nota snippet without a source URL: the surface of an SDK is not deduced by analogy.

Used by
T-hub DevelopersT-hub MCP (setup per client)
Contract
  • method, endpoint, responseFields[]
  • items[]: label, lang, code, source (verbatim doc URL)
  • no-JS safe: every panel renders
  • more than three tabs: below 767px the tab menu wraps inside the frame (ct-menu--wrap), a tab label never breaks
  • strings and comments are colored through :global selectors under .ct-code (the spans are injected by set:html, outside the scope)

First call

The same call, three ways

A LinkedIn profile URL in, a verified work email out. Same endpoint, same parameters, same response. You pick the interface, not the API.

POST/v2/enrichment/email
curl -X POST https://api.blitz-api.ai/v2/enrichment/email \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"person_linkedin_url":"https://www.linkedin.com/in/example-person"}'
from blitz_api import BlitzAPI

client = BlitzAPI()  # reads BLITZ_API_KEY

email = client.enrichment.email(
    person_linkedin_url="https://www.linkedin.com/in/example-person"
)
if email.found:
    print(email.email)
import { BlitzAPI } from "blitz-api-js";

const client = new BlitzAPI(); // reads BLITZ_API_KEY

const email = await client.enrichment.email({
  person_linkedin_url: "https://www.linkedin.com/in/example-person",
});
if (email.found) console.log(email.email);
200 OKfoundemailall_emails[]
JSON · data/pages/developers.json › code
{
  "kind": "code",
  "eyebrow": "First call",
  "title": "The same call, **three ways**",
  "subtitle": "A LinkedIn profile URL in, a verified work email out. Same endpoint, same parameters, same response. You pick the interface, not the API.",
  "method": "POST",
  "endpoint": "/v2/enrichment/email",
  "responseFields": [
    "found",
    "email",
    "all_emails[]"
  ],
  "items": [
    {
      "title": "cURL",
      "source": "https://docs.blitz-api.ai/api-reference/people-enrichment/find-work-email",
      "code": "curl -X POST https://api.blitz-api.ai/v2/enrichment/email \\\n  -H \"x-api-key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"person_linkedin_url\":\"https://www.linkedin.com/in/example-person\"}'"
    },
    {
      "title": "Python",
      "source": "https://docs.blitz-api.ai/sdks/python",
      "code": "from blitz_api import BlitzAPI\n\nclient = BlitzAPI()  # reads BLITZ_API_KEY\n\nemail = client.enrichment.email(\n    person_linkedin_url=\"https://www.linkedin.com/in/example-person\"\n)\nif email.found:\n    print(email.email)"
    },
    {
      "title": "TypeScript",
      "source": "https://docs.blitz-api.ai/sdks/typescript",
      "code": "import { BlitzAPI } from \"blitz-api-js\";\n\nconst client = new BlitzAPI(); // reads BLITZ_API_KEY\n\nconst email = await client.enrichment.email({\n  person_linkedin_url: \"https://www.linkedin.com/in/example-person\",\n});\nif (email.found) console.log(email.email);"
    }
  ]
}
See it on a live page →

McpTools

components/mcp/McpTools.astrokind: tools (root field of data/pages/mcp.json)

The reference sheet of an MCP server: one row per tool, grouped by prefix like the docs, the tool name and the endpoint it calls in mono, then the name and what goes in and comes back. The grammar of EndpointToolkit (framed sheet, h3 family labels, stretched name link), with its own contract because most rows are not product pages.

Notstretching EndpointToolkit to it: the five team pages rely on its 4 to 8 product-only rows.

Used by
T-hub MCP
Contract
  • title (**accent**), subtitle
  • groups[]: {label, items[]}; items: {tool, product: "/products/<slug>", job} or {tool, name, endpoint?, href?, job}
  • a tool with a product page takes its name, endpoint and link from the product JSON (sections/product-ref.ts): retyping them THROWS
  • a tool without a product page links only to its API reference page, and only when it calls a documented endpoint; no endpoint, no chip and no arrow
  • tool names unique, products unique; the two counters (tools, API endpoints) are computed
  • note optional: the template composes RECORD_DEFINITION from config/pricing.ts into it
  • below 767px the endpoint chip is hidden and the row tightens (24 rows: 5,400px at 375 with it)

One tool per endpoint

Tools are named after what they return, and each API tool runs exactly one endpoint: what the API reference says about a parameter or a response applies unchanged.

Tools
16
API endpoints
16

People

Companies

JSON · data/pages/mcp.json › tools (first two groups)
{
  "title": "One tool **per endpoint**",
  "subtitle": "Tools are named after what they return, and each API tool runs exactly one endpoint: what the API reference says about a parameter or a response applies unchanged.",
  "groups": [
    {
      "label": "People",
      "items": [
        {
          "tool": "people_search",
          "product": "/products/people-search",
          "job": "Title, level, function and company filters in, matching people across companies out."
        },
        {
          "tool": "people_employee_finder",
          "product": "/products/employee-finder",
          "job": "A company in, its current employees by level, function and country out."
        },
        {
          "tool": "people_waterfall_icp",
          "product": "/products/waterfall-icp",
          "job": "A company and ranked title tiers in, the best-placed people out, tier by tier."
        },
        {
          "tool": "people_enrich",
          "product": "/products/profile-enrichment",
          "job": "A profile URL in, the person and their whole career out."
        },
        {
          "tool": "people_find_work_email",
          "product": "/products/email-enrichment",
          "job": "A profile URL in, the verified work email out, or Not Found instead of a guess."
        },
        {
          "tool": "people_find_phone",
          "product": "/products/phone-enrichment",
          "job": "A profile URL in, mobile and direct US phone numbers out."
        },
        {
          "tool": "people_lookup_by_email",
          "product": "/products/reverse-email-lookup",
          "job": "An email address in, the professional profile behind it out."
        },
        {
          "tool": "people_lookup_by_phone",
          "product": "/products/reverse-phone-lookup",
          "job": "A phone number in, the professional profile behind it out."
        }
      ]
    },
    {
      "label": "Companies",
      "items": [
        {
          "tool": "company_search",
          "product": "/products/company-search",
          "job": "Industry, size, location and funding filters in, matching companies out."
        },
        {
          "tool": "company_enrich",
          "product": "/products/company-enrichment",
          "job": "A company's LinkedIn URL in, its full firmographic record out."
        },
        {
          "tool": "company_find_by_domain",
          "product": "/products/domain-to-linkedin",
          "job": "A web domain in, the company's LinkedIn URL out."
        },
        {
          "tool": "company_get_domain",
          "name": "LinkedIn URL to Domain",
          "endpoint": "POST /v2/enrichment/linkedin-to-domain",
          "href": "https://docs.blitz-api.ai/api-reference/company-enrichment/linkedin-url-to-domain",
          "job": "A company's LinkedIn URL in, its web domain out."
        },
        {
          "tool": "company_tam_by_jobs",
          "product": "/products/tam-by-jobs",
          "job": "Job filters in, the companies hiring for them out, with their matching job counts."
        },
        {
          "tool": "company_tam_by_people",
          "product": "/products/tam-by-people",
          "job": "People filters in, the companies whose employees match out, with a count for each."
        },
        {
          "tool": "company_employees_by_country",
          "name": "Headcount by country",
          "endpoint": "POST /v2/enrichment/company-distribution-by-country",
          "href": "https://docs.blitz-api.ai/api-reference/utilities/company-employment-distribution",
          "job": "A company in, its headcount split by country out."
        },
        {
          "tool": "company_employees_by_department",
          "name": "Headcount by department",
          "endpoint": "POST /v2/enrichment/company-distribution-by-department",
          "href": "https://docs.blitz-api.ai/api-reference/utilities/company-department-distribution",
          "job": "A company in, its headcount split by department out."
        }
      ]
    }
  ],
  "note": {
    "title": "Records",
    "text": "Tool calls run on your BlitzAPI plan and count exactly like API calls.",
    "href": "/pricing",
    "label": "See plans"
  }
}
See it on a live page →

McpPrompts

components/mcp/McpPrompts.astrokind: prompts (root field of data/pages/mcp.json)

Prompts to paste once a tool is connected: index, title, the prompt in a light field, the copy button. Every prompt stays selectable text without JavaScript.

Notinventing prompts: they come from the docs page, so what they promise is documented.

Used by
T-hub MCP
Contract
  • title (**accent**), subtitle
  • items[]: {title, prompt}, taken from the docs (source)
  • the Copy button is the only scripted element, hidden until the script enables it; « Copied » is announced (aria-live); a refused clipboard says « Select and copy »

Five prompts to try

Paste one into your AI tool once the server is connected. The last one runs a large job the right way: as a script, not as one giant tool call.

  1. Find a decision maker

    Using BlitzAPI, find the Head of Sales at stripe.com and get their work email.

  2. Build a prospect list

    Find 25 VPs of Marketing at B2B SaaS companies in the US with 50 to 200 employees. Show name, title, company and profile URL in a table.

  3. Check your account

    How many records do I have left on my BlitzAPI plan, and what's my rate limit per endpoint?

  4. Ask the docs

    What parameters does the waterfall ICP keyword search accept? Show me a complete example JSON body.

  5. Write the pipeline

    Write a Python script that takes a list of company domains, converts each to a LinkedIn URL, then finds the VP of Sales using BlitzAPI waterfall search. Save the results to a CSV and include retry logic.

JSON · data/pages/mcp.json › prompts
{
  "title": "Five prompts **to try**",
  "subtitle": "Paste one into your AI tool once the server is connected. The last one runs a large job the right way: as a script, not as one giant tool call.",
  "source": "https://docs.blitz-api.ai/guide/integrations/MCP",
  "items": [
    {
      "title": "Find a decision maker",
      "prompt": "Using BlitzAPI, find the Head of Sales at stripe.com and get their work email."
    },
    {
      "title": "Build a prospect list",
      "prompt": "Find 25 VPs of Marketing at B2B SaaS companies in the US with 50 to 200 employees. Show name, title, company and profile URL in a table."
    },
    {
      "title": "Check your account",
      "prompt": "How many records do I have left on my BlitzAPI plan, and what's my rate limit per endpoint?"
    },
    {
      "title": "Ask the docs",
      "prompt": "What parameters does the waterfall ICP keyword search accept? Show me a complete example JSON body."
    },
    {
      "title": "Write the pipeline",
      "prompt": "Write a Python script that takes a list of company domains, converts each to a LinkedIn URL, then finds the VP of Sales using BlitzAPI waterfall search. Save the results to a CSV and include retry logic."
    }
  ]
}
See it on a live page →

DarkWorkflowGrid

components/sections/DarkWorkflowGrid.astrokind: features

The dark matrix « Built for every data workflow »: six workflows, one acquisition CTA, on ink.

Used by
T-about
Contract
  • title, subtitle, cta
  • items[]: title, text, icon
  • columns

Built for every data workflow

Everything required to discover companies, identify buyers, enrich records, detect signals, and activate data from a single infrastructure layer.

Build TAM
Create dynamic markets using companies, people, jobs and signals
Find Decision Makers
Identify decision makers and key stakeholders across every account.
Jobs Intelligence
Turn millions of job postings into signals and market intelligence.
Enrich Records
Transform incomplete records into complete, actionable profiles.
Identity Resolution
Resolve domains, emails, LinkedIn URLs, and matching keys across fragmented datasets.
AI-Native Infrastructure
Plug agents and AI workflows into the data layer through the MCP server, Skills and typed SDKs.
JSON · data/pages/about.json › features[1]
{
  "kind": "features",
  "title": "Built for every **data workflow**",
  "subtitle": "Everything required to discover companies, identify buyers, enrich records, detect signals, and activate data from a single infrastructure layer.",
  "items": [
    {
      "title": "Build TAM",
      "text": "Create dynamic markets using companies, people, jobs and signals"
    },
    {
      "title": "Find Decision Makers",
      "text": "Identify decision makers and key stakeholders across every account."
    },
    {
      "title": "Jobs Intelligence",
      "text": "Turn millions of job postings into signals and market intelligence."
    },
    {
      "title": "Enrich Records",
      "text": "Transform incomplete records into complete, actionable profiles."
    },
    {
      "title": "Identity Resolution",
      "text": "Resolve domains, emails, LinkedIn URLs, and matching keys across fragmented datasets."
    },
    {
      "title": "AI-Native Infrastructure",
      "text": "Plug agents and AI workflows into the data layer through the MCP server, Skills and typed SDKs."
    }
  ]
}
See it on a live page →
05
Persona and company

The blocks that carry a voice

Pains, the use-case matrix, the endpoint toolkit and chain, the founder quote, the stack quote, the counters. They read a persona JSON or about.json, and the template derives their props structurally.

7 SECTIONS

PainsSection

components/persona/PainsSection.astrokind: split + features (untitled)

The pains row: an editorial column, the persona scene, three cells. The template derives everything from the first split and the untitled features.

Notleaving its bottom padding above a bordered band: it reads as an empty ruled strip, rail to rail. That is what closesOnBand removes.

Used by
T4 Team
Contract
  • title + accent
  • heading, description, bullets (from the pains split)
  • learnMoreHref + learnMoreLabel REQUIRED: the {title, href} item without text of the pains split. No default label or link: a missing one THROWS
  • features: the 3 cells of the untitled features section, each with title and text; any other count THROWS in the T4 template (the grid has 3 fixed columns)
  • slug + sceneLabel: the scene in motion/specs/teams/
  • closesOnBand: set by the T4 template, where the gray UseCaseMatrix band follows. No bottom padding and no bottom rule on the grid: the band's top rule closes it

Replace manual
GTM research

Search, enrich, validate, and route B2B data at scale
BlitzAPI gives you clean endpoints to build the workflow once, run it at scale, and push structured outputs into the tools your team already uses.
Create repeatable workflows for outbound, CRM enrichment, and routing.
Send clean company, contact, email, and signal data into your stack.
Less manual work
Replace research, lookup, and enrichment tasks with API calls.
Cleaner GTM data
Structured outputs, ready for your CRM, outbound tools, and workflows.
Ready to scale
Run the same playbook across hundreds or thousands of accounts.
JSON · content/teams/gtm-engineers.json › pains split + untitled features
{
  "kind": "split",
  "title": "Replace manual **GTM research**",
  "subtitle": "Search, enrich, validate, and route B2B data at scale",
  "items": [
    {
      "text": "BlitzAPI gives you clean endpoints to build the workflow once, run it at scale, and push structured outputs into the tools your team already uses."
    },
    {
      "text": "Create repeatable workflows for outbound, CRM enrichment, and routing."
    },
    {
      "text": "Send clean company, contact, email, and signal data into your stack."
    },
    {
      "title": "Talk to the team",
      "href": "/contact"
    }
  ]
}

{
  "kind": "features",
  "items": [
    {
      "title": "Less manual work",
      "text": "Replace research, lookup, and enrichment tasks with API calls."
    },
    {
      "title": "Cleaner GTM data",
      "text": "Structured outputs, ready for your CRM, outbound tools, and workflows."
    },
    {
      "title": "Ready to scale",
      "text": "Run the same playbook across hundreds or thousands of accounts."
    }
  ]
}
See it on a live page →

WorkplaceSection

components/persona/WorkplaceSection.astrokind: split (founder) + split (Plug BlitzAPI)

The human proof of a persona page: the founder quote, the two dataset volumes, and how a GTM uses it in their stack. A four-cell matrix with mono headers: the quote spans two columns with the two volumes on its right, and the stack quote runs full width below.

Notputting a pipeline diagram back in it. EndpointChain, right above it on the team pages, already draws this role's calls; the generic PERSON, EMAIL, PHONE cell it used to carry contradicted that chain on all five pages.

Used by
T4 Team
Contract
  • quote: {text, name, role} from the founder split (« Name, Role » cut at the first comma)
  • stackQuote: {text, accent, label} from the « Plug BlitzAPI » split

A data layer for GTM systems that need to move fast

Founder

“BlitzAPI helps GTM teams replace manual research with programmable data workflows. Search accounts, identify decision makers, enrich records, validate emails, and send clean data into your systems.”
Portrait of Antoine Blitz
Antoine Blitz
Founder at BlitzAPI

Data layer · Continuous

PROFILES550M+Maya ChenAcmeUSLéo MartinNorthwindFRPriya RaoGlobexGB

Data volume · Aggregate

DATA POINTS1B+

Plug BlitzAPI into your stack

“Use BlitzAPI as the API layer behind your Clay workflows, CRMs, internal tools, automations, and custom agents. High-volume playbooks, without per-record anxiety.”
JSON · content/teams/gtm-engineers.json › founder split + « Plug BlitzAPI » split
{
  "kind": "split",
  "title": "A data layer for GTM systems that need to **move fast**",
  "items": [
    {
      "title": "Antoine Blitz, Founder at BlitzAPI",
      "text": "BlitzAPI helps GTM teams replace manual research with programmable data workflows. Search accounts, identify decision makers, enrich records, validate emails, and send clean data into your systems."
    }
  ]
}

{
  "kind": "split",
  "title": "Plug BlitzAPI into your stack",
  "items": [
    {
      "text": "Use BlitzAPI as the API layer behind your Clay workflows, CRMs, internal tools, automations, and custom agents. High-volume playbooks, without per-record anxiety."
    }
  ]
}
See it on a live page →

CountersBand

components/persona/CountersBand.astro

The odometer counters. Only authorized KPIs, and a note that defines what a record is.

Used by
T-about (aside of Manifesto)
Contract
  • counters[]: digitsUpper (10 digits, last = final), suffix, label, note
  • variant: about | workplace
Records in our data infrastructure

A record is any object the infrastructure operates on: companies, people, job postings and signals. One organisation can account for many.

JSON · data/pages/about.json › stats
[
  {
    "value": "1B+",
    "label": "Records in our data infrastructure",
    "note": "A record is any object the infrastructure operates on: companies, people, job postings and signals. One organisation can account for many."
  }
]
See it on a live page →

UseCaseMatrix

components/persona/UseCaseMatrix.astrokind: usecases

What a role builds with BlitzAPI: the destination matrix of the site on the gray band of the teams pages (light cells, strong rules). A stretched link: the title is the link, its ::after covers the cell, so the whole cell stays clickable while the type and the description are read in order outside the link. Arrow visible at rest on every breakpoint, focus ring drawn inside the cell. The title link carries a scroll-margin-top as tall as the row above it, so Shift+Tab brings the whole cell, ring and type included, clear of the sticky nav.

Notan aria-label on a cell that wraps its whole content: it replaces the content for screen readers, who then never hear the description. Also persona/PlaybooksGrid, the October version that points to the Learn routes, and usecase/PlaybookGrid, which sits on the page background and hides its arrow on touch screens.

Used by
T4 Team (after PainsSection)
Contract
  • title (**accent**), subtitle
  • items[]: title and text REQUIRED, href optional; the template requires exactly 6 (a full grid at 3 and 2 columns)
  • the destination type comes from lib/destination.ts, the table shared with PlaybookGrid and NextStep (USE CASE, PRODUCT, TEAM, INTEGRATIONS, DEVELOPER TOOLKIT, GUIDE, GLOSSARY, BENCHMARK, PRICING, ARTICLE, DOCS); an href it cannot type THROWS
  • an item without href is a plain cell labelled CAPABILITY, never a link
  • text: the OUTCOME of the use case. The call itself (what goes in, what comes back) belongs to the toolkit line: the two never restate each other
  • source: names the JSON in build errors

What Software Developers build with BlitzAPI

Features and pipelines backed by the same REST endpoints.

  • Capability

    Data products

    Add company, contact, job, and signal data inside SaaS products or internal tools.

JSON · content/teams/developers.json › usecases (one cell without href)
{
  "kind": "usecases",
  "title": "What Software Developers **build** with BlitzAPI",
  "subtitle": "Features and pipelines backed by the same REST endpoints.",
  "items": [
    {
      "title": "Data products",
      "text": "Add company, contact, job, and signal data inside SaaS products or internal tools."
    },
    {
      "title": "Enrichment APIs",
      "text": "Complete company and contact records with emails, phones, websites, and profile data.",
      "href": "/use-cases/profile-enrichment"
    },
    {
      "title": "Search features",
      "text": "Build company, people, and job search experiences with API-based filters and outputs.",
      "href": "/products/company-search"
    },
    {
      "title": "Validation layers",
      "text": "Validate emails, catch-all domains included, and store only addresses that were proven, never guessed.",
      "href": "/products/email-enrichment"
    },
    {
      "title": "Signal pipelines",
      "text": "Use job postings, hiring trends, and company signals to trigger product or GTM actions.",
      "href": "/products/jobs-signal"
    },
    {
      "title": "AI agents",
      "text": "Give agents reliable B2B data for research, enrichment, scoring, and workflow execution.",
      "href": "https://docs.blitz-api.ai/"
    }
  ]
}
See it on a live page →

EndpointToolkit

components/sections/EndpointToolkit.astrokind: toolkit (root field of a team JSON)

The means at hand, as an API reference sheet: grouped by family in the mega menu order, one row per endpoint with the method and path in mono, the name, what the call takes and returns, and the plans that open it when not every plan does. The whole row links to the product page through a stretched link: the API name is the link and its ::after covers the row, so the link is named by the API alone and the path, plans and job are read in order outside it. The job line is balanced (no one-word last line), and the name link carries a scroll-margin-top so Shift+Tab brings the whole row clear of the sticky nav.

Notretyping a product name or an endpoint in the page JSON, or writing a plan by hand: plans are computed from COMPARE_GROUPS in config/pricing.ts.

Used by
T4 Team (after UseCaseMatrix)
Contract
  • title (**accent**), subtitle
  • items[]: {product: "/products/<slug>", job}; 4 to 8, no duplicate (schema)
  • job: what the call takes and what it gives back (« Send X, get Y back »), never the outcome a use case card already states. Write it as plain text: the component glues the API status Not Found with a no-break space at render, so it never splits across two lines
  • name, method and path, family, link and plans are READ from the product JSON by sections/product-ref.ts; a reference that does not resolve THROWS with the file and the index
  • method and path render through EndpointCall (tone light)
  • each family label is an h3 that names its list (aria-labelledby)
  • the two counters (endpoints, data families) are computed, and wrap to two lines when they no longer fit side by side
  • note optional: {title, text, href, label}, rendered as the foot of the sheet; the calling template adds its data-cta

The calls behind each client playbook

Build the playbook once on these endpoints, then run it per client with that client's filters. One API key, structured JSON in and out.

Endpoints
05
Data families
03

Search & Discovery

  • POST /v2/search/companies Company Search API Send one client's ICP filters, get the matching companies back, each with its LinkedIn URL.
  • POST /v2/search/people People Search API Send a client's persona filters, get matching people across that client's market, each with their full position history.
  • POST /v2/search/waterfall-icp-keyword Waterfall ICP API Send an account and the client's title hierarchy, get the strongest available contact back.

Enrichment

  • POST /v2/enrichment/email Email Enrichment API Send a contact's LinkedIn URL, get a verified work email back, or Not Found.

Jobs Intelligence

  • POST /v2/jobs/company Jobs Signal API Send a target account's LinkedIn URL, get its live job postings back, filtered by title, location or posting date.

Client work

Running BlitzAPI inside client engagements you operate is a standard, supported use on any plan. Reselling the data needs the reseller agreement of Build Your Infra; embedding it in your own product is Blitz Embedded.

Book a call about reseller terms
JSON · content/teams/agencies.json › toolkit (with its note)
{
  "title": "The calls behind **each client playbook**",
  "subtitle": "Build the playbook once on these endpoints, then run it per client with that client's filters. One API key, structured JSON in and out.",
  "items": [
    {
      "product": "/products/company-search",
      "job": "Send one client's ICP filters, get the matching companies back, each with its LinkedIn URL."
    },
    {
      "product": "/products/people-search",
      "job": "Send a client's persona filters, get matching people across that client's market, each with their full position history."
    },
    {
      "product": "/products/waterfall-icp",
      "job": "Send an account and the client's title hierarchy, get the strongest available contact back."
    },
    {
      "product": "/products/email-enrichment",
      "job": "Send a contact's LinkedIn URL, get a verified work email back, or Not Found."
    },
    {
      "product": "/products/jobs-signal",
      "job": "Send a target account's LinkedIn URL, get its live job postings back, filtered by title, location or posting date."
    }
  ],
  "note": {
    "title": "Client work",
    "text": "Running BlitzAPI inside client engagements you operate is a standard, supported use on any plan. Reselling the data needs the reseller agreement of Build Your Infra; embedding it in your own product is Blitz Embedded.",
    "href": "https://cal.com/antoine-blitz-acepmu/discovery-call-build-your-infra-blitz-api",
    "label": "Book a call about reseller terms"
  }
}
See it on a live page →

EndpointChain

components/sections/EndpointChain.astrokind: workflow (root field of a team JSON)

How the calls chain: a dark panel on the gutter, a rail of numbered nodes joined by a dashed connector, one row per call (call, what it does, what it passes on). The connector runs three times when its step enters the viewport, under 5 seconds, then stays still; it never moves under reduced motion. Under 1100px the call stacks over its text and the output stays on the right; under 768px everything stacks. The « Passes on » column lines up by itself; the last output, in accent, is what the chain delivers. The API names are the only links, underlined at rest; the path is white, because blue on ink reads as a link on this site.

Nota step whose output the next one does not consume, and more than four steps: past four, the chain stops illustrating and competes with the text. The four horizontal cards of the first draft were dropped at capture (names and paths broken at 220px). Also an endless loop on the connector: movement that lasts more than 5 seconds must be stoppable by everyone (WCAG 2.2.2), not only under reduced motion.

Used by
T4 Team (after EndpointToolkit)
Contract
  • title (**accent**), subtitle
  • steps[]: {product, text, output}; 3 or 4, THROWS otherwise
  • every workflow product must be in toolkit.items (schema): the chain only uses calls the page presents
  • text: what the step does with the previous output in THIS sequence, not the call's input and output again (the toolkit line says those)
  • product data read by sections/product-ref.ts, as in EndpointToolkit; method and path render through EndpointCall (tone dark); the eyebrow count is computed

Workflow · four calls in sequence

From an ICP to verified contacts

One way to chain them from Clay, n8n or your own code: a market goes in, verified contacts at accounts that are hiring come out.

  1. Search & Discovery

    Company Search API

    POST /v2/search/companies

    Describe the market once: industry, size, location, keywords. Each company comes back with its LinkedIn URL.

    Passes on Target companies

  2. Jobs Intelligence

    Jobs Signal API

    POST /v2/jobs/company

    Pull each company's jobs, grouped by month, and keep the accounts hiring for the team you sell to.

    Passes on Accounts that are hiring

  3. Search & Discovery

    Waterfall ICP API

    POST /v2/search/waterfall-icp-keyword

    Run your title cascade on each account. The engine moves down a tier only when the one above comes back empty.

    Passes on Decision makers per account

  4. Enrichment

    Email Enrichment API

    POST /v2/enrichment/email

    Look up each decision maker's work email and keep the ones who come back with a verified address. The rest return Not Found.

    Delivers Verified contacts, ready to sequence

JSON · content/teams/gtm-engineers.json › workflow
{
  "title": "From an ICP to **verified contacts**",
  "subtitle": "One way to chain them from Clay, n8n or your own code: a market goes in, verified contacts at accounts that are hiring come out.",
  "steps": [
    {
      "product": "/products/company-search",
      "text": "Describe the market once: industry, size, location, keywords. Each company comes back with its LinkedIn URL.",
      "output": "Target companies"
    },
    {
      "product": "/products/jobs-signal",
      "text": "Pull each company's jobs, grouped by month, and keep the accounts hiring for the team you sell to.",
      "output": "Accounts that are hiring"
    },
    {
      "product": "/products/waterfall-icp",
      "text": "Run your title cascade on each account. The engine moves down a tier only when the one above comes back empty.",
      "output": "Decision makers per account"
    },
    {
      "product": "/products/email-enrichment",
      "text": "Look up each decision maker's work email and keep the ones who come back with a verified address. The rest return Not Found.",
      "output": "Verified contacts, ready to sequence"
    }
  ]
}
See it on a live page →

EndpointCall

components/sections/EndpointCall.astro

The chip of one API call: the method, then the path in mono, split with a <wbr> before each « / ». A segment stays whole while it fits the line; wider than the line (text at 200% on a phone), it breaks at its hyphen rather than overflow its frame. Under 767px the chip and the path flow inline, so a long path moves under the method instead of being squeezed beside it.

Nota new method badge written by hand in a component: three drifted in one pass, with three paddings, two line heights and two letter spacings.

Used by
EndpointToolkit (tone light)EndpointChain (tone dark)size sm with a linked path: ready for a mono meta line (DataQuality carried it until its redesign of 2026-09-24)
Contract
  • method + path, read by sections/product-ref.ts (or lib/benchmark-proof.ts › endpoint); a method or a path of the wrong shape THROWS
  • tone: light (filled accent chip, ink path) | dark (outlined on-dark chip, white path)
  • size: md (path in --text-tag) | sm (path in --text-micro, inside a mono meta line)
  • href optional: the path becomes a link, underlined at rest, 24px touch target. Never inside another link
  • the path color follows --ec-path-color when the caller sets it for a state (the toolkit row hover)

TONE LIGHT · SIZE MD · EndpointToolkit

POST /v2/search/waterfall-icp-keyword

TONE DARK · SIZE MD · EndpointChain

POST /v2/search/waterfall-icp-keyword

TONE LIGHT · SIZE SM · LINKED PATH

POST /v2/enrichment/person
Astro · the two callers, and the small linked size
<EndpointCall method={row.method} path={row.path} tone="light" />   {/* EndpointToolkit */}
<EndpointCall method={card.method} path={card.path} tone="dark" />   {/* EndpointChain */}
<EndpointCall method="POST" path="/v2/enrichment/person" href="/products/profile-enrichment" tone="light" size="sm" />   {/* inside a mono meta line */}
See it on a live page →
06
Closing

FAQ, proof, then the decision

Every directory page closes the same way: signed band, FAQ with structured data, the offer strip, the CTA. The order is part of the contract. **The eight benchmark pages are the one exception**: no FAQPage markup is allowed under /benchmarks since Google dropped it from results on 2026-05-07, and an offer strip beside a measurement would read as an ad. They close on the CTA alone.

3 SECTIONS

FaqSection

components/sections/FaqSection.astro

The FAQ as native <details>, zero JavaScript, with its structured data. The answers are indexed and read by agents (llms-full.txt).

Notrecommending a product by name in prose without checking it still exists: FAQ residues are invisible to the link checker.

Used by
every template
Contract
  • items[]: q, a
  • title optional
  • FAQPage JSON-LD emitted automatically

Frequently asked questions

What is a company search API?

An API that returns the companies matching your criteria, such as industry, employee range, headquarters location or keywords, as structured records you can page through. It answers which companies fit a market. To look up one company you already know, from its domain or LinkedIn URL, use Company Enrichment.

Can I use the company search API as a company lookup API?

Yes, when the lookup is by criteria: describe the companies you want, by industry, employee range, headquarters country or keywords in the profile, and get every match back with its LinkedIn URL, domain and headquarters. To look up one company you already know by its website, resolve the domain with Domain to LinkedIn, then pass the URL to Company Enrichment for the full profile.

Which filters can I combine?

Industry, company size, location, keywords, revenue, web traffic, ad activity, SIC and NAICS codes, and market signals, all combinable in a single query.

How many companies can I search?

Queries run against a data layer of 65M+ companies, so you can map entire markets instead of sampling from static databases.

What do I do with the results?

Chain company results into People Search, enrichment, validation, or CRM sync to go from a defined market to activated accounts in one workflow.

How can I test it on my ICP?

Sign up on app.blitz-api.ai with 1,000 free records and run your real targeting filters before you commit.

How do I page through a large company search?

Company search is cursor-paginated: pass the cursor from each response into the next request, and a null cursor in the response means you have reached the end. The Python and TypeScript SDKs iterate the pages for you.

JSON · content/products/company-search.json › faq
[
  {
    "q": "What is a company search API?",
    "a": "An API that returns the companies matching your criteria, such as industry, employee range, headquarters location or keywords, as structured records you can page through. It answers which companies fit a market. To look up one company you already know, from its domain or LinkedIn URL, use Company Enrichment."
  },
  {
    "q": "Can I use the company search API as a company lookup API?",
    "a": "Yes, when the lookup is by criteria: describe the companies you want, by industry, employee range, headquarters country or keywords in the profile, and get every match back with its LinkedIn URL, domain and headquarters. To look up one company you already know by its website, resolve the domain with Domain to LinkedIn, then pass the URL to Company Enrichment for the full profile.",
    "link": {
      "label": "See the company enrichment API",
      "href": "/products/company-enrichment"
    }
  },
  {
    "q": "Which filters can I combine?",
    "a": "Industry, company size, location, keywords, revenue, web traffic, ad activity, SIC and NAICS codes, and market signals, all combinable in a single query."
  },
  {
    "q": "How many companies can I search?",
    "a": "Queries run against a data layer of 65M+ companies, so you can map entire markets instead of sampling from static databases."
  },
  {
    "q": "What do I do with the results?",
    "a": "Chain company results into People Search, enrichment, validation, or CRM sync to go from a defined market to activated accounts in one workflow."
  },
  {
    "q": "How can I test it on my ICP?",
    "a": "Sign up on app.blitz-api.ai with 1,000 free records and run your real targeting filters before you commit."
  },
  {
    "q": "How do I page through a large company search?",
    "a": "Company search is cursor-paginated: pass the cursor from each response into the next request, and a null cursor in the response means you have reached the end. The Python and TypeScript SDKs iterate the pages for you."
  }
]
See it on a live page →

Testimonials

components/sections/Testimonials.astro

The testimonial carousel. Hidden in every production build while QUOTES_ARE_PLACEHOLDERS is true in config/testimonials.ts (the four quotes are placeholders until the real verbatims arrive); it renders on the dev server.

Used by
T1 HomeT-about
Contract
  • slides from config/testimonials.ts
  • hidden in production while QUOTES_ARE_PLACEHOLDERS is true

Not rendered in this build: QUOTES_ARE_PLACEHOLDERS is true in config/testimonials.ts. Flip it once the real verbatims are in, and the carousel appears here and on the home page.

Astro
<Testimonials />   {/* slides: config/testimonials.ts */}
See it on a live page →

CtaSection

components/sections/CtaSection.astro

The pre-footer CTA. Same words everywhere, so the site ends every chapter on the same decision.

Used by
every template
Contract
  • title, subtitle, buttonLabel, buttonHref optional (defaults from config/site.ts)
  • cta: the data-cta id, set by the template

Data shouldn't be
the problem

Grab. Connect. Build.

Astro
<CtaSection cta={ctaId('product', 'cta_section', 'primary')} />
See it on a live page →
07
Measurement figures

One claim, one drawing, one twin table

The benchmark figures follow their own grammar, because they publish measurements rather than copy: every figure carries its population and its source and keeps a readable table of the same numbers, and it states a CLAIM computed from the data unless the claim is dropped by explicit decision (claim={false}, the directory figure on /benchmarks). Color is never the only encoding, no red, and rank one is marked by weight alone. They read the `benchmarks` collection, which is generated: no number is ever written by hand.

8 SECTIONS

ChartFrame

components/benchmark/ChartFrame.astro

The common frame of every measurement figure: one claim in large type, the drawing, a source line, and the exact numbers either folded or open. It THROWS at build when claim, n or source is missing: a forgotten claim never turns into a silent untitled figure, and a figure without its population proves nothing. Dropping the claim is explicit, claim={false}, and leaves no element, margin or rule in its place.

Notwriting the claim by hand. Five universal claims have been published false in this module, each contradicted by the figure right below it. A claim is walked over the data before it is written, and when it only holds on one measure of a switchable figure, it NAMES that measure.

Used by
T-bench-hubT-bench-runT-bench-duel
Contract
  • claim: the sentence the figure demonstrates, COMPUTED from the data, or false to render no claim at all, a deliberate choice (the directory figure since session 47)
  • n: the population, mandatory
  • source: the test id, never a file name
  • readAs, date, tableOpen optional
  • readAsPrint optional: the paper variant of readAs, for a figure the PDF shows only in part
  • slot "table": the twin table

On accurate records per 100, the providers range from 31.9 to 82.4 on the same list.

5 measures, the same 6 providers ranked on each one, strongest at the top

Coverage

Does it return a person at all?

  1. 1Apollo96.8%
  2. 2Prospeo94.9%
  3. 3BlitzAPI90.4%
  4. 4MoltSets84.6%
  5. 5GetLeads.io81.1%
  6. 6QuickEnrich68.8%

Accurate per 100

Out of 100 you send, how many come back usable?

  1. 1BlitzAPI82.4
  2. 2Apollo70.0
  3. 3Prospeo52.7
  4. 4QuickEnrich43.8
  5. too close to separate5MoltSets43.7
  6. 6GetLeads.io31.9

Accuracy

When it answers, is the answer right?

  1. 1BlitzAPI91.2%
  2. 2Apollo72.3%
  3. 3QuickEnrich63.7%
  4. 4Prospeo55.5%
  5. 5MoltSets51.7%
  6. 6GetLeads.io39.3%

Right employer

Right company, whatever the title says?

separation not tested

  1. 1BlitzAPI93.7%
  2. 2Apollo87.6%
  3. 3QuickEnrich75.4%
  4. 4GetLeads.io67.7%
  5. 5Prospeo66.9%
  6. 6MoltSets65.8%

Stale rate

How often does it name a job the person left?

separation not tested

  1. 1BlitzAPI4.4%
  2. 2Apollo10.8%
  3. 3Prospeo17.5%
  4. 4QuickEnrich24.6%
  5. 5GetLeads.io32.2%
  6. 6MoltSets33.2%
Each column ranks the providers on one measure, strongest at the top.n = 3,445 · test 2026-09-17_combined
Astro · the frame throws when claim, n or source is missing
<ChartFrame claim={computed} n={d.targets} source={`test ${d.testId}`} readAs="..." />
See it on a live page →

DimensionRanks

components/benchmark/DimensionRanks.astro

Columns are measures, rows are ranks: it answers « depending on what I optimise, which one ». The column order is derived, not chosen: measures whose separation was TESTED first, then the one where the publisher places lowest, then the tightest race. The rule lives in the figure itself: « separation not tested » in a column header, « too close to separate » on a row.

Nota radar or a composite score. A radar adds units that do not add up and invents an area the measurement does not have; a composite score is the « Winner » the recipe forbids.

Used by
T-bench-hub
Contract
  • providers: slug, name, color, kpi, interval
  • measures: key, label, question, higherIsBetter, unit
  • duelHref: slug to href, optional
  • publisherName

5 measures, the same 6 providers ranked on each one, strongest at the top

Coverage

Does it return a person at all?

  1. 1Apollo96.8%
  2. 2Prospeo94.9%
  3. 3BlitzAPI90.4%
  4. 4MoltSets84.6%
  5. 5GetLeads.io81.1%
  6. 6QuickEnrich68.8%

Accurate per 100

Out of 100 you send, how many come back usable?

  1. 1BlitzAPI82.4
  2. 2Apollo70.0
  3. 3Prospeo52.7
  4. 4QuickEnrich43.8
  5. too close to separate5MoltSets43.7
  6. 6GetLeads.io31.9

Accuracy

When it answers, is the answer right?

  1. 1BlitzAPI91.2%
  2. 2Apollo72.3%
  3. 3QuickEnrich63.7%
  4. 4Prospeo55.5%
  5. 5MoltSets51.7%
  6. 6GetLeads.io39.3%

Right employer

Right company, whatever the title says?

separation not tested

  1. 1BlitzAPI93.7%
  2. 2Apollo87.6%
  3. 3QuickEnrich75.4%
  4. 4GetLeads.io67.7%
  5. 5Prospeo66.9%
  6. 6MoltSets65.8%

Stale rate

How often does it name a job the person left?

separation not tested

  1. 1BlitzAPI4.4%
  2. 2Apollo10.8%
  3. 3Prospeo17.5%
  4. 4QuickEnrich24.6%
  5. 5GetLeads.io32.2%
  6. 6MoltSets33.2%
Collection · benchmarks/contact-data-september-2026 › providers[].kpi + interval
[
  {
    "key": "coverage_pct",
    "label": "Coverage",
    "question": "Does it return a person at all?",
    "higherIsBetter": true,
    "unit": "pct"
  },
  {
    "key": "accuracy_pct",
    "label": "Accuracy",
    "question": "When it answers, is the answer right?",
    "higherIsBetter": true,
    "unit": "pct"
  },
  {
    "key": "net_yield",
    "label": "Accurate per 100",
    "question": "Out of 100 you send, how many come back usable?",
    "higherIsBetter": true,
    "unit": "num"
  },
  {
    "key": "correct_employer_pct",
    "label": "Right employer",
    "question": "Right company, whatever the title says?",
    "higherIsBetter": true,
    "unit": "pct"
  },
  {
    "key": "stale_pct",
    "label": "Stale rate",
    "question": "How often does it name a job the person left?",
    "higherIsBetter": false,
    "unit": "pct"
  }
]
See it on a live page →

HeadToHeadRail

components/benchmark/HeadToHeadRail.astro

One row per competitor, sorted by SMALLEST gap first, and the entry point to the five duel pages. Two counterweights are rendered unconditionally: the coverage the publisher gives up, and the contacts where the competitor is right and the publisher is not (not « wrong »: that count also holds the publisher’s unverifiable answers, which the methodology calls neither right nor wrong). That sentence is counterLine() of viz.ts, shared with the duel page: written twice, it drifted once.

Notreusing the accuracy of the scorecard. A duel is scored on the contacts BOTH returned; the two bases differ by a point or more, and mixing them publishes a false page nothing in the build would catch.

Used by
T-bench-hub
Contract
  • rows: shared, the two accuracies ON THAT BASE, gap, ahead, both coverages, theyWereRight, pair, href (orient each pair with pairSides() of viz.ts)
  • publisherName, publisherColor, publisherYield
ApolloBlitzAPI vs Apollo

On the 3,081 contacts both returned

BlitzAPI91.4%
Apollo73.3%

18.1 points apart, right employer and title.

Apollo answers more often: it returned a person for 96.8% of the full list against 90.4%.

Apollo was right where BlitzAPI was not on 108 of those 3,081 contacts.

Kept together, BlitzAPI first and Apollo on the misses: 86.9 accurate per 100 instead of 82.4, for 330 extra queries.

QuickEnrichBlitzAPI vs QuickEnrich

On the 2,287 contacts both returned

BlitzAPI93.4%
QuickEnrich64.7%

28.7 points apart, right employer and title.

Reach goes the other way here: BlitzAPI returned a person for 90.4% of the full list against 68.8%.

QuickEnrich was right where BlitzAPI was not on 18 of those 2,287 contacts.

Kept together, BlitzAPI first and QuickEnrich on the misses: 83.3 accurate per 100 instead of 82.4, for 330 extra queries.

ProspeoBlitzAPI vs Prospeo

On the 3,033 contacts both returned

BlitzAPI91.3%
Prospeo57.9%

33.4 points apart, right employer and title.

Prospeo answers more often: it returned a person for 94.9% of the full list against 90.4%.

Prospeo was right where BlitzAPI was not on 21 of those 3,033 contacts.

Kept together, BlitzAPI first and Prospeo on the misses: 84.2 accurate per 100 instead of 82.4, for 330 extra queries.

MoltSetsBlitzAPI vs MoltSets

On the 2,730 contacts both returned

BlitzAPI92.1%
MoltSets52.3%

39.8 points apart, right employer and title.

Reach goes the other way here: BlitzAPI returned a person for 90.4% of the full list against 84.6%.

MoltSets was right where BlitzAPI was not on 34 of those 2,730 contacts.

Kept together, BlitzAPI first and MoltSets on the misses: 84.8 accurate per 100 instead of 82.4, for 330 extra queries.

GetLeads.ioBlitzAPI vs GetLeads.io

On the 2,683 contacts both returned

BlitzAPI92.1%
GetLeads.io39.7%

52.4 points apart, right employer and title.

Reach goes the other way here: BlitzAPI returned a person for 90.4% of the full list against 81.1%.

GetLeads.io was right where BlitzAPI was not on 10 of those 2,683 contacts.

Kept together, BlitzAPI first and GetLeads.io on the misses: 83.4 accurate per 100 instead of 82.4, for 330 extra queries.

Collection · duels filtered on the publisher, + pairBest
{
  "a": "blitzapi",
  "b": "apollo",
  "shared": 3081,
  "both_accurate": 2150,
  "only_a": 666,
  "only_b": 108,
  "neither": 157,
  "a_accuracy_pct": 91.4,
  "b_accuracy_pct": 73.3,
  "gap": 18.1,
  "ahead": "blitzapi"
}
See it on a live page →

DuelScoreboard

components/benchmark/DuelScoreboard.astro

Two columns of equal width, always both rendered, so an empty one is SEEN empty. Three separation states: tested, too close to separate, not tested. An exact tie belongs to neither column. It opened the head to head pages until 2026-09-22, when DuelFaceOff replaced it: it said WHO was ahead, not by how much nor where on the scale. It is kept here as a specimen, like DuelLedger. The only part that stays a reference for a future two-sided figure is the pair of equal columns, both always rendered. Its counting is NOT: it predates separation() (viz.ts), puts a measure with no published interval in the better side’s column and in its « N of M », and its empty-column sentence counts those measures among the ones « this run separates ». That is why this specimen and DuelFaceOff, two cards below, count the same pair differently.

Nothiding the competitor column when it is empty: on a page published by one of the two providers measured, the empty column is the proof, not an embarrassment. And copying its counting: a future two-sided figure takes its leader and its denominator from separation(), like DuelFaceOff, so only separated rows are marked and M counts the rows that carry an interval.

Used by
specimen only since 2026-09-22
Contract
  • measures: label, higherIsBetter, unit, us, them, usMargin, themMargin
  • usName, themName, usColor, themColor

BlitzAPI is ahead on5 of 6

  • accuracy91.2% against 72.3%18.9 points, against a combined interval of 2.5
  • net yield82.4 against 70.012.4 points, against a combined interval of 2.8
  • correct employer93.7% against 87.6%6.1 points, separation not tested
  • stale4.4% against 10.8%6.4 points, separation not tested
  • history recall96.1% against 86.1%10.0 points, separation not tested

Apollo is ahead on1 of 6

  • coverage96.8% against 90.4%6.4 points, against a combined interval of 1.6
Collection · the two providers of one pair, six measures, three separation states · retired counting (untested measures counted as ahead): do not copy it, see separation()
{ key: 'coverage_pct', us: 90.4, them: 96.8, usMargin: 1, themMargin: 0.6 }

DuelFaceOff

components/benchmark/DuelFaceOff.astro

Two providers face to face on the main measures: one row per measure, mirrored bars growing out of a center column of labels, the same scale on every row and on both sides, the two values always written: every row shares one inset and one value column, the featured row included, so the outer end of every track is the same 100. The header prints « Ahead on N of M tested » on BOTH sides, M being the measures that carry a published interval (the only rows where a side can be marked), so a side at zero is seen at zero; when a name no longer fits its half, that side moves to its own line instead of overflowing. A side is marked ahead (weight AND the word « Ahead », never color alone) only where the gap is wider than both confidence intervals added together; without an interval the row prints the gap and « separation not tested »; a gap inside the intervals is « too close to separate »; an exact tie on the printed values belongs to neither side. Lower-is-better rows say so, and the marker follows the direction while the bar keeps encoding the value. One row can be featured: larger, in a panel, with one sentence saying what it combines. On the duel pages it is accurate records per 100 contacts, the measure the report ranks on, with its two factors (coverage, accuracy) right below it.

Notturning the featured row into a composite score. It must be a published measure the reader can recompute from the rows below it. And do not sort the rows by where the publisher leads: coverage, where the competitor leads against Apollo and Prospeo, stays the second row on every pair page.

Used by
T-bench-duel
Contract
  • measures: PairMeasure[] (viz.ts): key, label, name, note, base, higherIsBetter, unit, us, them, usMargin, themMargin, featured, eyebrow, explain
  • usName, themName, usColor, themColor: colors from paletteMap, computed on the whole edition
  • max: the common scale, 100 by default
  • THROWS on a missing or out-of-scale value, and on more than one featured row

BlitzAPIAhead on 2 of 3 tested

ApolloAhead on 1 of 3 tested

  1. Coverage

    a person returned at all

    % of queried · higher is better

    Apollo ahead by 6.4 pointsbeyond the combined interval of 1.6

    BlitzAPI: 90.4%

    Apollo: 96.8%Ahead

  2. Accuracy

    right current employer and a matching title

    % of answered · higher is better

    BlitzAPI ahead by 18.9 pointsbeyond the combined interval of 2.5

    BlitzAPI: 91.2%Ahead

    Apollo: 72.3%

  3. Wrong title

    right employer, the title does not match

    % of answered · lower is better

    BlitzAPI lower by 12.7 pointsseparation not tested

    BlitzAPI: 2.5%

    Apollo: 15.2%

  4. Stale employer rate

    an employer the person has left

    % of answered · lower is better

    BlitzAPI lower by 6.4 pointsseparation not tested

    BlitzAPI: 4.4%

    Apollo: 10.8%

Second specimen, same edition: MoltSets and QuickEnrich, the first pair of the run where a measure with an interval (accurate records per 100) sits inside the combined interval, to show the « too close to separate » state. No pair with BlitzAPI shows that state in this run. No pair of this run is level on a printed value, so the « Level » state has no real specimen.

MoltSetsAhead on 1 of 3 tested

QuickEnrichAhead on 1 of 3 tested

  1. Coverage

    a person returned at all

    % of queried · higher is better

    MoltSets ahead by 15.8 pointsbeyond the combined interval of 2.7

    MoltSets: 84.6%Ahead

    QuickEnrich: 68.8%

  2. Accuracy

    right current employer and a matching title

    % of answered · higher is better

    QuickEnrich ahead by 12.0 pointsbeyond the combined interval of 3.7

    MoltSets: 51.7%

    QuickEnrich: 63.7%Ahead

  3. Wrong title

    right employer, the title does not match

    % of answered · lower is better

    QuickEnrich lower by 2.5 pointsseparation not tested

    MoltSets: 14.1%

    QuickEnrich: 11.6%

  4. Stale employer rate

    an employer the person has left

    % of answered · lower is better

    QuickEnrich lower by 8.6 pointsseparation not tested

    MoltSets: 33.2%

    QuickEnrich: 24.6%

Collection · the pair's five main measures, built by duel/model.ts (faceOffMeasures), the same call as the live page
{
  "key": "coverage_pct",
  "label": "Coverage",
  "name": "coverage",
  "note": "a person returned at all",
  "base": "% of queried",
  "higherIsBetter": true,
  "unit": "pct",
  "us": 90.4,
  "them": 96.8,
  "usMargin": 1,
  "themMargin": 0.6
}
See it on a live page →

DuelLedger

components/benchmark/DuelLedger.astro

Every published segment, on four measures, walked in both directions. It was the transparency device of a duel page until session 46, when the section was removed for length: the WALK survives in the page, and its result is published as one generated sentence under the face-off figure of each duel page (FaceOffSection), right after the coverage rank sentence. The component is kept, and specimened here, because the sentence and it read the same walk, and because a longer form may be wanted again.

Notcopying the row tie flag. It is one boolean per row, computed on accurate records per 100 between the FIRST and the SECOND of that row: on a pair where the competitor sits fifth, it would mark « too close » a gap of several dozen points.

Used by
specimen only since session 46
Contract
  • rows: dimension, segment, n, measureLabel, unit, us, them, tieReadable, smallSample
  • segmentsScanned, measureLabels, usName, themName
  • emptyState: the written sentence, composed by the page

This is the EMPTY STATE, rendered on purpose: on two of the five published pairs the competitor is ahead on no row at all. Hiding the block would turn those two pages into a pitch, and take the other three down with them.

Collection · segments walked in both directions; the empty state is a written sentence
emptyState: 'The N published segments, each read on coverage, accuracy, accurate records per 100 and the stale rate, make N x 4 segment-and-measure comparisons. X is ahead of or level with BlitzAPI on none of them.'

EditionIndex

components/benchmark/EditionIndex.astro

The list of every published report, built to absorb one a month. It is ALWAYS rendered, even with a single edition: a shape never seen on screen has never been measured, and it would appear for the first time in production. Year grouping keys off the spread of years, not a count.

Notone data-benchmark for the whole page. At twelve editions, eleven reports would live under the test id of the twelfth, and the page would silently attribute one run’s measurements to another.

Used by
T-bench-hub
Contract
  • editions: slug, testId, testDate, headline, targets, providersRanked, spread (top minus bottom on the PRINTED values, printed() of viz.ts; shown with one decimal), pdfPath
  • visible: how many before the fold, 12 by default
    Show the 1 earlier edition
    Collection · every published run. Here with visible=0, so the fold renders with real data
    {
      "slug": "contact-data-september-2026",
      "testId": "2026-09-17_combined",
      "testDate": "2026-09-17T00:00:00.000Z",
      "headline": "B2B data provider benchmark, September 2026",
      "targets": 3445,
      "providersRanked": 6,
      "spread": 50.50000000000001,
      "pdfPath": "/benchmarks/contact-data-september-2026/report.pdf"
    }
    See it on a live page →

    RankTrend

    components/benchmark/RankTrend.astro

    How positions move from one edition to the next. It renders NOTHING below two editions of the same protocol version: a trend on one point is not a trend, and an empty state would promise instead of stating. It plots the RANK, not the value, because every edition is a different list of contacts.

    Notreading it as performance over time, and shipping it unmeasured. It has never been on screen, because no page has two comparable editions yet: the layout recipe runs again in full the month the second one lands.

    Used by
    T-bench-hub
    Contract
    • editions: slug, testDate, ranks per provider slug
    • colors, names
    • excludedProviders, excludedEditions for the footnote

    Not a measurement. One edition is published, so this specimen feeds the same ranks under two dates: the lines are flat by construction. It exists so the geometry, the direct labels and the axis are measured before the second edition lands, instead of appearing for the first time in production.

    BlitzAPI82.4Apollo70.0Prospeo52.7QuickEnrich43.8MoltSets43.7GetLeads.io31.9
    1. August 2026
    2. September 2026
    Position on accurate records per 100 contacts, by edition
    ProviderAugust 2026September 2026
    BlitzAPI1 (82.4)1 (82.4)
    Apollo2 (70.0)2 (70.0)
    Prospeo3 (52.7)3 (52.7)
    QuickEnrich4 (43.8)4 (43.8)
    MoltSets5 (43.7)5 (43.7)
    GetLeads.io6 (31.9)6 (31.9)

    Only providers present in every edition shown carry a line: a line drawn across an edition a provider did not take part in would not mean anything. Each edition is a different list of contacts, so a position can move because the list moved.

    LAYOUT SPECIMEN · the same edition twice, under two dates. Flat lines are the point: nothing here is a measurement
    editions: [{ testDate: 'August 2026', ranks }, { testDate: 'September 2026', ranks }]  // same ranks
    08
    Quality proof

    The benchmark, carried to other pages

    Six sections that show the measured quality of BlitzAPI data outside the report, in the grammar Antoine validated on the home: a text block with the button to the full report, and a small preview card of the figures. They share one shell (QualityShell), so the scope the recipe checks is always in the block: data-benchmark, « Measured on <date> on <n> profiles », the methodology link. Four show BlitzAPI alone and can go on any page. Two compare (the ranking, the head to head): they name competitors, so outside /benchmarks the recipe fails the page unless Antoine has listed it (the home carries the accuracy ranking since 2026-09-24). Every number is read from the benchmarks collection; the copy carries tokens only.

    6 SECTIONS

    DataQuality

    components/sections/DataQuality.astro

    A plain split section (redesign of 2026-09-24, Antoine: the ledger version was too big): text on the left (eyebrow, title, one paragraph, the button to the full report, the methodology link) and on the right a small preview card of the measured figures, one thin bar each, the measurement date at its foot. It proves the data quality in one glance and never takes over the page. The scope stays in the block because the recipe checks it there before it lets a quality percentage out of /benchmarks: data-benchmark on the root (the testId of a published edition that measures BlitzAPI), « Measured on <that edition’s date> on <BlitzAPI’s profile count> profiles » in its own text, a link to /benchmarks/methodology, not opened on html, body or main, not wrapping the page h1, and each quality percentage equal to the figure its label names.

    Notany comparison. No competitor name, no rank, no count of providers, no average: the helper does not return them, and anywhere outside /benchmarks the recipe fails on a measured provider name and on a comparison that names no one (a rank, the other providers tested, « better than other providers », a count or an average of providers). Never type a figure in the JSON either: the copy carries tokens, the collection carries the numbers.

    Used by
    No public page since 2026-09-24 (the home carries QualityRanking on accuracy instead): ready for T2 Product, T4 Team and T-pricing
    Contract
    • copy: an object of the calling page JSON (specimen copy: data/quality-blocks.json › dataQuality): eyebrow, title (**accent**), subtitle, cta.label, methodologyLabel, cardLabel, measured, and figures.<key> with label and caption. A missing field THROWS, naming the file and the field
    • copy carries tokens, never numbers: {edition} {date} {profiles} (and the count tokens of lib/benchmark-proof.ts). An unknown token THROWS, and « measured » must still read « Measured on <date> on <n> profiles » after filling
    • figures: the card rows, in order; coverage, accuracy, net_yield (the default, the report formula) and stale. Every value is on a scale of 100, so each bar is the value itself; net_yield is the only row in accent
    • numbers: lib/benchmark-proof.ts, from the latest published edition that CONTAINS the publisher. None THROWS; rates that disagree with the funnel counts THROW
    • track: the data-cta id of the report button, set by the calling page

    Data quality

    Accurate B2B data, measured in public

    Every month we run our own API on a list of real LinkedIn profiles and check each answer against the person's current employer and title. The full report, method included, is public.

    BlitzAPI · September 2026 benchmark

    • Coverage90.4%Profiles that came back with a person
    • Accuracy91.2%Answers with the right employer and title
    • Accurate records per 10082.4Coverage × accuracy, per 100 profiles sent

    Measured on September 17, 2026 on 3,445 profiles

    Data quality

    Accurate B2B data, measured in public

    Every month we run our own API on a list of real LinkedIn profiles and check each answer against the person's current employer and title. The full report, method included, is public.

    BlitzAPI · September 2026 benchmark

    • Coverage90.4%Profiles that came back with a person
    • Accuracy91.2%Answers with the right employer and title
    • Accurate records per 10082.4Coverage × accuracy, per 100 profiles sent
    • Stale employer4.4%Answers naming an employer the person has left

    Measured on September 17, 2026 on 3,445 profiles

    JSON · data/quality-blocks.json › dataQuality (numbers: collection benchmarks, lib/benchmark-proof.ts)
    {
      "eyebrow": "Data quality",
      "title": "Accurate B2B data, **measured in public**",
      "subtitle": "Every month we run our own API on a list of real LinkedIn profiles and check each answer against the person's current employer and title. The full report, method included, is public.",
      "cta": {
        "label": "Read the full report"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "BlitzAPI · {edition} benchmark",
      "measured": "Measured on {date} on {profiles} profiles",
      "figures": {
        "coverage": {
          "label": "Coverage",
          "caption": "Profiles that came back with a person"
        },
        "accuracy": {
          "label": "Accuracy",
          "caption": "Answers with the right employer and title"
        },
        "net_yield": {
          "label": "Accurate records per 100",
          "caption": "Coverage × accuracy, per 100 profiles sent"
        },
        "stale": {
          "label": "Stale employer",
          "caption": "Answers naming an employer the person has left"
        }
      }
    }
    
    <DataQuality copy={page.dataQuality} source="…" track={ctaId('<page>', 'inline', 'benchmark_report')} />
    <DataQuality copy={…} source="…" figures={['coverage', 'accuracy', 'net_yield', 'stale']} />
    See it on the home page →

    QualityStats

    components/sections/quality/QualityStats.astro

    Four measured figures of BlitzAPI in one row of tiles, for a page that wants the proof in one line of reading. BlitzAPI alone: no competitor, no rank.

    Nota fifth tile or a rank. The tiles are the report formula plus the stale rate, nothing more.

    Used by
    No public page yet: ready for T2 Product and T-pricing
    Contract
    • copy: eyebrow, title (**accent**), subtitle, cta.label, methodologyLabel, cardLabel, measured, figures.<key> label and caption (brand-lab copy: data/quality-blocks.json › stats)
    • figures: the tiles, in order (default: coverage, accuracy, net_yield, stale); net_yield is the only tile in accent
    • numbers: lib/benchmark-proof.ts (BlitzAPI alone)
    • layout: stacked (text and actions above, the tiles full width)

    Data quality

    Measured on real profiles, published in full

    Our monthly benchmark runs BlitzAPI on a list of real LinkedIn profiles and checks each answer against the person's current employer and title.

    BlitzAPI · September 2026 benchmark

    • Coverage90.4%Profiles that came back with a person
    • Accuracy91.2%Answers with the right employer and title
    • Accurate records per 10082.4Coverage × accuracy, per 100 profiles sent
    • Stale employer4.4%Answers naming an employer the person has left

    Measured on September 17, 2026 on 3,445 profiles

    JSON · data/quality-blocks.json › stats
    {
      "eyebrow": "Data quality",
      "title": "Measured on real profiles, **published in full**",
      "subtitle": "Our monthly benchmark runs BlitzAPI on a list of real LinkedIn profiles and checks each answer against the person's current employer and title.",
      "cta": {
        "label": "Read the full report"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "BlitzAPI · {edition} benchmark",
      "measured": "Measured on {date} on {profiles} profiles",
      "figures": {
        "coverage": {
          "label": "Coverage",
          "caption": "Profiles that came back with a person"
        },
        "accuracy": {
          "label": "Accuracy",
          "caption": "Answers with the right employer and title"
        },
        "net_yield": {
          "label": "Accurate records per 100",
          "caption": "Coverage × accuracy, per 100 profiles sent"
        },
        "stale": {
          "label": "Stale employer",
          "caption": "Answers naming an employer the person has left"
        }
      }
    }
    
    <QualityStats copy={page.qualityStats} source="…" track={ctaId('<page>', 'inline', 'benchmark_report')} />

    QualityVerdicts

    components/sections/quality/QualityVerdicts.astro

    What BlitzAPI answers are made of: one bar at 100 split into the four verdicts of the report, and its legend. Shares print as numbers out of 100 answers, without a % sign: a tight legend would otherwise let the recipe read one verdict under the next one’s label.

    Nota donut or a pie: four shares of one whole read faster on one bar, and the report draws the same bar.

    Used by
    No public page yet: ready for T2 Product (enrichment family)
    Contract
    • copy: the shell fields, scale, verdicts.correct, wrong_title, stale, unverifiable, count (token {n}) (brand-lab copy: data/quality-blocks.json › verdicts)
    • numbers: lib/benchmark-proof.ts › verdicts, which THROWS on a verdict the report does not know and on verdicts that do not cover every answer
    • colors: viz.ts › VERDICT_COLORS, the status palette of the report, never series colors

    Data quality

    What comes back when you call us

    Every answer BlitzAPI returned in the benchmark was checked against the person's current profile and sorted into one of four verdicts.

    BlitzAPI answers · September 2026

    Out of 100 answers

    • Right employer and title91.22,840 answers
    • Right employer, different title2.579 answers
    • Stale employer4.4138 answers
    • Unverifiable1.958 answers

    Measured on September 17, 2026 on 3,445 profiles

    JSON · data/quality-blocks.json › verdicts
    {
      "eyebrow": "Data quality",
      "title": "What comes back **when you call us**",
      "subtitle": "Every answer BlitzAPI returned in the benchmark was checked against the person's current profile and sorted into one of four verdicts.",
      "cta": {
        "label": "Read the full report"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "BlitzAPI answers · {edition}",
      "measured": "Measured on {date} on {profiles} profiles",
      "scale": "Out of 100 answers",
      "verdicts": {
        "correct": "Right employer and title",
        "wrong_title": "Right employer, different title",
        "stale": "Stale employer",
        "unverifiable": "Unverifiable"
      },
      "count": "{n} answers"
    }
    
    <QualityVerdicts copy={page.qualityVerdicts} source="…" />

    QualityHundred

    components/sections/quality/QualityHundred.astro

    « Send 100 profiles »: a 10 by 10 grid, one cell per profile, in three states. The most concrete reading of accurate records per 100.

    Notrounding each part on its own: 99 or 101 cells the month two remainders fall the same way.

    Used by
    No public page yet: ready for T2 Product and T4 Team
    Contract
    • copy: the shell fields, legend.accurate, returnedNotAccurate, notReturned, rounding (brand-lab copy: data/quality-blocks.json › hundred)
    • numbers: lib/benchmark-proof.ts › parts, split into whole profiles by the largest-remainder method on the printed values, so the three counts always add up to 100; the build THROWS if the parts do not make 100
    • the « came back, not verified accurate » cells are hatched, so the three states never rest on color alone

    Data quality

    Send 100 profiles, see what comes back

    Each square is one LinkedIn profile sent to our API in the benchmark: the accurate records, the answers that were not verified accurate, and the profiles that did not come back.

    100 profiles sent · September 2026

    • Came back accurate82
    • Came back, not verified accurate8
    • Did not come back10

    Rounded to whole profiles.

    Measured on September 17, 2026 on 3,445 profiles

    JSON · data/quality-blocks.json › hundred
    {
      "eyebrow": "Data quality",
      "title": "Send 100 profiles, **see what comes back**",
      "subtitle": "Each square is one LinkedIn profile sent to our API in the benchmark: the accurate records, the answers that were not verified accurate, and the profiles that did not come back.",
      "cta": {
        "label": "Read the full report"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "100 profiles sent · {edition}",
      "measured": "Measured on {date} on {profiles} profiles",
      "legend": {
        "accurate": "Came back accurate",
        "returnedNotAccurate": "Came back, not verified accurate",
        "notReturned": "Did not come back"
      },
      "rounding": "Rounded to whole profiles."
    }
    
    <QualityHundred copy={page.qualityHundred} source="…" />

    QualityRanking

    components/sections/quality/QualityRanking.astro

    Every ranked provider of the edition on one measure, as horizontal bars. Each bar carries the fixed hue of its provider and its name in full (the palette passes the dataviz validator with a color-vision reserve that requires this second encoding); the publisher carries the brand blue (Antoine, 2026-09-24) and stands out by weight too; two neighbours whose intervals do not separate them are marked « too close to separate »; the disclosure sits in the card.

    Nota « winner » row or a medal. And placing it on a new page outside /benchmarks without Antoine’s decision recorded in COMPARISON_PAGES: the recipe fails the page on the first competitor name.

    Used by
    T1 Home (measure accuracy_pct, since 2026-09-24: the first page outside /benchmarks listed in the recipe’s COMPARISON_PAGES)T-event /stateofgtme (measure accuracy_pct, linkDuels false, Antoine 2026-10-27)/benchmarks pages
    Contract
    • copy: the shell fields, disclosure (required: the recipe looks for it before it tolerates a competitor name), measures.net_yield, coverage_pct, accuracy_pct, measureNotes.<key> (optional one-line definition of the measure shown), close (brand-lab copy: data/quality-blocks.json › ranking)
    • measure: net_yield (default) | coverage_pct | accuracy_pct
    • numbers: lib/benchmark-compare.ts, the only helper that returns the provider list
    • linkDuels: each competitor name opens its head to head (default true)

    Benchmark

    One list, every provider measured the same way

    Each provider was queried on the same LinkedIn profiles and scored against a reference that belongs to none of them.

    Accurate records per 100 · September 2026

    1. 1BlitzAPI82.4
    2. 2Apollo70.0
    3. 3Prospeo52.7
    4. 4QuickEnrich43.8Too close to separate
    5. 5MoltSets43.7
    6. 6GetLeads.io31.9

    Measured on September 17, 2026 on 3,445 profiles

    BlitzAPI runs this benchmark and is one of the providers measured.

    JSON · data/quality-blocks.json › ranking (benchmarks pages only)
    {
      "eyebrow": "Benchmark",
      "title": "One list, **every provider measured the same way**",
      "subtitle": "Each provider was queried on the same LinkedIn profiles and scored against a reference that belongs to none of them.",
      "cta": {
        "label": "Read the full report"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "{measure} · {edition}",
      "measured": "Measured on {date} on {profiles} profiles",
      "disclosure": "BlitzAPI runs this benchmark and is one of the providers measured.",
      "measures": {
        "net_yield": "Accurate records per 100",
        "coverage_pct": "Coverage",
        "accuracy_pct": "Accuracy"
      },
      "close": "Too close to separate"
    }
    
    <QualityRanking copy={page.qualityRanking} source="…" measure="net_yield" />

    QualityHeadToHead

    components/sections/quality/QualityHeadToHead.astro

    BlitzAPI against one competitor on the three measures with a published interval, two bars each. A side is « ahead » only when the gap clears both intervals added together (viz.ts › separation, on the printed values); otherwise « too close to separate », and a tie belongs to no one. Coverage is always shown, including where the competitor leads: the concession is structural.

    Notdropping the coverage row on the pairs where the competitor leads on it, or marking a side ahead by color alone.

    Used by
    /benchmarks pages only (it names a competitor: the recipe fails any other page)
    Contract
    • copy: the shell fields (title and subtitle accept {provider}), disclosure, measures.<key>, ahead (tokens {name} and {gap}), close, level (brand-lab copy: data/quality-blocks.json › headToHead)
    • against: the competitor slug; an unknown or unranked slug THROWS
    • numbers: lib/benchmark-compare.ts; the CTA opens the head to head page of that competitor

    Head to head

    BlitzAPI vs Apollo, on the same list

    Both providers were queried on the same LinkedIn profiles and scored the same way. Where Apollo is ahead, the figure shows it.

    BlitzAPI vs Apollo · September 2026

    • Accurate records per 100

      BlitzAPI82.4
      Apollo70.0

      BlitzAPI ahead by 12.4 points

    • Coverage

      BlitzAPI90.4%
      Apollo96.8%

      Apollo ahead by 6.4 points

    • Accuracy

      BlitzAPI91.2%
      Apollo72.3%

      BlitzAPI ahead by 18.9 points

    Measured on September 17, 2026 on 3,445 profiles

    BlitzAPI runs this benchmark and is one of the providers measured.

    JSON · data/quality-blocks.json › headToHead (benchmarks pages only)
    {
      "eyebrow": "Head to head",
      "title": "BlitzAPI vs {provider}, **on the same list**",
      "subtitle": "Both providers were queried on the same LinkedIn profiles and scored the same way. Where {provider} is ahead, the figure shows it.",
      "cta": {
        "label": "Read the head to head"
      },
      "methodologyLabel": "How we test",
      "cardLabel": "BlitzAPI vs {provider} · {edition}",
      "measured": "Measured on {date} on {profiles} profiles",
      "disclosure": "BlitzAPI runs this benchmark and is one of the providers measured.",
      "measures": {
        "net_yield": "Accurate records per 100",
        "coverage_pct": "Coverage",
        "accuracy_pct": "Accuracy"
      },
      "ahead": "{name} ahead by {gap} points",
      "close": "Too close to separate",
      "level": "Level"
    }
    
    <QualityHeadToHead copy={page.qualityHeadToHead} source="…" against="apollo" />