Click tracking
Visitors reach merchants through tracked links. Every offer in the public API has an outboundUrl. When a visitor follows it, the platform records a click and redirects to the merchant. This is how the platform counts how many visitors each product, merchant and website sends out.
The outbound link
An outboundUrl looks like this:
https://<redirect domain>/go/<offerId>?w=<websiteId>&s=<signature>
- The host is the website's redirect domain, or the platform's own domain when none is set.
wis the website id andsa signature the platform issued for that website and offer. Only links the API handed out are accepted, so clicks cannot be forged and offers cannot be enumerated. The signature does not cover the host, so links keep working when the domain changes.- Open the link in the visitor's browser. It needs no token. Use it as the
hrefof your buy button, withrel="nofollow sponsored".
What GET /go/:offerId does
| Case | Result |
|---|---|
| Live offer, valid signature | A click is recorded and the visitor is redirected (302) to the merchant's product page. |
| Bot or crawler user agent, or no user agent | Redirected, not counted. |
| Offer inactive, or product or merchant deprecated | Redirected to the merchant's home page (or the website's), not counted. |
| Invalid signature or unknown offer | Redirected to the website's home page, not counted. |
| Unknown website id | 404. |
The response is never cached (Cache-Control: no-store) and is marked noindex, nofollow. The click stores the referrer host only (no path or query), so no visitor data is kept.
Redirect domain
Set a Redirect domain on the website to keep links on a domain of your own, such as go.shop.example. See Websites. There are two ways to serve it.
Point the domain at the platform
Add a CNAME record to the platform's host. The platform answers /go on any host, so no code is needed. On Cloudflare Workers the domain must also be listed in DEPLOYMENT_EXTRA_HOSTS so the Worker answers it.
Serve /go yourself
The @product-comparison/redirect package is a redirect client you run in the website's own frontend. It resolves each link through the API, caches the answer and redirects the visitor, then reports clicks in batches. With it the website serves /go on its own domain.
GET /api/public/v1/go/:offerId?s=<signature>returns where the link leads. It never records a click.- The client reports clicks with
POST /api/public/v1/clicksin batches.
It also answers GET /go/_ping, which the redirect domain check uses (see below).
GET /api/public/v1/go/:offerId
Authenticated with the website token. Query: s, the signature.
curl -H "Authorization: Bearer $LISTINGS_TOKEN" \
"https://platform.example/api/public/v1/go/offer_123?s=0123456789abcdef"
{ "target": "https://shop.example/products/ettan-loos", "kind": "offer", "trackable": true, "ttl": 300 }
| Field | Meaning |
|---|---|
target | Absolute http or https URL to redirect the visitor to. |
kind | offer for the merchant's product page, fallback for a home page (unknown, inactive or forged link). |
trackable | Whether to report a click on this link. false for fallbacks. |
ttl | Seconds the resolution may be cached: 300 for offers, 60 for fallbacks. |
POST /api/public/v1/clicks
Authenticated with the website token. Send up to 100 entries per request. Each entry stands for count clicks on the same link in the same hour from the same referrer, so a busy link costs one entry per flush, not one request per click.
| Field | Description |
|---|---|
offerId | The offer id from the link. |
s | The signature from the link: 16 hex characters. |
at | Optional ISO 8601 time inside the hour of the clicks, with offset. Defaults to now. Times older than 24 hours are clamped to 24 hours ago. |
referrerHost | Optional referrer host, up to 253 characters. |
count | Number of clicks, 1 to 1000. Default 1. |
curl -X POST -H "Authorization: Bearer $LISTINGS_TOKEN" -H "Content-Type: application/json" \
-d '{"clicks":[{"offerId":"offer_123","s":"0123456789abcdef","referrerHost":"google.com","count":3}]}' \
"https://platform.example/api/public/v1/clicks"
{ "accepted": 1, "rejected": 0 }
Entries with a bad signature or an offer outside the website's project are dropped and counted in rejected. Accepted entries are stored as individual clicks, so they count exactly like clicks on the platform's own /go.
With the typed client: client.outbound.reportClicks(entries). See Listings client.
Check the redirect domain
After you save a redirect domain, the platform requests https://<domain>/go/_ping and shows who answers: Points to this instance, Self-hosted redirect client, Unknown server or Not reachable. The check is informational only. Links use the domain whatever the result.
Where clicks show up
- Analytics: total clicks against the previous period, clicks over time, Most clicked products, Most clicked merchants, Clicks per website, and click events in Recent events. See Analytics.
- Overview: clicks of the last 24 hours.
- Products, Merchants and Offers: clicks of the last 30 days in the lists (sortable) and on each detail page.
- Websites: clicks of the last 7 days per website.
Merging products or merchants moves their clicks to the destination.