Skip to content

Audience collect API

United Pet Club members on the Pro level of Audience Insights can connect their own websites so visits there are counted next to their UPC pages and attributed to the animals, litters and listings the pages are about. This reference is for the developers, agencies and plugin authors who wire a member’s site to the collector.

Base URL: https://audience.unitedpetclub.com (test: https://audience-test.unitedpetclub.com, staging: https://audience-staging.unitedpetclub.com). The collector is a dedicated public host, not the API gateway: it needs no partner token, accepts anonymous cross-origin posts from registered sites, and answers 202 immediately — nothing on it can slow a page down.

A member registers a site at Dashboard → Insights → External Sites and receives two values:

ValueShapeWhere it may liveUsed by
Site keypk_…in the page (data-site, X-Upc-Site, ?k=) — public, printablethe browser script; identifies the site
Site secretsk_…a server, a plugin’s settings, an environment variable — never a web pagethe server collector (Authorization: Bearer sk_…); makes the body’s visitorIp / userAgent trusted

The secret is shown once at registration; the platform stores only a hash. The member can rotate it from the same screen (the old one stops working at once). Hits are accepted only while the site is active — i.e. the member has verified the domain (DNS TXT upc-audience-verify=<token> or <meta name="upc-audience-verification" content="<token>"> on the home page) and the site is not suspended.

The simplest integration is one tag on every page:

<script defer src="https://audience.unitedpetclub.com/api/upc.js" data-site="pk_your_site_key"></script>

upc.js is dependency-free and posts to {script origin}/api/v1/hits unless data-endpoint overrides it. On load and on every pushState / popstate navigation it sends one hit carrying the page URL, title and referrer, the screen and viewport size, device pixel ratio, colour depth, orientation, touch, colour scheme, reduced-motion preference, languages, IANA time zone and offset, connection type, and the page facts below. When the page is hidden or unloaded it sends a follow-up (update: true) with the time the visitor actually spent (visible, focused, with input in the last 30 s) and the maximum scroll depth, via navigator.sendBeacon with a fetch keepalive fallback.

Referrer on client-side navigations (version 2). The first hit of a page load sends document.referrer: the site that sent the visitor. For each later pushState / popstate navigation, version 2 sends the previous page’s URL as the referrer, because the browser keeps document.referrer fixed at the first load. So only the page a visitor landed on is credited to Google, an AI assistant or a campaign, and later page moves count as internal. Version 1 repeated the first referrer on every navigation of a single-page app, which counted each in-app page as a new search or social referral. window.__upcAudience.version reports "2".

URLCacheUse it when
https://audience.unitedpetclub.com/api/upc.{version}.js, e.g. upc.2.jsone year, immutableRecommended. Browsers fetch the script once and reuse it. When the changelog announces a new version, change the number in your tag.
https://audience.unitedpetclub.com/api/upc.jsone hourYou cannot edit the tag easily. Visitors pick up a new version within an hour, at the cost of a revalidation per hour.

The number in the path is only a cache key: every URL serves the current script. Pinning upc.2.js does not keep you on version 2 once version 3 ships. It means visitors who already have the file keep using it until their cache expires or you change the number. (Until 2026-09-29 the bare upc.js was also cached for a year, so browsers could keep a copy that never updated. Pinning a version avoids that ambiguity.)

<script defer src="https://audience.unitedpetclub.com/api/upc.2.js" data-site="pk_your_site_key"></script>
Attribute / globalEffect
data-site (or window.__upcAudienceSite)the site key — required; without it the script does nothing
data-endpointalternative collect URL (default {script origin}/api/v1/hits)
data-consent="granted"lets the script set the persistent visitor cookie upc_vid (random, one year, SameSite=Lax, Secure on https). Without it, and always when the browser sends Global Privacy Control, the script is cookieless
window.__upcAudienceConsent = true | falsethe same switch from your consent manager, evaluated at each send

The script never sends the raw IP (the server sees it), e-mail, names, form values, or query parameters that look like tokens. Returning-visitor accuracy without the cookie relies on a daily rotating hash on the server side; two sites never share a visitor identity.

To attribute a visit to a platform entity, declare the entity on the page. The script reads every <meta name="upc:subject"> (a KIND:guid, several allowed, comma / semicolon / space separated; the first is the primary subject) and every data-upc-subject attribute, plus:

<meta name="upc:subject" content="ANIMAL:8a6f1c2e-3b4d-4e5f-9a1b-2c3d4e5f6a7b">
<meta name="upc:subject" content="LITTER:0f9e8d7c-6b5a-4f3e-8d2c-1b0a9f8e7d6c">
<meta name="upc:page-kind" content="ANIMAL">
<meta name="upc:owner" content="<owner profile id>">

Subject kinds: PROFILE, ANIMAL, LITTER, LISTING, CONTRACT, RECIPE, BLOG_POST, ARTICLE, PETLINK_PAGE, PETLINK_NODE, STOREFRONT, OFFER, DESIGN_TEMPLATE, SHORT_LINK, TAG, EVENT, COURSE, PAGE. Page kinds: HOME, PROFILE, ANIMAL, LITTER, LISTING_HUB, LISTING, STORE, PRODUCT, BLOG_HUB, BLOG_POST, ARTICLE, RECIPE, CONTRACT, PETLINK, SEARCH, BREED, SPECIES, STATIC, OTHER. The ids are the platform ids of the member’s entities (an animal’s id is in its UPC page address). A visit to a page with no subject still counts for the site; it just does not roll up to an entity.

Outbound-link clicks and file downloads (pdf, docx, zip, images, media, …) are recorded automatically. Anything else:

upc.track("CONTACT", { subject: "ANIMAL:8a6f…", code: "email-breeder" });
upc.track("PURCHASE", { valueMinor: 12500, currency: "CAD", props: { sku: "PUPPY-KIT" } });

kind is one of CLICK, OUTBOUND_CLICK, DOWNLOAD, CONTACT, INQUIRY, SHARE, SAVE, ADD_TO_CART, PURCHASE, DONATE, SCAN, VIDEO_PLAY, FORM_SUBMIT or CUSTOM; options are code (max 64), url, subject, valueMinor + currency, and props (small JSON, no personal data by contract). window.__upcAudience also exposes pageView(), flush() and version.

POST /api/v1/hits — Content-Type: application/json (the script posts text/plain to stay a simple request), body ≤ 64 KB, ≤ 200 hits per envelope. POST /api/v1/actions is an alias for action-only envelopes. The site key travels in X-Upc-Site or ?k=, or as site in the body.

{
"site": "pk_your_site_key",
"sentAtUnixMs": 1788300000000,
"hits": [
{
"hitId": "019a3f1e-7c2b-7d4e-8a9b-0c1d2e3f4a5b",
"url": "https://www.example.com/puppies/luna",
"host": "www.example.com",
"path": "/puppies/luna",
"title": "Luna — available",
"referrer": "https://www.google.com/",
"occurredAtUtc": "2026-09-06T14:03:11Z",
"collector": "SERVER",
"visitorIp": "203.0.113.42",
"userAgent": "Mozilla/5.0 …",
"acceptLanguage": "en-CA,en;q=0.9",
"subjects": ["ANIMAL:8a6f1c2e-3b4d-4e5f-9a1b-2c3d4e5f6a7b"],
"pageKind": "ANIMAL"
}
],
"actions": []
}
FieldTypeNotes
hitIdUUID (v7 preferred)client-minted; a browser hit that reuses a server hit’s id merges into it (one visit, both sets of facts). Otherwise two hits for the same visitor and path within 10 s merge on their own
url, host, path, locale, title, referrerstringquery strings are dropped except for UTM / click-id parameters
occurredAtUtcISO 8601re-based on the server clock using sentAtUnixMs
collectorEDGE | BROWSER | SERVERwho produced this input
visitorIp, userAgent, acceptLanguage, clientHintsstring / objecttrusted only from the server collector (bearer secret); a browser post carrying them is ignored
signedIn, profileId, profileKindCode, professionalKindCodes, accountKindCodeserver-only; a signed-in UPC member on your site (rare outside UPC-hosted sites)
httpStatus, prerenderserver-only; speculative loads are counted separately, never as visits
visitorIdstringthe upc_vid value, when consented
screen { w, h, dpr, colorDepth, orientation }, viewport { w, h }, touch, colorScheme, reducedMotion, languages[], timeZone, utcOffsetMinutes, connectionbrowser facts
durationMs, activeMs, scrollDepthPct, updateengagement; update: true marks a follow-up for an earlier hitId
subjects[], pageKind, ownerProfileIdwhat the page is about; first subject is primary

verifiedCrawler and internal exist on the wire but are honoured only from the platform’s own edge collector — a partner cannot mark a hit as a verified crawler.

hitId, kind, code, targetUrl, subject, valueMinor, currencyCode, props, occurredAtUtc, and (server collector only) visitorIp, userAgent, visitorId, profileId.

The collector decides how much of the body to believe from the credential it sees:

CredentialCollectorBody fields believed
Authorization: Bearer sk_… matching the siteSERVEReverything except verifiedCrawler / internal
site key onlyBROWSERpage and device facts; IP, UA and profile fields are stripped. The request’s Origin (or Referer) host must be one of the site’s registered domains — a post from any other origin answers 403 as an invalid site key. Add every host you serve from (example.com and www.example.com are different) under Extra domains

CORS is open for the collect routes: the response echoes the request Origin (Access-Control-Allow-Origin: <origin>, Vary: Origin), allows POST, GET, OPTIONS and the headers Content-Type, X-Upc-Site, X-Upc-Hit, Authorization, and caches the preflight for a day. OPTIONS answers 204.

202 Accepted — { "data": { "accepted": 1, "dropped": 0, "sampled": false }, "resultCode": "Audience_AudienceCollect_AD_Hit_SU" }. sampled: true means the site is under a flood valve and this batch was dropped by sampling; kept batches are re-weighted, so totals stay approximately right.

StatusresultCodeMeaning
400Audience_AudienceCollect_VL_Request_RJmalformed envelope
401Audience_AudienceCollect_VL_SiteSecret_RJbearer secret does not match
403Audience_AudienceCollect_VL_SiteKey_RJunknown key, site not active (unverified), or the browser Origin is not a registered domain
403Audience_AudienceCollect_VL_Site_RJsite suspended by the platform
413Audience_AudienceCollect_VL_Envelope_RJover 64 KB
429Audience_AudienceCollect_HD_Rate_RJover 100 hits/s (burst 1,000) for this site — retry with back-off
503Audience_AudienceCollect_HD_Disabled_FLcollection switched off platform-wide — retry later

GET /api/v1/sites/{siteKey}/ping → { "site": "pk_…", "ok": true, "collectEnabled": true } for a plugin’s settings screen.

Server-side integration (plugins, hosted sites)

Section titled “Server-side integration (plugins, hosted sites)”

Send a server hit from the request that renders the page — for WordPress, template_redirect — with the visitor’s IP and User-Agent, and put the same hitId in a <meta name="upc:hit"> tag so the browser script’s hit merges into it. That combination counts cached pages, crawlers and JavaScript-off readers (the server hit) and durations, screens and scroll (the browser hit) as one visit. Rate limit on your side to well under 100 hits/s per site.

Page registration. On UPC’s own sites every page tells the platform what it is about at render time; on your site the platform learns it from the upc:subject meta tags the script sends and from the subjects / pageKind / ownerProfileId you put on server hits. Declare the facts on every hit rather than once — a hit that arrives before the page is known is re-attributed within 15 minutes once it is, but a page nobody declared stays a plain site visit. The plugin-config read the member sees (GET …/me/sites/{id}/plugin-config) renders exactly this: the site key, the collect URL, the script tag, the verification record and a subject-meta example.

Reading a member’s insights programmatically — a summary and the visit log per subject for a member’s own custom website — is planned behind the AUDIENCE_API right, which Audience Insights Pro includes (the yearly AUDIENCE_PRO add-on; see Platform prices). The token model is still to be decided, so nothing is exposed to partners today; the tier-gated reads (/api/audience/v1/me/...) exist only for the signed-in United Pet Club apps. Watch the Changelog.