Exposure sources let you send ad-exposure data into MX8 Labs so it can be matched against survey respondents. Before setting one up, it is worth checking that the campaign is large enough to measure: Campaign size and measurability converts impressions, frequency and audience size into the respondent sample a lift study would need. This is the foundation of ad-effectiveness research - by linking who saw an ad with how they responded to a survey, you can measure real-world campaign impact.
How exposure data works
Every time a person is exposed to an ad, a record is created that captures an IP address (plaintext or hashed) plus the identifiers you configure — typically a user identifier (UID) and dimensions such as the brand associated with the ad, both of which are optional. Using the identifiers supplied with your exposure records, MX8 Labs determines whether a survey respondent was exposed, so you can compare exposed and unexposed audiences.
Two ways to send exposure data
MX8 Labs supports two ingest methods. Which one you choose depends on where your exposure data originates and how much control you need over the delivery process.
Pixel - A lightweight tracking endpoint hosted by MX8 Labs. You embed a pixel URL in your ad server or tag manager, and each time the pixel fires it captures an exposure event in real time. Pixel sources are ideal when you want a quick, low-integration setup and your ad platform supports third-party pixel calls.
S3 snapshot - A server-side file-based approach. You upload gzip-compressed CSV files containing exposure records directly into an MX8 Labs-managed S3 bucket. S3 snapshot sources are ideal when you already collect exposure data in your own systems and prefer to send it in batch, or when you need to hash IP addresses before they leave your environment.

The two source types also have different monitoring views. Pixel sources provide hourly delivery metrics such as impressions and UID presence. S3 sources provide per-file ingest history with Total, Retained, Imported, and Rejected row counts. See Exposure Source Reporting and Monitoring S3 Exposure Ingests.
Key terminology
- Source name - A human-readable label for your exposure source. Once created, this cannot be changed.
- Dimension - An additional attribute you attach to each exposure record (for example,
brand). Dimensions are used as query-parameter keys for pixels and as aggregation buckets in reporting. - Retention days - How long MX8 Labs keeps exposure records for matching. You set this when creating the source — choose a window that suits your campaign timeline.
- UID - Your user identifier, up to 128 characters, sent alongside each exposure record.
- Hashed IP - An IP address that has been run through a hashing algorithm (currently MD5) before being sent to MX8 Labs. This lets you avoid transmitting plaintext IP addresses.
Using exposure data in your survey
Once your exposure source is sending data into MX8 Labs, you wire it into a survey with s.get_exposed_values(...) and an s.tag() block. get_exposed_values looks up the current respondent's exposed values for the dimension you configured on the source and returns a list — one entry per exposed value. Wrapping the rest of the survey in with s.tag(exposed=...): tags every question inside the block with the respondent's exposure status. Select that tag as the Indicator Tag when you set up your lift report.
from survey import Survey
s = Survey(**globals())
exposed_brands = s.get_exposed_values(
source="campaign-123",
exposed_dimension="brand",
)
with s.tag(exposed=bool(exposed_brands)):
# The rest of your survey runs inside the block.
# Every question is tagged with the respondent's
# exposure status automatically.
s.select_question(
"How familiar are you with this brand?",
options=["Very familiar", "Somewhat familiar", "Not familiar"],
)
# ... other questions ...
s.complete()
Exposure recency and frequency
get_exposed_values creates a reportable multi-value exposure response. Each matched value becomes its own reporting row and carries tags that you can read in survey code, use in data preparation, or include as reporting context:
frequency— how many times the respondent was exposed for that value.days_since_exposure— the number of days since the most recent exposure, when recency data is available.
For example, if a respondent matched Brand A three times and most recently two days ago, the reportable Brand A value carries frequency=3 and days_since_exposure=2. Recoding and reporting preserve those tags. When no valid recency timestamp is available, frequency is still reported and days_since_exposure is omitted rather than invented.
This lets you branch on frequency or recency without leaving the survey. For example, you can route heavy-exposure respondents to a different set of questions, or restrict the lift comparison to people exposed within the last 7 days:
exposed_brands = s.get_exposed_values(
source="campaign-123",
exposed_dimension="brand",
)
# Flag heavy, recent exposure across the matched values.
recent_heavy_exposure = any(
value.frequency >= 3 and value.days_since_exposure <= 7
for value in exposed_brands
)
with s.tag(recent_heavy_exposure=recent_heavy_exposure):
s.rating_question(
"How likely are you to consider this brand?",
number_of_points=5,
)
To tag media items as exposed based on the respondent's matched exposures — for example, only showing creatives the respondent has actually seen — use s.tag_exposed_media(...). Tagged media items expose the same frequency and days_since_exposure attributes.
For the reporting side, see Reporting lift against control groups.
Next steps
- Creating a Pixel Exposure Source - set up real-time tracking in minutes.
- Creating an S3 Snapshot Exposure Source - configure server-side batch delivery.

