B2B Company Data Enrichment & Completion
Pricing
from $5.00 / 1,000 completed records
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
18 days ago
Last modified
Categories
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
- Normalizes each record (domain, website, email, phone, social URLs, company name).
- Checks your data contract: a required field counts as done only when it is present and valid, not merely non-empty.
- Detects gaps and works only on those.
- Avoids already-complete work: a record that already satisfies the contract makes no request at all.
- Uses internal public-company extraction: a small, bounded read of the company's own website (home, contact, about, imprint).
- Validates every candidate: business-level email addresses on the company's own domain, company social pages, public phone numbers.
- Tracks provenance: for every value Akatra adds you get the method, the source page, the attempt and the time.
- 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
| Field | Meaning |
|---|---|
records | Your 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, sourceDataset | Read the records from an existing Apify dataset instead. The dataset is only ever read, never changed. |
requiredFields | The data contract. See below. |
emailVerification | not_required (default), verified_if_possible, verified_required. See below. |
maxPagesPerWebsite | Public pages read per company website, 1 to 10 (default 5). |
maxConcurrency | Websites read at the same time, 1 to 50 (default 10). |
maxRecords | Process only the first N records. |
maxProviderAttempts | Attempts per missing field, 1 to 10 (default 3). |
maxBudgetUsd | Optional ceiling on external provider spend. Internal work is not counted. |
providerStrategy | internal_first (default) or internal_only. |
includeProvenance | Attach 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 isnot_checked.verified_if_possible: Akatra verifies when a mailbox-level verifier is available. If none is, the record can still becompleteand 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
status | Meaning |
|---|---|
complete | Every required field is satisfied. Soft warnings can remain (for example email verification under verified_if_possible). |
partial | Some required fields are unresolved after the allowed work. Not finding data is partial, not failed. |
budget_exhausted | A spending limit blocked the remaining work. |
failed | The record itself could not be processed. |
fieldStates value | Meaning |
|---|---|
existing | Already in your record and valid. Not worked on. |
resolved | Akatra found and validated it in this run. |
unresolved | Not found after the allowed work. |
unverified | Present and valid, but the verification you required has not happened. |
stopReason | Meaning |
|---|---|
contract_satisfied | Everything required is done. |
attempts_exhausted | Every eligible provider was tried and none returned a usable value. |
provider_unavailable | No provider can serve an unresolved field (for example address). |
identity_conflict | Your website and domain point to different businesses. Akatra does not guess. |
budget_blocked | A spending limit blocked the remaining work. |
engine_error | The 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.internalEstimatedUsdisnullbecause Akatra does not invent an internal price;externalProviderUsdis0unless an external provider was used. maxBudgetUsdlimits 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)
| Event | Price | When it is charged |
|---|---|---|
completed-record | $0.005 | The 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.001 | The 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
httpandhttpspages 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.txtand skips pages that are explicitly disallowed. Ifrobots.txtcannot 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_contentorno_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.
Support and legal note
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.