How to Handle Late-Arriving Marketing Data
Data Engineer and Co-founder, martechdevs
Last updated 9 min read
Handle late-arriving marketing data by preserving when something happened, when the source changed and when your system received it. Reprocess affected records and reporting periods using stable identities, explicit revision rules and reconciliation checks. A recent-date lookback helps with routine delays, but corrections to older records need a separate recovery path and visible restatement history.
Last updated: 12 October 2026.
Find out which kind of late data you have
A purchase occurring yesterday can arrive today because a device was offline. A purchase received yesterday can be corrected today because its amount was wrong. A conversion can arrive promptly but receive a different campaign attribution later. These changes require different treatment even when they all appear as yesterday's dashboard changing.
Separate at least three cases. A late new occurrence adds a previously missing business event. A revised record changes an existing fact. A late relationship adds context, such as the account associated with a lead or the campaign associated with a conversion. Deletion is another explicit change, not simply the absence of a row in a partial extract.
Start with the decision that depends on the data. A provisional daily campaign report can tolerate declared revisions. A customer message triggered by a late historical event may be harmful if the customer already completed the journey. Your marketing data warehouse needs both analytical correction rules and operational activation rules.
Do not attempt to make every source immutable. Instead, make the history of what changed explainable and make repeated processing safe.
Preserve the clocks instead of overwriting them
Use separate fields for business occurrence time, source update time when reliable, ingestion time and model publication time. Record the business timezone used to assign dates. An occurrence at 23:50 UTC belongs to the next calendar day in Asia/Dhaka; its warehouse load date should not decide which campaign day receives the result.
A source update timestamp is only useful if the source updates it for every change you care about. Some report APIs regenerate aggregate rows without exposing a reliable per-row revision timestamp. In that case, retain extraction time and replace the appropriate report slice under a documented snapshot convention.
An ingestion timestamp should represent durable arrival into your own raw layer. A transformation timestamp indicates when a model ran, not when the source data became complete. Updating every row's timestamp during a rebuild can make old data appear fresh unless those meanings remain separate.
Preserve provenance: source account, extract identifier, endpoint or report configuration, cursor range, schema version and payload hash where appropriate. These fields allow you to answer whether two different values came from a vendor revision, a changed request or your own transformation.
Do not use the latest timestamp as an automatic winner without understanding it. Stripe's webhook documentation says event delivery order is not guaranteed and distinct events can share a creation timestamp. Event identity and current-object reconciliation are different concerns from sorting delivery time.
Use a raw history and a resolved current view
Keep source deliveries in a restricted raw history so you can replay them. In the resolved layer, represent one current record at the intended business grain. For an order line, that may be source account plus order ID plus line ID; for an aggregate advertising report, it may include date, account, campaign and every requested breakdown.
Define revision precedence from an authoritative source sequence or version when available. If there is no such sequence, design a source-specific reconciliation method rather than inventing one from arrival order. A late webhook containing an older snapshot should not overwrite a newer confirmed object state simply because it arrived later.
Duplicate delivery detection is also source-specific. Logging a delivery ID can suppress the same event being processed again. It does not prove that two different event IDs cannot describe the same business change. Conversely, deduplicating all records with the same customer and timestamp can erase legitimate activity.
A merge statement does not choose the correct business grain for you. BigQuery's MERGE documentation describes how multiple source matches can cause an update failure. Resolve ambiguous revisions before the merge and test uniqueness on both sides rather than hoping the database will select the right row.
Work through a small correction ledger
The following example uses hypothetical USD amounts, UTC ingestion timestamps and explicit business dates already assigned by the source contract. It deliberately uses a source revision number; do not assume your vendor supplies an equivalent field.
Order A is $100 for 1 October. Order B is initially $200 for 1 October. Both are available by 02:00 UTC on 2 October, so the first report shows $300 for 1 October.
Order C is a late $50 occurrence for 1 October, received on 3 October. Its delivery is repeated on 4 October. On 5 October, a higher revision of B changes its amount to $180. Another late order D for 30 September adds $40 to that earlier date.
The corrected 1 October total is $100 + $180 + $50 = $330. The change from the first report is plus $30: plus $50 late activity and minus $20 correction. September gains $40 separately. Adding every delivered amount would count B twice and C twice, producing a false total.
This original Python fixture resolves versions visible at a reporting cutoff. It is an acceptance model for these stated semantics, not a generic production connector:
from collections import defaultdict
# id, business_date, revision, amount_cents, ingested_at_utc
rows = [
("A", "2026-10-01", 1, 10000, "2026-10-02T01:00:00Z"),
("B", "2026-10-01", 1, 20000, "2026-10-02T01:10:00Z"),
("C", "2026-10-01", 1, 5000, "2026-10-03T09:00:00Z"),
("C", "2026-10-01", 1, 5000, "2026-10-04T09:00:00Z"),
("B", "2026-10-01", 2, 18000, "2026-10-05T09:00:00Z"),
("D", "2026-09-30", 1, 4000, "2026-10-05T09:10:00Z"),
]
def totals_as_of(deliveries, cutoff):
versions = {}
latest = {}
for key, day, revision, cents, received in deliveries:
if received > cutoff:
continue
version_key = (key, revision)
payload = (day, cents)
if version_key in versions and versions[version_key] != payload:
raise ValueError("Conflicting payloads for one source revision")
versions[version_key] = payload
if key not in latest or revision > latest[key][0]:
latest[key] = (revision, day, cents)
totals = defaultdict(int)
for revision, day, cents in latest.values():
totals[day] += cents
return dict(totals)
assert totals_as_of(rows, "2026-10-02T02:00:00Z") == {
"2026-10-01": 30000
}
assert totals_as_of(rows, "2026-10-06T00:00:00Z") == {
"2026-10-01": 33000, "2026-09-30": 4000
}All timestamps use the same fixed UTC format so lexical comparison is valid in this fixture. Production code should parse and validate timestamps, revisions, currencies and keys. Source account belongs in the key when IDs are not globally unique. The example omits deletion handling, which needs its own explicit tombstone or complete-snapshot rule.
The cutoff answers what your system knew at that time. A current corrected report answers a different question. Keeping both makes a historical budget decision understandable without hiding later evidence.
Make incremental processing aware of change
Filtering only on business_date > last_processed_date misses C, B's correction and D. Their business dates are old even though the new information is recent.
A reliable arrival or change cursor can identify newly received raw records. Resolve their current versions, collect the affected business keys and dates, then rebuild the dependent aggregates for those dates. If a revision moves a record between dates or campaigns, include both its previous and new locations so that the old aggregate loses the value as the new one gains it.
A rolling lookback is another useful technique when a source republishes recent report slices. Read the full chosen slice, validate it and replace that slice consistently. Overlapping windows should produce the same final result when rerun; appending the entire overlap repeatedly will not.
dbt's incremental-model documentation explains that row-update behavior depends on the model's unique key and incremental strategy. A configured key is not a uniqueness test, and it cannot recover records excluded by your input filter. Test the grain and the filtering rule independently.
Choose the lookback from the source's behavior and your observed revision distribution. Google's GA4 export schema documents updates to recent daily tables for late events. That supports re-reading recent partitions; it does not establish a universal three-day correction window for your CRM, billing or advertising reports.
For session-level implications, see GA4 BigQuery sessionization. This guide's broader requirement is that every downstream model affected by a changed fact gets a defined repair path.
Give older corrections a separate route
A seven-day routine lookback will not fix a correction to a two-month-old invoice. Add a periodic reconciliation process, explicit change feed or targeted backfill mechanism that can identify and repair older differences.
Measure the age distribution of changes, not only new deliveries. A source can deliver new rows quickly while revising old values for weeks. Track the value affected as well as the count: one large correction may matter more than thousands of tiny late events.
For a targeted backfill, record the source scope, original and new totals, affected dates, model version and downstream consumers. Estimate the query cost and isolate the rebuild before publishing it. Reconcile row counts, business-key counts and monetary totals; a row-count match alone can conceal changed values.
A broad full refresh can be appropriate after a logic change, but it is not a harmless universal repair button. dbt documents that full refresh rebuilds incremental models. Confirm source history remains available, downstream dependencies are included and operational side effects are controlled before using it.
The dbt for marketing guide explains the modeling workflow. The important operating habit is to make backfills reviewable and reproducible instead of silently rerunning an unknown amount of history.
Protect activation from historical replay
Analytical corrections should not automatically resend lifecycle messages or create duplicate sales tasks. Separate fact restoration from action eligibility.
Suppose an activation event from 20 September arrives on 5 October. The warehouse should reflect the true business date. Before enrolling the account in an onboarding message, the activation system must check current state, prior deliveries, the allowed lateness window and any consent or suppression rules.
Store action-level idempotency keys separately from event deduplication. Correcting an event's campaign label does not create a new customer action. A source replay should not remove unsubscribe state or restore an obsolete lead owner.
Where a correction should change an audience, calculate the desired current audience and compare it with the destination state. Additions and removals need validation, especially after a large backfill. The warehouse audiences guide provides the broader activation design.
Explain restatements to the person using the report
Publish a data-through timestamp, a report generation timestamp and a status such as provisional or reconciled under your documented policy. These are separate from the website article's last-updated date and separate from a database job's success timestamp.
For the example ledger, report that 1 October changed from $300 to $330, with $50 of newly arrived orders and a $20 downward correction. That explanation is more useful than telling a marketing manager that the pipeline reran successfully.
Set materiality rules with the report owner. A small routine revision may only need a revision log; a change that reverses a budget decision may need direct review. Preserve the earlier published snapshot when the organization needs to reproduce what a decision maker saw.
Connect these rules to the marketing data freshness SLA. Timely delivery and stable historical totals are related, but neither proves the other.
Test the recovery behavior explicitly
- A late new occurrence updates its original business period.
- A repeated delivery leaves resolved counts and totals unchanged.
- An older revision arriving later cannot overwrite a newer authoritative revision.
- Conflicting payloads for the same revision produce an exception rather than an arbitrary winner.
- A date or campaign correction removes value from the previous aggregate and adds it to the new one.
- A correction beyond the routine lookback has a tested recovery route.
- Rebuilding the same inputs twice produces the same analytical result without repeated customer actions.
- The published revision reconciles to source evidence and preserves the earlier as-of view when required.
Start with one important metric and a fixture containing late, duplicate and revised records. If the team cannot predict its final totals, adding more frequent refreshes will only produce incorrect answers faster.
Questions people ask
What is late-arriving marketing data?
It is information received after the reporting or processing window in which it would normally be expected. Distinguish a new late occurrence from a correction or relationship change to an existing record, because the update logic differs.
Is a three-day lookback enough?
Only if it covers the relevant source behavior and your acceptance policy. A documented recent-event window for one analytics export does not cover older CRM corrections, billing adjustments or all advertising restatements.
Should reports use event time or ingestion time?
Business reporting usually groups by the defined business occurrence or effective date, while ingestion time tracks availability and supports processing. Preserve both so a delayed arrival does not move revenue or activity into the wrong period.
Does a MERGE statement remove duplicates automatically?
It applies the matching and update rules you supply. Incorrect keys, multiple source revisions or an input filter that misses old corrections still require separate resolution and testing.
Should late events trigger customer journeys?
Only after checking current eligibility, event age, prior actions and suppression rules. Repairing historical analytics should not automatically resend a message or repeat an operational action.
Sources
- Stripe: Webhook delivery order and duplicate handling. Accessed 12 October 2026.
- Google Cloud: BigQuery MERGE statements. Accessed 12 October 2026.
- dbt: Configure incremental models. Accessed 12 October 2026.
- Google Analytics: BigQuery Export schema. Accessed 12 October 2026.
Written by
Jafrin Akter AloData Engineer and Co-founder, martechdevs
Jafrin Akter Alo is a data engineer and co-founder of martechdevs, where the team has delivered 74+ martech, data and growth stack implementations.