Keep an ad audience in sync
The job: stop paying to advertise to people who already converted, and start paying to reach the companies who read you and did not.
Who this is for: anyone spending on LinkedIn, Meta or Google audiences.
Two audiences, opposite jobs
Suppression — existing customers, and anyone you have already won. Every impression served to them is wasted budget and mild irritation.
Retargeting — high-intent visitors you have not converted. These are the people worth paying to reach again.
Both come from one endpoint with a different filter.
suppress = [a for a in get_all("/accounts", classification="customer")]
retarget = [
v for v in get_all("/visitors", min_intent=60)
if v["classification"] == "lead" and not v["is_locked"]
]
Uploading
Ad platforms take hashed emails. Hash on your side, never send plaintext:
import hashlib
def normalise(email: str) -> str:
return email.strip().lower()
def sha256(email: str) -> str:
return hashlib.sha256(normalise(email).encode()).hexdigest()
platform.upload_audience(
name="signal-high-intent-leads",
hashed_emails=[sha256(v["resolved_email"]) for v in retarget
if v.get("resolved_email")],
)
Normalising before hashing matters — Dana@Acme.com and dana@acme.com hash
differently and the platform will match neither.
Replace, do not append
Audiences go stale in one direction: a lead who became a customer stays in your retargeting list forever unless you remove them. Rebuild the whole audience on each run rather than adding to it. It is one more API call and it removes an entire class of embarrassing spend.
Consent, which is not optional
Signal identifies visitors under the lawful basis you configured, and that is not the same basis as advertising to them. Before you upload anything:
- check what your privacy policy told these people;
- honour suppression — the enrichment suppression list exists for DSAR requests and an audience upload must respect it;
- keep the audience out of any region where you have not established a basis. Identification is US-only by design; your ad targeting should not be wider than your lawful basis.
If you cannot answer "what did we tell this person we would do with their address", do not upload it.
What will go wrong
Match rates look broken. 20–40% is normal. The platform only matches addresses it already has; a low rate is not a sign your data is wrong.
Minimum audience sizes. Most platforms refuse audiences under ~300 matched records. A precise, small, high-intent list may be unusable — which is an argument for using it in outbound rather than ads.
resolved_email can be null. Filter before hashing or you will upload the
string "None" several hundred times.
Next: fill your CRM with who is actually visiting · export and keep in sync
Markdown source: /developers/workflows/keep-an-ad-audience-in-sync.md