B2B Company Data Enrichment & Completion avatar

B2B Company Data Enrichment & Completion

Pricing

from $5.00 / 1,000 completed records

Go to Apify Store
B2B Company Data Enrichment & Completion

B2B Company Data Enrichment & Completion

Complete missing company data from public sources. Add business emails, phones and company social profiles to existing records without re-enriching fields you already have. Built for CSV, API, AI agent and MCP workflows.

Pricing

from $5.00 / 1,000 completed records

Rating

0.0

(0)

Developer

Akatra

Akatra

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

18 days ago

Last modified

Share

Akatra B2B Data Completion Agent

Complete missing company data without enriching everything again.

Already have part of the data? Akatra identifies what is missing and completes only what you requested.

Send existing business records and the fields you need. Akatra checks which of those fields are already present and valid, reads the company's own public website for the rest, validates what it finds, records where every value came from, and stops as soon as your data contract is satisfied. Fields you already have are never worked on again.

What it does

  1. Normalizes each record (domain, website, email, phone, social URLs, company name).
  2. Checks your data contract: a required field counts as done only when it is present and valid, not merely non-empty.
  3. Detects gaps and works only on those.
  4. Avoids already-complete work: a record that already satisfies the contract makes no request at all.
  5. Uses internal public-company extraction: a small, bounded read of the company's own website (home, contact, about, imprint).
  6. Validates every candidate: business-level email addresses on the company's own domain, company social pages, public phone numbers.
  7. Tracks provenance: for every value Akatra adds you get the method, the source page, the attempt and the time.
  8. Stops when the contract is satisfied (or when no further eligible work exists) and tells you why.

Each record ends as complete, partial, budget_exhausted or failed, with a status for every requested field.

Why completion is different from full re-enrichment

A typical enrichment run asks for every field of every record, even when you already hold the value. Akatra starts from your data instead. If 40 of your 100 records already have the business email, those 40 emails are not searched, not fetched and not billed. Work is planned per missing field, cached per website (ten records of the same company read the site once), and capped per field.

Input

FieldMeaning
recordsYour company records (objects). Useful keys: companyName, website or domain, businessEmail, businessPhone, address, linkedinCompany, instagram, facebook, x, youtube. A recordId or id of your own is kept.
sourceMode, sourceDatasetRead the records from an existing Apify dataset instead. The dataset is only ever read, never changed.
requiredFieldsThe data contract. See below.
emailVerificationnot_required (default), verified_if_possible, verified_required. See below.
maxPagesPerWebsitePublic pages read per company website, 1 to 10 (default 5).
maxConcurrencyWebsites read at the same time, 1 to 50 (default 10).
maxRecordsProcess only the first N records.
maxProviderAttemptsAttempts per missing field, 1 to 10 (default 3).
maxBudgetUsdOptional ceiling on external provider spend. Internal work is not counted.
providerStrategyinternal_first (default) or internal_only.
includeProvenanceAttach provenance to every value Akatra found (default on).

Required fields

companyName, domain, website, businessEmail, businessPhone, address, linkedinCompany, instagram, facebook, x, youtube.

Akatra can currently complete businessEmail, businessPhone, linkedinCompany, instagram, facebook, x and youtube from public company websites. address can be required and checked, but it is not extracted in V1 (see Limitations); a missing address stays unresolved.

Email verification semantics

emailVerification controls what "done" means for businessEmail.

  • not_required: a valid business address is enough. Its verification status is not_checked.
  • verified_if_possible: Akatra verifies when a mailbox-level verifier is available. If none is, the record can still be complete and carries a soft warning (soft_gap_unresolved:businessEmail:verification_required).
  • verified_required: the email stays unresolved until mailbox-level verified evidence exists.

Verification statuses are never blurred: not_checked is not verified. mx_only is not verified. catch_all is not verified. unknown is not verified. verified means mailbox-level evidence. The current V1 has no mailbox-level verifier, so addresses are reported as not_checked, and verified_required records stay partial.

Output

Every input record produces one dataset item. The run also stores a compact SUMMARY record in the key-value store.

Each item answers the questions an AI agent or a script needs: which fields were requested (requestedFields), which already existed (fieldStates = existing), which Akatra resolved (resolvedFields), which remain unresolved (unresolvedFields) and why (unresolvedReasons), what was tried (attempts), where each accepted value came from (provenance), what it cost (cost), and why processing stopped (stopReason).

schemaVersion is the version of the Akatra result schema (currently 1), not the Actor code version. It changes only when the layout of an item changes in a breaking way.

Status meanings

statusMeaning
completeEvery required field is satisfied. Soft warnings can remain (for example email verification under verified_if_possible).
partialSome required fields are unresolved after the allowed work. Not finding data is partial, not failed.
budget_exhaustedA spending limit blocked the remaining work.
failedThe record itself could not be processed.
fieldStates valueMeaning
existingAlready in your record and valid. Not worked on.
resolvedAkatra found and validated it in this run.
unresolvedNot found after the allowed work.
unverifiedPresent and valid, but the verification you required has not happened.
stopReasonMeaning
contract_satisfiedEverything required is done.
attempts_exhaustedEvery eligible provider was tried and none returned a usable value.
provider_unavailableNo provider can serve an unresolved field (for example address).
identity_conflictYour website and domain point to different businesses. Akatra does not guess.
budget_blockedA spending limit blocked the remaining work.
engine_errorThe record could not be processed.

Provenance

For each value Akatra adds: providerId, method (mailto, visible_text, tel_link, json_ld, same_as, page_link, cloudflare_protected), sourceUrl, pageType (homepage, contact, about, imprint, guessed), attempt and discoveredAt. Akatra does not invent confidence scores, and rejected values are never stored.

Example input

{
"records": [
{
"recordId": "acme-001",
"companyName": "Plausible Analytics",
"website": "https://plausible.io",
"x": "https://x.com/plausiblehq"
}
],
"requiredFields": [
"businessEmail",
"linkedinCompany",
"x"
]
}

Example output

One dataset item (the website was read live; x was already in the record, so it was not searched):

{
"recordType": "result",
"schemaVersion": 1,
"recordId": "acme-001",
"aliases": [],
"status": "complete",
"company": {
"name": "Plausible Analytics",
"domain": "plausible.io",
"website": "https://plausible.io/",
"businessEmails": [
{
"address": "hello@plausible.io",
"verification": "not_checked"
}
],
"businessPhones": [],
"address": null,
"social": {
"linkedin": "https://www.linkedin.com/company/plausible-analytics",
"instagram": null,
"facebook": null,
"x": "https://x.com/plausiblehq",
"youtube": null
}
},
"requestedFields": [
"businessEmail",
"linkedinCompany",
"x"
],
"resolvedFields": [
"businessEmail",
"linkedinCompany"
],
"unresolvedFields": [],
"stopReason": "contract_satisfied",
"unresolvedReasons": {},
"fieldStates": {
"businessEmail": "resolved",
"linkedinCompany": "resolved",
"x": "existing"
},
"attempts": [
{
"providerId": "internal_web",
"capability": "businessEmailDiscovery",
"fields": [
"businessEmail"
],
"attempt": 1,
"status": "success",
"latencyMs": 328,
"estimatedCostUsd": 0.0,
"errorCode": null
},
{
"providerId": "internal_web",
"capability": "linkedinCompanyDiscovery",
"fields": [
"linkedinCompany"
],
"attempt": 1,
"status": "success",
"latencyMs": 0,
"estimatedCostUsd": 0.0,
"errorCode": null
}
],
"provenance": {
"businessEmail": [
{
"providerId": "internal_web",
"actorId": null,
"sourceUrl": "https://plausible.io/contact",
"method": "mailto",
"attempt": 1,
"discoveredAt": "2026-10-03T16:22:39+00:00",
"verification": null,
"estimatedCostUsd": null,
"pageType": "contact",
"context": null
}
],
"linkedinCompany": [
{
"providerId": "internal_web",
"actorId": null,
"sourceUrl": "https://plausible.io/",
"method": "page_link",
"attempt": 1,
"discoveredAt": "2026-10-03T16:22:39+00:00",
"verification": null,
"estimatedCostUsd": null,
"pageType": "homepage",
"context": "found_on_official_company_website"
}
]
},
"dataQuality": {
"completeness": 100.0
},
"cost": {
"providerCalls": 2,
"internalEstimatedUsd": null,
"externalProviderUsd": 0.0,
"akatraChargeUsd": null
},
"warnings": [],
"original": {
"recordId": "acme-001",
"companyName": "Plausible Analytics",
"website": "https://plausible.io",
"x": "https://x.com/plausiblehq"
},
"scrapedAt": "2026-10-03T16:22:39+00:00"
}

The SUMMARY record (abridged):

{
"recordType": "summary",
"schemaVersion": 1,
"processed": 1,
"complete": 1,
"partial": 0,
"budgetExhausted": 0,
"failed": 0,
"requestedFields": [
"businessEmail",
"linkedinCompany",
"x"
],
"resolvedFieldCounts": {
"businessEmail": 1,
"linkedinCompany": 1,
"x": 0
},
"unresolvedFieldCounts": {
"businessEmail": 0,
"linkedinCompany": 0,
"x": 0
},
"providerCalls": 2,
"providerFailures": 0,
"fallbackCount": 0,
"internalResolutionCount": 2,
"externalResolutionCount": 0,
"avoidedProviderCalls": 1,
"cost": {
"internalEstimatedUsd": null,
"externalProviderUsd": 0.0,
"akatraChargeUsd": null
},
"runtimeSeconds": 0.36,
"providerStrategy": "internal_first",
"externalProvidersUsed": false,
"internalWork": {},
"stopReasonCounts": {
"contract_satisfied": 1
},
"charging": {
"mode": "off",
"chargedEvents": 0,
"limitReached": false
},
"scrapedAt": "2026-10-03T16:22:38+00:00"
}

Cost and budget

  • Fields you already have are not worked on and are not billed.
  • Internal work (reading public pages) has no external provider cost. cost.internalEstimatedUsd is null because Akatra does not invent an internal price; externalProviderUsd is 0 unless an external provider was used.
  • maxBudgetUsd limits external provider spend only. Your Apify run limit still applies to everything.
  • Results are saved to the dataset first and charged afterwards, so a spending limit never discards finished records.
  • Memory defaults to 1024 MB.

Pricing (pay per event)

EventPriceWhen it is charged
completed-record$0.005The record ends complete and Akatra resolved at least one of your required fields in this run. One charge per record, however many fields were filled.
resolved-field$0.001The record does not end complete; charged once for each required field Akatra resolved and validated.

A record is charged with one event type at most, never both. There is no charge for fields that were already valid in your input, for records where Akatra resolved nothing, for failed records, for rejected person-like data, or for attempts that found nothing. Results are always saved before the charge, so reaching your run's maximum charge stops the run without losing saved records. Apify platform usage is included in the event prices.

Security

  • Akatra only fetches public http and https pages on ports 80 and 443. It resolves the host, checks every returned IP address, and connects to the checked address. Private, loopback, link-local, metadata and other internal addresses are blocked, including through redirects.
  • Redirects are followed manually (at most 3) and each hop is checked again. Response size, decompressed size, time and content type are limited.
  • It reads each site's robots.txt and skips pages that are explicitly disallowed. If robots.txt cannot be read, Akatra does not assume permission and does not crawl that host for that call.
  • Your source dataset is read-only. Results go only to this run's own dataset.
  • No cookies, no sign-in, no CAPTCHA solving, no browser impersonation. Requests identify themselves as AkatraB2BDataCompletion.
  • This is a defence against server-side request forgery, not a promise of absolute immunity.

Public company data only

Akatra returns company-level public information. It does not return or store names of people, job titles, personal email addresses, personal LinkedIn profiles or private mobile numbers. Role-based business addresses on the company's own domain (such as info@ or sales@) are accepted; abuse@, postmaster@ and similar technical addresses are not. Person-like keys and values in your own records are removed from the echoed original object. You are responsible for having a lawful basis to process the data you submit and the data you collect.

Limitations

  • Static HTML only. There is no browser rendering. Some JavaScript-heavy sites show no contact data in their HTML and stay unresolved (reported as insufficient_static_content or no_data_found).
  • No proxy by default. Some sites block requests from data centre addresses (HTTP 403 or 429); those records stay partial.
  • Email is the hardest field. Many companies publish only a contact form. Akatra accepts only role-based addresses on the company's own domain.
  • Address is not extracted in V1. A wrong address is worse than a missing one.
  • No personal contact enrichment (decision makers, employees, personal emails, private mobiles).
  • External providers may be unavailable depending on the Apify plan. V1 works internal-first and does not depend on them.
  • Website identity conflicts are not silently resolved (identity_conflict).
  • Email verification is not performed (see above).
  • Sites that disallow crawling in robots.txt, or deny access to it, are skipped.

What to expect. In a 100-company mixed test on 3 October 2026 (public company websites, every email and social field required, 1024 MB), Akatra found a business email for about 1 in 5 records that lacked one and a given social profile for roughly 55 to 65 percent of records that lacked it. Results depend heavily on the sites. Treat Akatra as a way to fill what public pages offer, not as a guarantee.

Use it from code, AI agents and MCP

Input and output are plain JSON and deterministic apart from timestamps and latencies.

curl -X POST "https://api.apify.com/v2/acts/akatra~b2b-data-completion-agent/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"records":[{"companyName":"Plausible Analytics","website":"https://plausible.io"}],"requiredFields":["businessEmail","linkedinCompany"]}'

For AI agents, add this Actor to the Apify MCP server (https://mcp.apify.com) and let the agent call it with records and requiredFields. Read status, unresolvedFields, unresolvedReasons and stopReason to decide what to do next; read provenance to cite where a value came from.

Questions or a site that should be handled differently: use the Issues tab on this Actor. Akatra reads publicly available company pages. Check the terms of the sites you process and the data protection rules that apply to you.