Advertising & Publisher Embed
United Pet Club runs its own ad engine. Partners can surface its ads two ways, both backed by
the same anonymous endpoints under /api/advertising/v1/public/:
| Tier | Effort | For |
|---|---|---|
| Embed script | one script tag + one div per slot | Websites that want UPC ads inline, with revenue share |
| Public decision API | direct integration | Native apps and custom front ends |
Embed script
Section titled “Embed script”- In the UPC Dashboard, under Advertising → Earn From Ads → Partner websites, register
your domain (a bare host name such as
example.com; no scheme, port, path or IP address) and verify ownership with the<meta name="upc-ads-site-verification" content="…">tag on your home page or a DNS TXT recordupc-ads-site-verification=…. Verification gives you a site key. - Add the loader once per page and a container per slot:
<script async src="https://gateway.unitedpetclub.com/api/advertising/v1/public/embed.js" data-site-key="YOUR_SITE_KEY"></script>
<div data-upc-ad="EXTERNAL_SITE_NATIVE"></div><div data-upc-ad="EXTERNAL_SITE_BANNER"></div>The script collects every [data-upc-ad] element, asks for all of them in one decision call
(surface: EXTERNAL_SITE, your siteKey, the page URL and <html lang>), renders a native
card or a 300×250 banner with the Sponsored label and a click-through link, and hides a
container that has nothing relevant to show. It is self-contained — no third-party code, no
cookies; the viewer key is a random value kept in localStorage only to cap frequency. Ads
on partner sites are never personalised.
Placements available to partner sites: EXTERNAL_SITE_NATIVE (one native card) and
EXTERNAL_SITE_BANNER (300×250). Earnings accrue per billable impression/click under the
publisher program’s site rate and are paid out from the Dashboard.
Public decision API
Section titled “Public decision API”POST /api/advertising/v1/public/decisionsContent-Type: application/json
{ "surface": "MOBILE", "slots": [{ "placement": "MOBILE_FEED_NATIVE", "count": 1 }], "viewerKey": "random-stable-per-install", "locale": "de-DE", "geo": { "country": "DE", "region": "BY", "isPrecise": true }, "context": { "speciesCode": "DOG", "breedSlug": "labrador-retriever" }, "deviceKind": "MOBILE", "consentAdvertising": false, "pageUrl": "app://feed"}The response envelope carries resultCode (Advertising_Engine_GT_Decision_SU on success)
and data:
| Field | Meaning |
|---|---|
slots[].placement / ads[] | One entry per requested placement; an empty ads with collapse: true means render nothing |
ads[].kind | PAID, HOUSE, AFFILIATE, EXTERNAL_NETWORK or NETWORK_NATIVE — exactly one of creative, affiliate, external is set. NETWORK_NATIVE is an ad network’s ad that United Pet Club fetched for you: render its creative exactly like a PAID one (show advertiserName under it) and send its events the same way; there is no script to load. Treat any kind you don’t recognise as “render nothing” |
ads[].token / expiresAtUtc | Signed token you send back with every event; valid for 15 minutes |
ads[].disclosureLabel | The label you must show on the unit (e.g. Sponsored; Affiliate link on AFFILIATE units) |
affiliate | AFFILIATE only: a product card (retailer, itemCode, title, imageUrl, clickUrl, priceMinor + currencyCode when a current price is known, isPrime). Show the retailer’s name (View on Chewy) and the label. Open clickUrl as-is: for retailer products it is a UPC click-through URL, for Amazon the product page |
creative.clickUrl | Always a UPC click-through URL — open it as-is; the landing page is resolved server-side |
creative.impressionPixels | Rare, NETWORK_NATIVE only: image URLs the network counts views by. Load each once (as an image, never a script) when you send the IMPRESSION event; absent means there is nothing to load |
geo.source | Which input decided the viewer’s region: CLIENT, PROFILE, IP or LOCALE |
Report what happened:
POST /api/advertising/v1/public/events{ "events": [ { "token": "…", "kind": "IMPRESSION" }, { "token": "…", "kind": "VIEWABLE", "viewableMs": 1200 } ], "viewerKey": "…" }kind is one of IMPRESSION, VIEWABLE, CLICK, CONVERSION, VIDEO_START,
VIDEO_COMPLETE, DISMISS. Each kind is counted once per decision — resending is harmless.
Clicks should open GET /api/advertising/v1/public/c/{token}, which redirects to the landing
page. Viewers can report a unit with POST /api/advertising/v1/public/reports
(token, reasonCode: MISLEADING | OFFENSIVE | IRRELEVANT | BROKEN | OTHER).
Rules for direct integrations: show the disclosureLabel; never serve a slot the decision did
not return; send consentAdvertising: true only when the user has actually opted in to
personalised advertising in your app; request all of a screen’s slots in one call so the
per-screen density limit can apply.