# 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.

```python
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:

```python
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](./fill-your-crm-with-who-is-actually-visiting.md) ·
[export and keep in sync](./export-and-keep-in-sync.md)
