Build an Audience from Your Website Visitors

Turn the people your pixel recognizes into an audience you can retarget, route to sales, or suppress

Availability: website visitor audiences are rolling out behind a feature flag and are not yet enabled for every project.

flowchart LR
    A[Pixel on your site] --> B[Create pixel audience]
    B --> C[Poll Until Complete]
    C --> D[Download visitors]
    C --> E["Use as a WEBSITE_VISITORS rule<br/>in other audiences"]

    style A fill:#3b82f6,color:#fff
    style D fill:#22c55e,color:#fff
    style E fill:#22c55e,color:#fff

Use Case

A website visitor audience (type: "pixel") is the set of people your pixels recognized on your site over a lookback window, optionally narrowed by the pages they viewed and by person or company filters. It is rebuilt every day, so it follows your traffic.

Three common uses:

  • Retargeting. Send everyone who looked at your pricing page in the last 30 days to your ad platform. Each row is a person with all of their hashed emails, so it works as an audience for matching.
  • Routing hand-raisers to sales. Set source_pixel_output to "new" so each daily refresh contains only people who newly qualified since the previous build, such as first-time visitors to /demo. Feed that list to your CRM or sales alerts without re-sending the same people.
  • Suppression. Exclude a visitor audience from any other audience with a WEBSITE_VISITORS rule, for example to keep current customers (visitors to /app/login) out of a prospecting campaign.

Prerequisites


Steps

1. Create the Visitor Audience

import time
import requests

API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
ORG_ID = "your_organization_id"
PROJECT_ID = "your_project_id"
PIXEL_ID = "your_pixel_id"

HEADERS = {
    "X-Api-Key": API_KEY,
    "X-Api-Secret": API_SECRET,
    "Content-Type": "application/json",
}
AUDIENCES = "https://api.delivr.ai/api/v1/audiences"

resp = requests.post(
    AUDIENCES,
    headers=HEADERS,
    params={"project_id": PROJECT_ID},
    timeout=30,
    json={
        "organization_id": ORG_ID,
        "project_id": PROJECT_ID,
        "audience_name": "Pricing page visitors, last 30 days",
        "segmentation_type": "Audience",
        "type": "pixel",
        "source_pixel_ids": [PIXEL_ID],
        "source_pixel_window_days": 30,
        "source_pixel_include_urls": ["/pricing"],
        "source_pixel_exclude_urls": ["/careers"],
        "source_pixel_min_visit_days": 1,
        "source_pixel_output": "all",
        "filter": {},
    },
)
resp.raise_for_status()
visitors_id = resp.json()["id"]

filter is optional narrowing. Send {} to take everyone who qualifies, or add person and company conditions (for example seniority_level or company_employee_count_range) to keep only matching visitors. A visitor audience cannot use intent topics, uploaded lists, or WEBSITE_VISITORS rules in its own filter, and it does not accept record_limit.

2. Settings

FieldTypeDefaultDescription
source_pixel_idsarray of strings(required)1 to 20 pixels in this project.
source_pixel_window_daysinteger30Lookback window, 1 to 90 days (UTC), counted back from today.
source_pixel_include_urlsarray of strings[]Up to 20 page patterns. A person qualifies only if they viewed any page matching any pattern. Empty means any page counts.
source_pixel_exclude_urlsarray of strings[]Up to 20 page patterns. A person is dropped if they viewed any page matching any pattern.
source_pixel_min_visit_daysinteger1Distinct days the person was seen, from 1 up to the window.
source_pixel_outputstring"all""all" returns everyone who qualifies. "new" returns only people who were not in the previous build.

Page patterns are case-insensitive substring matches on the full page URL, so /pricing matches https://example.com/Pricing?plan=pro. The include and exclude rules apply to the person, not to a single visit: "viewed any" means at least one page view in the window matched, and "never viewed any" means no page view in the window matched. Exclusion wins when both apply.

With "new", the first build has nothing to compare against and returns everyone who qualifies. Later builds return only people who were not in the previous completed build. A person who drops out and later qualifies again is new again.

The audience renews daily unless you set renew_frequency. People who have opted out of data sharing are removed from the results.

3. Poll Until Complete

def wait_for(audience_id):
    while True:
        body = requests.get(
            f"{AUDIENCES}/{audience_id}", headers=HEADERS, params={"project_id": PROJECT_ID}, timeout=30
        ).json()
        if body["status"] in ("Completed", "Validated"):
            return body
        if body["status"] == "Failed":
            raise RuntimeError(body.get("error"))
        time.sleep(15)

visitors = wait_for(visitors_id)
print(f"{visitors['size']} visitors, ready: {visitors['source_pixel_ready']}")

source_pixel_ready is true once the audience has completed a build. You need it before another audience can reference this one (step 5). Preview and download work as in Create an Intent Audience, steps 4 and 5.

4. Change the Definition

Update any source_pixel_* setting with PUT /api/v1/audiences/{id}. Send only the fields you want to change; the rest keep their stored values, and the combined result is validated as a whole. A change rebuilds the audience.

requests.put(
    f"{AUDIENCES}/{visitors_id}",
    headers=HEADERS,
    params={"project_id": PROJECT_ID},
    timeout=30,
    json={"source_pixel_output": "new"},
).raise_for_status()

5. Use Visitors in Another Audience

Add a WEBSITE_VISITORS rule to an intents or contact audience. The value is a list of visitor audience IDs as strings.

resp = requests.post(
    AUDIENCES,
    headers=HEADERS,
    params={"project_id": PROJECT_ID},
    timeout=30,
    json={
        "organization_id": ORG_ID,
        "project_id": PROJECT_ID,
        "audience_name": "Cloud Computing Intent, also on our pricing page",
        "segmentation_type": "Audience",
        "type": "intents",
        "filter": {
            "condition": "and",
            "rules": [
                {"fieldName": "INTENT", "conditionRules": {"operator": "in", "value": ["4eyes_119418"]}},
                {"fieldName": "WEBSITE_VISITORS", "conditionRules": {"operator": "in", "value": [str(visitors_id)]}},
            ],
        },
    },
)
resp.raise_for_status()

Use not in to suppress instead:

{"fieldName": "WEBSITE_VISITORS", "conditionRules": {"operator": "not in", "value": [str(customers_id)]}}

Listing several visitor audiences in one in rule matches anyone in any of them. A not in rule removes anyone in any of them.

Rules for the condition:

  • Top level, joined with AND. Place it directly in the outer rules list of a group whose condition is and. Inside a nested group or an or group it is rejected.
  • Same project. Every ID must be a visitor audience in the audience's own project.
  • Finished first. The referenced audience must have completed at least one build (source_pixel_ready is true). Otherwise the request returns 400 with code visitor_reference_unresolved.
  • Latest build. The rule matches people against the referenced audience's most recent completed build, read each time the audience that uses it builds. With source_pixel_output: "new", that means only the people in the latest refresh, not everyone who has ever qualified.
  • Matching is by hashed email. Every hashed email we hold for a visitor counts toward the match.
  • Not portable. An audience with a WEBSITE_VISITORS rule cannot be cloned to another project or published as a template.

Output Columns

Each row is one person. The columns are the usual contact profile columns (name, job title, seniority, company fields, hashed emails including emails_sha256_lc_hem, and so on), plus these, all returned as strings:

ColumnDescription
pixel_first_seenFirst time the person was seen in the window (ISO 8601, UTC).
pixel_last_seenMost recent time the person was seen in the window (ISO 8601, UTC).
pixel_page_viewsNumber of events recorded for the person in the window.
pixel_visit_daysNumber of distinct days the person was seen.
pixel_pagesUp to 20 distinct page URLs viewed, most recent first, joined with , . Filled only when source_pixel_include_urls or source_pixel_exclude_urls is set; otherwise empty.
pixel_idsThe pixels that saw the person, joined with , .

Use select to narrow the columns, as with other audiences.


Errors

HTTP StatuscodeWhen
400pixel_not_in_projectA pixel in source_pixel_ids is not a pixel of this project (including IDs that do not exist).
400visitor_reference_unresolvedA WEBSITE_VISITORS rule names an audience that is not a finished visitor audience in this project.
400(none)Invalid settings: more than 20 pixels or page patterns, a window outside 1 to 90, source_pixel_min_visit_days larger than the window, an unknown source_pixel_output, source_pixel_* fields on a non-pixel audience, or record_limit on a pixel audience.

Next Steps


Did this page help you?