Business List Quality & Change Detection avatar

Business List Quality & Change Detection

Pricing

from $1.00 / 1,000 input record processeds

Go to Apify Store
Business List Quality & Change Detection

Business List Quality & Change Detection

Clean supplied JSON or CSV business lists, flag duplicate candidates and compare snapshots before CRM imports. Includes branch-aware matching and review reasons.

Pricing

from $1.00 / 1,000 input record processeds

Rating

0.0

(0)

Developer

Emre Durukan

Emre Durukan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Categories

Security

Limited permissions

Share

Business List Quality and Snapshot Changes

Check a business list before importing it into your CRM. Find data quality issues, flag exact or possible duplicates, and compare an export with an earlier snapshot to see which supplied records are new or changed.

This Actor works with business records you provide as JSON or CSV. It returns normalized records, review reasons and row references. It does not crawl websites, collect leads, enrich records or update your CRM.

Version 0.3.0 is a paid release: $0.10 per successful analysis plus $0.001 per input record across both supplied snapshots. Review the Pricing tab and the counting rules below before starting a run.

Pricing and run budget

Prices are in USD. Each completed analysis costs $0.10, plus $0.001 for each input record in the current and previous snapshots combined. Duplicate and invalid records count too. Records present only in the previous snapshot count toward processing charges, even though the dataset contains results for current rows only.

InputAnalysisInput-record chargesTotal at these rates
100 current + 100 previous records$0.10200 × $0.001$0.30
5,000 current + 5,000 previous records$0.1010,000 × $0.001$10.10
Empty current + empty previous snapshot$0.10$0.00$0.10
Empty current + 100 previous records$0.10100 × $0.001$0.20

An empty current snapshot still completes an analysis and incurs the analysis fee. Previous input records are still counted, while the dataset has zero current result rows. CSV headers and skipped blank lines are not business records.

Set a sufficient maximum total charge for the complete run. Before analysis, the Actor checks the full quote against the available run budget. If it cannot cover the complete quote, the run fails before analysis or analysis/input-record event charges; it does not analyze a smaller paid subset. Billing is determined by the Actor's platform pricing, not an input switch. The Pricing tab on this Actor page shows the effective event prices.

Charges and output storage are not one transaction. A platform interruption or failure can leave partial charges, partial output or an incomplete billing state. A failed run does not imply that no events were charged. Check the run's charges and SUMMARY before deciding whether to start a new run. Review results only when the run succeeds and SUMMARY.output.complete is true.

Common uses

  • Prepare a client-supplied lead list for review before a CRM import.
  • Compare weekly or monthly business exports using stable source IDs.
  • Give an agency a review queue for conflicting identities, repeated records and malformed fields.
  • Keep known branches separate when businesses share a website or phone number.

Quick start

  1. Open the Actor's Input tab and use the JSON input editor.
  2. Paste the fictional example below.
  3. Review the Pricing tab, allow a sufficient maximum total charge, and start the run.
  4. When the run succeeds, check SUMMARY and confirm output.complete is true.
  5. Review the result rows in the default dataset, then export the dataset in your preferred format.
{
"currentRecords": [
{"id": "B001", "name": "Harbor Dental", "website": "https://harbor.example", "country": "US", "city": "Washington"},
{"id": "B002", "name": "Cedar Interior Studio", "website": "https://cedar.example", "country": "US", "city": "Washington"},
{"id": "B003", "name": "Maple Design", "website": "https://maple.example", "country": "US", "city": "Washington"},
{"id": "B003", "name": "Maple Design", "website": "https://maple.example", "country": "US", "city": "Washington"}
],
"previousRecords": [
{"id": "B001", "name": "Harbor Dental", "website": "https://harbor.example", "country": "US", "city": "Washington"},
{"id": "B002", "name": "Cedar Interiors", "website": "https://cedar.example", "country": "US", "city": "Washington"}
],
"matchKey": "id",
"includeContactDetails": false
}

This example produces four current result rows: one unchanged, one changed, one new and one exact_duplicate. The changed row identifies name in changedFields; the repeated Maple Design row points to current row 3 through duplicateOfRow.

For a first quality check without change detection, omit both previous-snapshot fields. This creates a baseline. An explicit previousRecords: [] instead means that you are comparing with an empty list, so valid current records can be marked new. Duplicate and invalid findings can take precedence in either mode.

JSON, CSV and field mapping

Supply exactly one current source: currentRecords or currentCsv. You may optionally supply exactly one previous source: previousRecords or previousCsv. JSON and CSV can be used in the same run, one format per snapshot.

CSV input is the text contents of a comma-delimited CSV, including its header row. It is not a URL or file path. UTF-8 text, a leading BOM and quoted cells are supported. Headers must be unique and nonempty, and every data row must have the same number of cells as the header.

Use fieldMap when your CRM or agency export uses different column names. The direction is canonical field → source column, and the same mapping applies to both snapshots:

{
"currentCsv": "CRM ID,Business Name,Company URL,Country\nB001,Harbor Dental,https://harbor.example,US\nB002,Cedar Interiors,https://cedar.example,US\n",
"fieldMap": {
"id": "CRM ID",
"name": "Business Name",
"website": "Company URL",
"country": "Country"
},
"countryCode": "US"
}

Supported canonical fields are id, name, website, phone, country, address, city, postalCode and branchId. A row needs a usable id or name. Unknown columns, including email, are ignored by the analysis.

OptionHow to use it
matchKeyUse a stable, unique id where possible. Alternatives are website and phone, with additional identity checks.
countryCodeOptional supported two-letter phone-region hint, such as US or TR. Use the business's actual country.
compareFieldsNonempty list of unique canonical fields to compare after matching. Defaults to all supported fields except id.
includeContactDetailsDefaults to false. Set to true only when you need normalized phone, street address and postal code values in the results.
maxRecordsOptional lower per-snapshot limit, from 1 to 5,000. Oversized snapshots fail; rows are not silently cut off.
deleteInputOnSuccessDefaults to false. Requests best-effort removal of the run's input record after successful output storage. See the data-handling notes below.

Matching and results

The default dataset contains one result item for each current row. The key-value store's SUMMARY record contains counts, quality issue totals, warnings, pricing metadata, completion information and input-cleanup status.

Use results only when the run succeeds and SUMMARY.output.complete is true. A failed or interrupted run can leave partial output; the presence of dataset items alone does not establish completion.

StatusMeaning
baselineProcessed without a previous snapshot.
newNo reliable match was found in the supplied previous snapshot.
changedA matched row differs in at least one selected field after normalization.
unchangedA matched row has no detected difference in selected fields.
exact_duplicateA repeated normalized record meets the strict duplicate conditions. Review before removing it.
possible_duplicateA duplicate candidate or ambiguous identity needs human review.
invalidA row cannot be processed reliably; inspect its reason codes.

Each item includes rowNumber, record, previousRecord, changedFields, reasonCodes, matchedPreviousRow, duplicateOfRow and candidateRows. Row references start at 1. matchedPreviousRow refers to the previous snapshot; duplicate and candidate references refer to the current snapshot.

Matching is conservative. Exact duplicates require identical normalized canonical values, no quality issues, and a stable ID or corroborating name, address and country. A shared name, website or phone alone does not prove a duplicate. Conflicting records with the same branch-qualified ID require review. Supply branchId for known locations: different explicit branches are kept distinct, and missing or ambiguous branch information can prevent a reliable match. The Actor never merges records automatically.

An earlier row missing from the current upload is not reported as deleted or closed. “New” means new to the supplied comparison, and “changed” means the supplied values differ after normalization. Neither label verifies a real-world business event.

Limits

  • Up to 5,000 records per snapshot, or 10,000 across current and previous snapshots.
  • Complete JSON input up to 8 MiB; each CSV text up to 3 MiB.
  • Up to 100 CSV columns and 2,000 characters per supported field value.
  • Duplicate candidate scanning considers at most 64 candidates per current row and returns at most 20 candidate references. Truncation is reported in warnings. This is not an exhaustive all-pairs search.

Phone and website checks normalize supplied values and flag some malformed data. They do not test website availability, phone reachability, contact ownership, business existence, marketing consent or legal identity. Review warnings and source records before importing, merging or discarding data. Runs can fail or be temporarily unavailable; check the run status before using output.

Data handling and use conditions

Submit only business information you are authorized to process, including any required permission from your client or organization. Business names, direct phone numbers and addresses can contain personal information. Do not submit credentials, payment information, sensitive personal data or unrelated information.

Supported record fields are sent over HTTPS to a Cloudflare-hosted analysis service. This includes supplied contact fields even when includeContactDetails is false: that option filters the output, not the processing input. Unknown columns such as email are excluded from the remote analysis request, but can remain in the original Actor input stored on Apify. Records are not sent to an LLM or an enrichment service, and websites listed in your records are not fetched.

By default, phone, address and postalCode are omitted from both current and previous result records. Names, IDs, websites, cities, countries and branch IDs remain. This is not anonymization and does not remove contact data from the uploaded input.

Apify inputs, datasets, logs and other storage remain subject to platform retention and account access settings. The Cloudflare application processes requests in memory, does not intentionally persist record bodies, and does not intentionally log record values. These application choices do not guarantee deletion of all infrastructure records or metadata.

With deleteInputOnSuccess: true, check SUMMARY.privacy.inputDeletionStatus for deleted or failed. Cleanup is best effort and concerns the run's input record only. It does not erase datasets, backups, logs, previous runs or copies you hold. Failed or interrupted runs may retain input. Set appropriate access and retention rules before submitting production data.

You remain responsible for decisions about corrections, imports, merges, retention and deletion. No guarantee is made that all errors, duplicates or changes will be found. Do not use the output as the sole basis for a consequential decision about a person or business.

Support

Use the Support tab on this Actor page to report an issue. Include the run ID, error code and a small fictional example that reproduces the problem. Remove customer records, credentials and unnecessary personal information before sharing. No response-time commitment is offered.