Competitor Offer and Pricing Data Normalizer avatar

Competitor Offer and Pricing Data Normalizer

Pricing

from $8.50 / 1,000 delivered offer normalization candidates

Go to Apify Store
Competitor Offer and Pricing Data Normalizer

Competitor Offer and Pricing Data Normalizer

Normalize buyer-supplied offer evidence into review-required delivery candidates. Check submitted-field consistency, preserve terms and attribution metadata, and expose evidence gaps without fetching a source or claiming external verification.

Pricing

from $8.50 / 1,000 delivered offer normalization candidates

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

11 days ago

Last modified

Share

Offer Evidence Normalizer — Review-Required Offer Rows

Normalize buyer-submitted offer, coupon, landing-page, and ad evidence into deterministic machine-readable rows without pretending that syntactic consistency is external verification.

Built for: marketing operations, merchandising, affiliate operations, evidence-governance, and data-quality teams that already possess authorized source evidence and need a bounded normalization step before human review.

Commercial result unit: one Dataset delivery candidate whose linked result-found push is confirmed by an exact named-event counter increase of one. The separate automatic Actor start event also applies. A Dataset candidate can settle free when the counter delta is zero; current-run KVS OUTPUT, not the candidate row, is authoritative. Input rows, Dataset reads, rejected evidence, duplicate work, BYOK attempts, and withheld work are not additional result units.

Offer Evidence Normalizer: buyer input to evidence-backed action

Why this Actor exists

Offer evidence often arrives from several systems with different field names, mixed structured and unstructured terms, duplicate source records, and attribution fields that look more authoritative than they really are. A spreadsheet can standardize the easy columns, but it rarely preserves the exact normalization basis, duplicate conflicts, review boundary, current run identity, or billing state.

This Actor performs the narrow deterministic part:

  • validates a closed input contract;
  • accepts either bounded inline rows or one buyer-authorized Apify Dataset;
  • canonicalizes HTTPS URLs, domains, identifiers, arrays, and terms;
  • parses only explicitly supported offer-language patterns;
  • compares structured terms with bounded source text and retains conflicts;
  • collapses exact duplicate evidence keys while exposing disagreement;
  • checks whether submitted attribution fields are internally consistent;
  • emits one review-required row for each normalization-complete eligible offer;
  • optionally adds bounded labels with the buyer's OpenRouter key;
  • records current-run delivery, named-event, work-unit, budget, anomaly, KVS, and replay truth in OUTPUT.

It does not fetch a submitted source URL, authenticate a merchant, confirm that an offer is live, prove brand ownership, verify a coupon, establish legal rights, estimate conversion, measure demand, or authorize downstream automation.

Who uses it

  • marketing-operations teams that need one closed normalization contract before review;
  • merchandising and affiliate teams comparing submitted offer terms without asserting source truth;
  • data-governance teams that need duplicate, conflict, attribution, and retention boundaries;
  • CRM and warehouse integrators that need stable digests and a current-run delivery receipt;
  • agent builders that need explicit evidence gaps and safeToAutomate:false instead of a fabricated recommendation.

The buyer supplies the evidence and remains responsible for source rights, purpose, review, and downstream use. This Actor is useful where deterministic normalization is valuable but an external verification claim would be misleading.

What you get

Every Dataset delivery candidate uses output contract 2.0 and keeps input contract 1.0 explicit. The row records billing eligibility and intent, never final settlement. Important fields include:

Field dictionary

FieldMeaning
actorRunIdTrusted current Apify run binding.
attemptIndex / attemptDigestDeterministic identity of this run's one delivery attempt for the row.
entityBounded buyer-submitted brand identity projection; not verified identity.
offerNormalized offer kind, terms, dedupe key, and normalization digest.
entityAttribution.assessmentStatussubmitted_consistent only when submitted proof fields agree internally.
entityAttribution.externalVerificationPerformedAlways false. The Actor does not inspect the source.
normalizationNeedsReviewFalse only when deterministic term normalization is complete and conflict-free.
attributionNeedsReviewAlways true because attribution is not externally verified.
needsReviewAlways true on Dataset delivery candidates.
reviewReasonsExact reasons review is still required.
decision.confidenceEvidence-sufficiency score, basis, and explicit gaps—not an offer-truth probability.
decision.recommendedActionHuman routing instruction.
decision.safeToAutomateAlways false.
resultCandidateAlways true: this row was submitted through the linked Dataset path; it is not a paid-settlement claim.
decision.billingDeclares eligibility, intent, unit, and settlementSource: current_run_kvs_output; it never claims the row was charged.
analysisStatus / labelsOptional BYOK labeling status and bounded labels.
usageAlways null on Dataset rows; provider usage is stored once at run level in OUTPUT.

Evidence and boundaries

The critical truth boundary

The input phrase verified_by_source is retained as submitted metadata. It does not mean this Actor contacted or verified that source. When the submitted proof type, domain, source entity ID, URL, and typed identifiers agree under the closed rules, the output says submitted_consistent. It also says:

  • verificationScope: submitted_fields_only;
  • externalVerificationPerformed: false;
  • safeToAutomateBusinessDecision: false;
  • attributionNeedsReview: true;
  • decision.safeToAutomate: false.

That distinction is deliberate. Internal consistency is useful for quality control, but it is not a merchant authorization, source licence, live-offer check, legal conclusion, or business permission.

How to run

For a small review, submit bounded inline rows. For an existing authorized Apify Dataset, submit its datasetId. Keep the returned platform run ID, poll that same run to terminal state, and then read that run's Dataset plus the default KVS OUTPUT. Do not start another run merely to discover whether the first one completed.

The default fixture uses inline buyer-supplied evidence, requires no provider secret, and keeps optional BYOK labeling off. Start with that path unless an approved workflow specifically needs the Dataset source or optional labels.

Input contract

Choose exactly one source:

  1. rows: one to 100 inline evidence rows; or
  2. datasetId: one selected Apify Dataset with explicit READ permission.

The Actor never accepts an arbitrary Dataset URL, signed URL, source token, pagination selector, remote host, or generic payload wrapper. A Dataset ID must match the closed syntax. The Dataset path is fixed, one SDK request probes at most 11 records, and the product accepts no more than 10 Dataset items. Inline and Dataset rows pass the same row, string, UTF-8, URL, and normalization contract.

The public input contract remains 1.0. Output 2.0 is an intentional truth correction: existing v1 input producers remain valid, while generated paid results no longer call buyer assertions externally verified.

Offer Evidence Normalizer: evidence-to-action workflow

requestId is correlation metadata. It is not a cross-run idempotency key. Starting another run is a new commercial execution and can charge the start and result events again.

Complete input example

{
"schemaVersion": "1.0",
"requestId": "offer-normalize-20260804-001",
"rows": [
{
"entity": {
"entityId": "brand:demo-outfitters",
"brandName": "Demo Outfitters",
"legalName": null,
"domain": "demo.example",
"ticker": null,
"aliases": [],
"country": "US",
"sourceEntityIds": ["prefill:demo-outfitters"]
},
"evidence": {
"evidenceType": "landing_page",
"sourceName": "demo_fixture",
"sourceRecordId": "offer-001",
"sourceUrl": "https://demo.example/offers/spring",
"observedAt": "2026-08-04T08:00:00Z",
"rawTitle": "Spring offer",
"rawTermsText": "20% off orders $50+ with code SPRING20. Ends 2026-09-30.",
"terms": {
"offerKind": "percent_off",
"percentOff": 20,
"minPurchaseAmount": { "value": 50, "currency": "USD" },
"couponCode": "SPRING20",
"endAt": "2026-09-30T23:59:59Z"
},
"entityAttribution": {
"status": "verified_by_source",
"proofType": "first_party_domain",
"proofValue": "demo.example",
"sourceEntityId": "prefill:demo-outfitters",
"proofUrl": "https://demo.example/",
"proofDomain": "demo.example",
"merchantId": null,
"adAccountId": null,
"officialSourceId": null
},
"additionalUrls": []
}
},
{
"entity": {
"entityId": "brand:demo-outfitters",
"brandName": "Demo Outfitters",
"legalName": null,
"domain": "demo.example",
"ticker": null,
"aliases": [],
"country": "US",
"sourceEntityIds": ["prefill:demo-outfitters"]
},
"evidence": {
"evidenceType": "landing_page",
"sourceName": "demo_fixture",
"sourceRecordId": "offer-001",
"sourceUrl": "https://demo.example/offers/spring",
"observedAt": "2026-08-04T08:00:00Z",
"rawTitle": "Spring offer",
"rawTermsText": "20% off orders $50+ with code SPRING20. Ends 2026-09-30.",
"terms": {
"offerKind": "percent_off",
"percentOff": 20,
"minPurchaseAmount": { "value": 50, "currency": "USD" },
"couponCode": "SPRING20",
"endAt": "2026-09-30T23:59:59Z"
},
"entityAttribution": {
"status": "verified_by_source",
"proofType": "first_party_domain",
"proofValue": "demo.example",
"sourceEntityId": "prefill:demo-outfitters",
"proofUrl": "https://demo.example/",
"proofDomain": "demo.example",
"merchantId": null,
"adAccountId": null,
"officialSourceId": null
},
"additionalUrls": []
}
}
],
"options": { "labels": "off", "analysisModel": null, "maxItems": 100 }
}

Normalization rules

Structured terms are primary. The bounded parser fills only fields that are absent and only when the source text contains an explicit supported pattern. Supported concepts include percentage and amount discounts, buy-X-get-Y quantities, free shipping, coupon codes, minimum purchase amounts, membership and new-customer restrictions, geography, channels, dates, and exclusions.

The parser does not guess. Phrases such as “special pricing,” “sale,” “up to,” or “from” without an unambiguous supported value remain partial or unparsed. When structured fields and text disagree, the Actor retains a conflict instead of choosing one value. Conflict, partial, unparsed, missing-proof, and mismatched-proof rows are not eligible paid normalization rows.

The canonical evidence key is:

entityId | sourceName | sourceRecordId | canonicalSourceUrl | offerKind

Equivalent keys are grouped after deterministic sorting. Exact duplicates collapse into one normalized work unit. Duplicate rows that disagree on terms or submitted attribution become conflicts. The Actor does not use “last write wins,” does not select a preferred source, and does not silently bill multiple copies of the same key.

The derived normalizationDigest binds the canonical entity ID, source identity, canonical source URL, offer kind, and normalized terms. A derived offerId equals that digest; a supplied offer ID remains explicitly marked as supplied. These digests are integrity bindings, not signatures, source authorization, anonymization, or cross-run purchase protection.

Happy, partial, and failure output

A happy run writes one settlement-neutral Dataset candidate per eligible normalization and then proves its current-run settlement in KVS OUTPUT. A bounded partial run preserves already confirmed work plus the exact stop reason. A pre-delivery failure writes no candidate; a post-push unknown or anomalous state is never presented as a clean retry.

Complete Dataset delivery candidate

The fixture below is the executable output-v2 shape used by schema and semantic tests.

{
"schemaVersion": "2.0",
"inputSchemaVersion": "1.0",
"requestId": "offer-normalize-20260804-001",
"requestDigest": "sha256:b922715033f82998b5066e9369f29c31021f004f25561ad5dc6fd1bba57ca92c",
"inputDigest": "sha256:41e292e4323d4b0a85d5134d7e3485912daa24b330bcdd9fc15a056ded8db126",
"actorRunId": "example-run-001",
"attemptIndex": 1,
"attemptDigest": "sha256:2ee444a419726dacd335cb609c4fe7ff5d0639882d6916857e94b6ab5aa1d5bf",
"resultCandidate": true,
"entity": {
"entityId": "brand:demo-outfitters",
"brandName": "Demo Outfitters",
"legalName": null,
"domain": "demo.example",
"ticker": null,
"aliases": [],
"sourceEntityIds": ["prefill:demo-outfitters"],
"country": "US"
},
"eventType": "offer_evidence",
"eventId": "offer-001",
"observedAt": "2026-08-04T08:00:00Z",
"effectiveAt": null,
"sourceName": "demo_fixture",
"sourceUrl": "https://demo.example/offers/spring",
"sourceRecordId": "offer-001",
"confidence": null,
"offer": {
"offerId": "sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"offerIdSource": "derived",
"normalizationDigest": "sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"offerKind": "percent_off",
"normalizationStatus": "complete",
"terms": {
"percentOff": 20,
"amountOff": null,
"buyQuantity": null,
"getQuantity": null,
"minPurchaseAmount": { "value": 50, "currency": "USD" },
"couponCode": "SPRING20",
"couponCodeCanonical": "SPRING20",
"memberOnly": null,
"newCustomerOnly": null,
"startAt": null,
"endAt": "2026-09-30T23:59:59Z",
"geography": null,
"channels": null,
"exclusions": null
},
"termConflicts": [],
"dedupeKey": "brand:demo-outfitters|demo_fixture|offer-001|https://demo.example/offers/spring|percent_off"
},
"entityAttribution": {
"assessmentStatus": "submitted_consistent",
"submittedStatus": "verified_by_source",
"verificationScope": "submitted_fields_only",
"externalVerificationPerformed": false,
"safeToAutomateBusinessDecision": false,
"proofType": "first_party_domain",
"proofValue": "demo.example",
"sourceEntityId": "prefill:demo-outfitters",
"proofUrl": "https://demo.example/",
"proofDomain": "demo.example",
"merchantId": null,
"adAccountId": null,
"officialSourceId": null
},
"evidence": {
"evidenceType": "landing_page",
"sourceName": "demo_fixture",
"sourceRecordId": "offer-001",
"sourceUrl": "https://demo.example/offers/spring",
"observedAt": "2026-08-04T08:00:00Z",
"rawTitle": "Spring offer",
"rawTermsText": "20% off orders $50+ with code SPRING20. Ends 2026-09-30.",
"sourceTerms": {
"offerKind": "percent_off",
"percentOff": 20,
"minPurchaseAmount": { "value": 50, "currency": "USD" },
"couponCode": "SPRING20",
"endAt": "2026-09-30T23:59:59Z"
},
"additionalUrls": [],
"evidenceTruncated": false
},
"normalizationNeedsReview": false,
"attributionNeedsReview": true,
"needsReview": true,
"reviewReasons": ["external_attribution_not_verified"],
"analysisStatus": "skipped_no_key",
"analysisModel": null,
"usage": null,
"usageScope": "run",
"labels": null,
"decision": {
"entityId": "offer:sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"eventId": "offer-evidence:sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"observedAt": "2026-08-04T08:00:00Z",
"freshness": { "status": "unknown", "observedAt": "2026-08-04T08:00:00Z", "basis": "buyer_submitted_observed_at" },
"change": { "status": "not_measured", "basis": "single_run_normalization" },
"confidence": {
"level": "medium",
"score": 65,
"basis": ["submitted_offer_fields_normalized", "submitted_attribution_fields_internally_consistent"],
"gaps": ["external_offer_not_fetched", "external_attribution_not_verified", "source_rights_not_verified", "business_effect_not_verified"]
},
"evidence": {
"requestDigest": "sha256:b922715033f82998b5066e9369f29c31021f004f25561ad5dc6fd1bba57ca92c",
"inputDigest": "sha256:41e292e4323d4b0a85d5134d7e3485912daa24b330bcdd9fc15a056ded8db126",
"normalizationDigest": "sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"attemptDigest": "sha256:2ee444a419726dacd335cb609c4fe7ff5d0639882d6916857e94b6ab5aa1d5bf"
},
"recommendedAction": "REVIEW_SUBMITTED_OFFER_AND_ATTRIBUTION_BEFORE_USE",
"priority": "normal",
"safeToAutomate": false,
"summary": "Offer fields normalized from buyer-submitted evidence; external attribution and offer truth were not verified.",
"failureDiagnostics": null,
"billing": { "billingEligible": true, "billingIntent": "linked_push_result_found", "eventName": "result-found", "unit": "normalized_offer_delivery_candidate", "settlementSource": "current_run_kvs_output" }
},
"deliveryIntent": { "event": "result-found", "businessRows": 1 }
}

Decision routing

Each delivered row includes an additive decision object designed for evidence review:

  • stable entityId and eventId derived from the normalized offer ID;
  • buyer-submitted observedAt;
  • freshness status unknown with basis buyer_submitted_observed_at;
  • change status not_measured because one stateless run has no prior observation;
  • medium confidence score 65 based on completed normalization and internally consistent submitted proof fields;
  • explicit gaps for external offer checks, attribution verification, source rights, and business effect;
  • request, input, normalization, and attempt digests;
  • recommended action REVIEW_SUBMITTED_OFFER_AND_ATTRIBUTION_BEFORE_USE;
  • priority normal;
  • safeToAutomate:false;
  • null failure diagnostics on a valid delivered row;
  • one declared result billing unit.

The confidence score describes only the evidence available to this Actor. It is not accuracy, merchant authenticity, redemption probability, offer availability, legal validity, conversion likelihood, or expected revenue. A downstream system must not raise that score because a brand name looks familiar or because a URL is syntactically valid.

KVS OUTPUT

Read the default Key-Value Store record named OUTPUT after every run. Dataset rows answer “what normalizations were delivered?” OUTPUT answers “what happened in this exact run?”

OUTPUT includes:

  • accepted input and output schema versions;
  • Actor name, current run ID, request ID, and request digest;
  • terminal status and whether any named paid result was confirmed;
  • input, deduped, submitted-consistent, eligible, and rejected work-unit counts;
  • attempted pushes, confirmed Dataset writes, exact named result events, paid rows, free rows, anomalous rows, unknown operations, last receipt, and last attempt identity;
  • requested, unique, duplicate, successful, failed, delivered, paid, free, anomalous, withheld, and unknown run counts;
  • partial, budget, fatal, replay, and automation flags;
  • input source and Dataset request statistics;
  • output limit and truncation truth;
  • optional provider analysis status and run-level token usage;
  • bounded errors;
  • primary and recovery KVS states plus requested/failed exit state.

Complete successful OUTPUT example

{
"schemaVersion": "2.0",
"inputSchemaVersion": "1.0",
"actor": "us-brand-offer-evidence-normalizer",
"runId": "example-run-001",
"requestId": "offer-normalize-20260804-001",
"requestDigest": "sha256:b922715033f82998b5066e9369f29c31021f004f25561ad5dc6fd1bba57ca92c",
"status": "found",
"resultFound": true,
"counts": { "input": 2, "deduped": 1, "submittedConsistent": 1, "eligible": 1, "rejected": 0 },
"delivery": {
"attemptedPushCount": 1,
"confirmedDatasetWrites": 1,
"confirmedResultEvents": 1,
"confirmedCharges": 1,
"freeDatasetWrites": 0,
"anomalousDatasetWrites": 0,
"anomalousSettlementCount": 0,
"anomalousChargeCount": null,
"unknownDatasetWrites": 0,
"unknownSettlements": 0,
"lastReceiptId": "fixture-receipt-1",
"lastAttempt": {
"attemptIndex": 1,
"attemptDigest": "sha256:2ee444a419726dacd335cb609c4fe7ff5d0639882d6916857e94b6ab5aa1d5bf",
"offerId": "sha256:04ce7e6a1a35568be96af2eb241cd83be44264bfbda0e05d3c5c6aa3ce18751b",
"state": "confirmed_paid"
}
},
"run": {
"requestedCount": 2,
"uniqueCount": 1,
"duplicateCount": 1,
"successfulCount": 1,
"failedCount": 0,
"deliveredRowCount": 1,
"paidRowCount": 1,
"freeRowCount": 0,
"anomalousDeliveryCount": 0,
"withheldRowCount": 0,
"unknownDeliveryCount": 0,
"unknownSettlementCount": 0,
"partial": false,
"budgetStopped": false,
"fatalError": false,
"replaySafe": false,
"safeToAutomate": false
},
"source": { "kind": "inline", "datasetId": null },
"limits": { "maxItems": 100, "truncated": false },
"analysisStatus": "skipped_no_key",
"analysisModel": null,
"usage": null,
"usageScope": "run",
"sourceStats": { "datasetRequests": 0, "decodedBytes": 0, "elapsedMs": 12 },
"errors": [],
"replaySafe": false,
"terminal": {
"outcome": "SUCCEEDED",
"failureStage": null,
"retryable": false,
"primaryKvsWrite": "confirmed",
"recoveryKvsWrite": "not_attempted",
"exit": "requested"
}
}

Status matrix

StatusMeaningRetry boundary
foundEvery eligible row within the result limit was delivered and its named result event was confirmed.A new run is a new purchase; replaySafe:false.
no_matchNormalization produced no eligible delivered row.No Dataset push was attempted; inspect counts and evidence problems.
result_limit_reachedExactly maxItems eligible rows were delivered and more eligible rows existed.This is not a budget error. Increase the declared limit only if another purchase is intended.
budget_stoppedThe next result did not fit the buyer cap or a linked push was confirmed free by an exact zero named-event delta.Inspect paid/free/withheld counts; do not call it a result-limit stop.
partialSome paid rows were confirmed before a deterministic pricing or row failure stopped the run.Preserve the receipt and correct the named failure before deciding on another run.
unknown_deliveryThe linked push threw, so Dataset delivery itself is unknown.Never retry blindly. Reconcile the original Dataset, PPE counters, KVS, and logs.
settlement_unknownThe linked push returned, proving one Dataset write, but the post-push named counter was unreadable.Treat delivery as known and payment as unresolved; never retry blindly.
settlement_anomalyThe named counter and aggregate receipt contradicted the expected single-unit settlement.Preserve the original run. Named-counter truth is retained; manual reconciliation is required.
Input/source/pricing failureFailure was established before any Dataset push.The receipt is replay-safe with respect to result delivery, but another run still has a new start event.

Pricing

The live Apify pricing panel is the current source of truth. The configured contract accepts exactly these six paired tiers:

TierActor startOne delivered normalizationStart + one result*
FREE$0.00500$0.01000$0.01500
BRONZE$0.00475$0.00950$0.01425
SILVER$0.00450$0.00900$0.01350
GOLD$0.00425$0.00850$0.01275
PLATINUM$0.00410$0.00820$0.01230
DIAMOND$0.00400$0.00800$0.01200

*Configured event prices only. Platform credits, taxes, storage, account agreements, optional OpenRouter usage, and other platform consumption are not included.

Mixed tiers, missing events, additional events, a priced default Dataset-item event, malformed values, or a platform-spent amount that does not include the resolved start event fail closed before delivery. A valid positive infinite platform cap is treated as unlimited. Finite caps use integer micro-USD arithmetic.

Before every linked push, the Actor rereads pricing and budget state. It then reads getChargedEventCount('result-found') and requires it to equal the number of already confirmed paid rows. One pushData(row, 'result-found') follows. The named counter is read again:

  • exact delta +1: one paid result confirmed;
  • exact delta 0: one free Dataset write confirmed;
  • any other readable delta: settlement anomaly;
  • thrown linked push: Dataset delivery is unknown, so the run stops without retry and records unknown_delivery.
  • returned linked push plus an unreadable post-push named counter: Dataset delivery is known, settlement is unknown, and the run records settlement_unknown.

The push is never retried inside the run. Aggregate SDK receipts are retained as a second consistency signal, but a vague positive aggregate cannot replace the exact named-event delta.

Bring your own key (BYOK)

BYOK labels are optional. Set options.labels to byok, provide an explicit OpenRouter model, and supply openrouterApiKey. The deterministic normalization product remains independent of those labels.

The Actor sends at most one bounded request to the fixed OpenRouter chat-completions endpoint. The request contains only normalized offer IDs, offer kinds, and normalized terms for the rows planned for delivery. It does not send arbitrary instructions, tools, source-page content, images, or a browsing request. A response may add only one closed offer category and audience label for every selected offer ID.

Provider errors, timeouts, malformed output, missing labels, or request-cap skips cannot change offer facts, attribution assessment, dedupe, eligibility, delivery, or billing. Dataset rows contain usage:null; aggregate token usage appears once in run-level OUTPUT.

The key is marked secret in the Input UI and excluded from request digests, Dataset rows, KVS OUTPUT, and application logs. Apify still stores Actor input in the run's INPUT record according to workspace retention. Restrict run access and delete stored runs when the approved purpose ends. OpenRouter receives the bounded projection under the buyer's provider agreement, subprocessors, logging, and retention settings; this Actor does not promise zero retention.

Integration recipes

Start one run, poll that exact run, then read its exact Dataset and KVS. Do not create another run as a polling mechanism.

curl -X POST \
"https://api.apify.com/v2/acts/zinin~us-brand-offer-evidence-normalizer/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @actors/us-brand-offer-evidence-normalizer/fixtures/inline-prefill.json

In an integration, require all of the following before accepting a paid result:

  1. platform run ID equals OUTPUT.runId;
  2. output schema is 2.0 and input schema is 1.0;
  3. Dataset row count equals run.deliveredRowCount;
  4. named result-found count equals run.paidRowCount;
  5. each Dataset delivery candidate has the same actorRunId;
  6. every attempt index and attempt digest is unique within the run;
  7. no unexpected paid Dataset event exists;
  8. unknownDeliveryCount, unknownSettlementCount, and anomaly counts are zero for ordinary success;
  9. safeToAutomate remains false;
  10. a reviewer follows the declared recommended action and evidence gaps.

Accepted historical run evidence

These two redacted summaries bind the Actor's previously accepted inline and authorized-Dataset production proofs. They demonstrate real platform delivery and PPE settlement on the listed legacy build; they are not examples of the replacement output 2.0 candidate that still requires its own no-retry canary.

{
"runId": "Rae3RnIRp2bkhFm0f",
"buildId": "YblzxHduCD2mtSX8K",
"status": "SUCCEEDED",
"evidenceAccepted": true,
"acceptanceScope": "historical input/output 1.0 production proof",
"inputSource": "inline",
"chargedEventCounts": {
"apify-actor-start": 1,
"result-found": 1
}
}
{
"runId": "g3w1LQmXSFsJM0FrV",
"buildId": "YblzxHduCD2mtSX8K",
"status": "SUCCEEDED",
"evidenceAccepted": true,
"acceptanceScope": "historical input/output 1.0 production proof",
"inputSource": "authorized_dataset",
"chargedEventCounts": {
"apify-actor-start": 1,
"result-found": 1
}
}

Commercial playbooks

Merchandising review: normalize authorized offer evidence, reject conflicts, then route every delivered candidate to a human who checks the authoritative offer source.

Affiliate operations: use the dedupe key and digests to detect repeated submitted evidence before starting another paid run; never translate internal consistency into merchant approval.

CRM or warehouse ingestion: store Dataset candidates with their run ID, then join only to the same run's KVS settlement receipt. Keep unknown and anomalous outcomes out of automated activation.

Agent-assisted review: allow an agent to summarize normalized terms and evidence gaps, but require it to retain safeToAutomate:false, cite the buyer-supplied source identity, and stop on an unresolved delivery state.

An agent can use this Actor as a bounded evidence-normalization tool, not as an offer-verification authority. Configure the agent to preserve the exact run ID and digests, quote the evidence gaps, and route the result to review. Never allow an agent to translate submitted_consistent into “verified,” “official,” “live,” “valid,” or “authorized.”

For scheduled runs, remember that the Actor is stateless across runs. Canonical digests help compare normalized content, but they do not prevent buying the same result twice. Maintain business idempotency before starting a run if duplicate purchases are unacceptable.

If a run times out in the caller, poll the same run ID. If the run reports unknown Dataset delivery, unknown settlement after a known Dataset write, or a settlement anomaly, stop. Inspect the original Dataset, KVS, charged-event counts, logs, and attempt identity. A second run is not a safe reconciliation strategy.

Sources and rights

This Actor fetches no submitted evidence URL and has no third-party source licence of its own. That does not grant the buyer rights to process or redistribute submitted data. The buyer must have an approved purpose and the rights to submit every brand field, offer term, source record, URL, timestamp, and proof field.

Do not include credentials, signed URLs, customer lists, personal contacts, device identifiers, advertising identifiers, confidential notes, or unnecessary free text. A hash or opaque ID can still be personal or confidential when another system can relink it. Hashing is not anonymization.

Use workspace access controls and a defined retention schedule for Input, Dataset, KVS, and logs. Preserve upstream correction and deletion obligations. This Actor does not contact a source, notify a data subject, verify consent, determine controller/processor roles, or delete data in another system.

The output is not legal advice, a trademark conclusion, a platform-policy decision, a price guarantee, or authorization for targeting, outreach, purchasing, ranking, eligibility, or enforcement.

Failure model

Input rejected

Read OUTPUT.errors and terminal.failureStage. Common causes are source XOR violations, unknown fields, oversized input, invalid Dataset IDs, unsupported schema version, malformed dates/URLs, term conflicts, proof mismatches, and invalid BYOK relations. Closed contracts reject unknown fields instead of silently dropping them.

Selected Dataset unavailable

The Actor distinguishes not found, forbidden, malformed, redirected, oversized, truncated, timed-out, and transport failures. These states produce no paid normalization row. The run-level receipt binds the attempted Dataset ID and request statistics without echoing a credential.

No eligible row

no_match means the submitted work produced no normalization-complete, submitted-consistent row. It does not prove that no offer exists. Inspect normalization and attribution reasons in the source system; rejected rows are not republished as Dataset delivery candidates.

Result limit reached

result_limit_reached means the declared maxItems was reached. It is deliberately separate from budget_stopped. The receipt shows exactly how many eligible rows were paid and how many were withheld.

Primary OUTPUT write fails

The runtime makes one bounded recovery write. A persisted recovery receipt says primary KVS write failed, recovery write confirmed, exit failed, and includes output_write_failed. If both writes fail, the platform run fails without pretending a receipt exists.

Operating guide

Before using a row, verify outside this Actor:

  • the source was authorized for this purpose;
  • the source record still exists and is current;
  • the merchant, brand, and proof identifiers belong together;
  • the offer terms match the current authoritative source;
  • dates, geography, channels, exclusions, and audience restrictions are complete;
  • coupon or promotion availability is confirmed where necessary;
  • source rights permit downstream storage and use;
  • personal and confidential data were minimized;
  • the reviewer understands that observedAt is buyer submitted;
  • the downstream action is proportionate and human approved.

FAQ

Does submitted_consistent mean verified?

No. It means only that the submitted proof fields satisfy the closed internal relations. The Actor explicitly records that no external verification was performed.

Does the Actor visit the evidence URL?

No. It canonicalizes and records HTTPS URLs but does not fetch, resolve, render, authenticate, malware-scan, or verify them.

Are incomplete or conflicting rows charged?

No result-found event is requested for rows that are normalization-incomplete, conflicted, missing proof, mismatched, duplicate-only, withheld before delivery, or rejected by contract.

Can a complete normalization still require review?

Yes. Every delivered row requires attribution and source review. Normalization completeness and external truth are different questions.

Can BYOK labels alter eligibility or billing?

No. Labels are an optional bounded presentation layer. Provider failures do not change deterministic facts, eligibility, delivery, or billing decisions.

Is rerunning the same request ID idempotent?

No. Each run is a new commercial execution. Use your own pre-run idempotency control and compare digests before starting another run.

Why is provider usage absent from each Dataset row?

One provider call can label several rows. Repeating the same aggregate token count on every row would overstate usage. It appears once in KVS OUTPUT.

Can I automate campaign or pricing changes from the output?

No. safeToAutomate is false. Review the authoritative source, rights, terms, attribution, and business context first.

Public ActorUse it for
Shopify Store IntelligenceAuthorized storefront evidence collection before normalization.
Shopify Price Change MonitorSource-specific price-change evidence with its own contract.
Structured ExtractBounded structured extraction from buyer-authorized pages.
Intent Signal AggregatorAggregate already authorized evidence into a separate signal product.
Social Preview CheckerInspect authorized page preview metadata before downstream review.

Use each Actor only inside its own source-rights, privacy, billing, and completeness boundary. A crosslink is not evidence that two outputs are automatically compatible.