Prompt
How do I set up an impression log API for revenue reconciliation in our adtech data warehouse?
Latest observation
To set up an impression log API for revenue reconciliation in an adtech data warehouse, design it as a reliable event-ingestion layer that captures every billable impression with enough identifiers to match internal delivery data against partner invoices or SSP/DSP reports.
1) Define the reconciliation goal
Decide what you need to reconcile:
- Delivery vs. billed impressions
- Gross revenue vs. net revenue
- Publisher/placement/account-level attribution
- Discrepancies by day, geo, device, creative, exchange, etc.
This determines what fields your API must collect.
2) Define the impression event schema
Each impression record should be immutable and include reconciliation-grade identifiers.
Minimum recommended fields
impression_id— unique event IDrequest_id/auction_id— request or bid request identifierad_serving_id— internal ad server event IDtimestamp_utcpartner_name/exchange_nameadvertiser_idcampaign_idline_item_idplacement_id/inventory_idpublisher_idsite_app_idcreative_idad_formatdevice_typecountryrevenue_grossrevenue_netcurrencybilling_model— CPM, CPC, etc.viewability_flagif relevantfraud_flag/invalid_traffic_flagmatch_keyfields used by finance/vendor reports
Helpful extras
user_id_hashorcookie_idif allowedip_hashua_hashplacement_namedeal_id/seat_idorder_idsupply_pathdiscrepancy_reason
3) Create the API contract
Use a REST or streaming ingestion API. For high-volume adtech, a batch-friendly append-only ingestion endpoint is common.
Example endpoint
POST /v1/impressions
Example payload
{
"impression_id": "imp_12345",
"request_id": "req_abc987",
"timestamp_utc": "2026-07-26T12:34:56Z",
"partner_name": "exchange_x",
"advertiser_id": "adv_1",
"campaign_id": "cmp_22",
"line_item_id": "li_88",
"placement_id": "pl_14",
"publisher_id": "pub_9",
"site_app_id": "site_77",
"creative_id": "cr_33",
"ad_format": "display",
"device_type": "mobile",
"country": "US",
"revenue_gross": 0.0123,
"revenue_net": 0.0098,
"currency": "USD",
"billing_model": "CPM",
"fraud_flag": false,
"invalid_traffic_flag": false
}
4) Decide ingestion pattern
Best options
- Streaming API for near-real-time reconciliation
- Batch API for hourly/daily file-style ingestion
- Hybrid: stream raw logs into storage, then transform into warehouse tables
Recommended architecture
- API receives impression events
- Validate schema and auth
- Write raw events to immutable storage
- Load into warehouse staging tables
- Transform into reconciliation tables
- Compare against partner invoices/reporting data
5) Store raw and curated data separately
Raw layer
Keep the original payloads unchanged for auditability.
Staging layer
Normalize timestamps, currencies, IDs, and deduplicate.
Curated reconciliation layer
Use dimension-fact models:
fact_impressionsfact_revenuedim_campaigndim_publisherdim_partnerdim_placement
This makes discrepancy analysis much easier.
6) Build deduplication and idempotency
Impression data often arrives multiple times.
Use:
impression_idas primary dedupe keyrequest_id+timestamp+creative_idas fallback- idempotency keys on API writes
Track:
- duplicate count
- late-arriving events
- out-of-order events
7) Add reconciliation logic
Create comparison jobs that aggregate by:
- date
- partner
- campaign
- placement
- currency
- region
Compare:
- internal counted impressions
- partner-reported impressions
- billable impressions
- revenue amounts
Store discrepancy metrics:
- absolute delta
- percentage delta
- status flags
- root-cause category
Example:
matchedlate_eventpartner_missinginternal_missinginvalid_trafficcurrency_mismatch
8) Handle currency and revenue normalization
Revenue reconciliation fails fast without consistent money handling.
Best practices:
- Store original currency and amount
- Normalize into a reporting currency using FX rate table
- Track FX rate timestamp and source
- Use decimal types, not floats
Example fields:
revenue_originalcurrency_originalrevenue_usdfx_ratefx_rate_date
9) Security and compliance
Because impression logs may contain user-level identifiers:
- Authenticate API clients with OAuth2/API keys/mTLS
- Encrypt in transit and at rest
- Hash or tokenize user identifiers
- Minimize PII
- Define retention policies
- Audit access to raw logs
10) Observability and quality controls
Monitor:
- ingestion rate
- error rate
- duplicate rate
- late-arrival rate
- null field rate
- reconciliation deltas
Add alerts for:
- sudden impression drops
- revenue spikes/drops
- partner feed delays
- schema drift
11) Suggested warehouse tables
raw_impression_events
- stores original JSON
- append-only
stg_impressions
- parsed and normalized events
fact_impressions
- deduped, validated, analytics-ready
fact_partner_billing
- partner invoice/report rows
reconciliation_results
- matched/unmatched/delta status by grouping key
12) Recommended implementation pattern
If you want a practical setup:
- API: FastAPI/Go/Node service
- Queue/stream: Kafka / Kinesis / PubSub
- Storage: S3/GCS/ADLS raw parquet/json
- Warehouse: Snowflake/BigQuery/Redshift
- Transforms: dbt/Spark
- Orchestration: Airflow/Dagster
- Monitoring: Datadog/Prometheus
13) Example reconciliation workflow
- Impression arrives at API
- Event is validated and stored raw
- Warehouse job loads it into staging
- Deduplication creates canonical impression rows
- Partner invoice data lands in a separate table
- Daily reconciliation query compares totals
- Exceptions are written to a discrepancy table
- Finance and ops review unresolved differences
14) A simple reconciliation query pattern
Aggregate both sides by the same grain:
datepartner_namecampaign_id
Then compare:
internal_impressionspartner_impressionsinternal_revenuepartner_revenue
This produces a clean mismatch report.
15) Key design principles
- Append-only, immutable logs
- Strong event IDs and idempotency
- Separate raw and normalized layers
- Currency normalization
- Deduplication and late-event handling
- Audit-friendly reconciliation tables
If you want, I can also provide:
- a sample OpenAPI spec for the impression endpoint,
- a warehouse schema for Snowflake/BigQuery, or
- a dbt reconciliation model example.
Observation history
See 1 earlier observation
Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.
How did Obsurfable measure this prompt?
Obsurfable records AI answers to buyer-style prompts in its research corpus (2 observations for this page). Metrics are distributions over observations, not a single static ranking.
Which AI systems does Obsurfable collect answers from?
OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.