Integrate with the API (server)
Fetch the ad on your backend, render it in your server-rendered HTML, and fire the impression pixel from your server too.
Referer header on the pixel call.1. Get your placement ID
Create a placement in the publisher dashboard and copy the ID. Register the domain you'll show ads on.
2. Fetch the ad (forward visitor metadata)
Call the serve endpoint from your backend. Forwarding the visitor's IP is required. Impression dedup hashes per IP: without it, every visitor is hashed under your server's single IP and you'll record one impression per hour total instead of one per visitor. Full parameter list: Serve endpoint parameters.
X-Forwarded-For (first entry), CF-Connecting-IP, or whichever header your platform sets, otherwise you'll forward your edge node's IP and dedup collapses every visitor into one event per hour.Node (built-in fetch, framework-agnostic):
// Read the visitor IP from the forwarded header your platform sets.
const xff = req.headers["x-forwarded-for"];
const visitorIp = (typeof xff === "string" ? xff.split(",")[0].trim() : "")
|| req.headers["cf-connecting-ip"]
|| req.socket?.remoteAddress
|| "";
const url = new URL("https://adventory.to/api/ad/serve");
url.searchParams.set("p", PLACEMENT_ID);
url.searchParams.set("ip", visitorIp); // required
url.searchParams.set("ua", req.headers["user-agent"] || ""); // recommended
const res = await fetch(url, { cache: "no-store" });
const { ad } = await res.json();Python (requests, behind a proxy):
import requests
xff = request.headers.get("X-Forwarded-For", "")
visitor_ip = xff.split(",")[0].strip() if xff else request.remote_addr
params = {
"p": PLACEMENT_ID,
"ip": visitor_ip, # required
"ua": request.headers.get("User-Agent"), # recommended
}
r = requests.get("https://adventory.to/api/ad/serve", params=params, timeout=2)
ad = r.json().get("ad")PHP (behind a proxy):
$xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$visitorIp = $xff
? trim(explode(',', $xff)[0])
: ($_SERVER['REMOTE_ADDR'] ?? '');
$query = http_build_query([
'p' => PLACEMENT_ID,
'ip' => $visitorIp, // required
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '', // recommended
]);
$res = file_get_contents("https://adventory.to/api/ad/serve?$query");
$ad = json_decode($res, true)['ad'];country is also accepted as an optional query parameter (ISO 3166-1 alpha-2). Skip it unless your platform exposes a geo-lookup header (e.g. CF-IPCountry on Cloudflare, X-Vercel-IP-Country on Vercel); the serve endpoint falls back to its own geo lookup if you don't pass it.
3. Render the ad
Mix the ad fields into your server-rendered HTML. Same field mapping as the browser path: product_name for the title, headline for the tagline, logo for the image, cta_text for the button label, colors.* for theming, click_url as the CTA href. Wrap the CTA with rel="noopener sponsored" (the sponsored rel marks the link as a paid placement, per the Google guidance for paid links). Full response shape: Serve response reference.
Don't forget the attribution: render one of ad.attribution.texts visibly near the ad, linked to ad.attribution.url. The attribution link is a disclosure (not a paid link), so use rel="noopener" only. Full rules: Attribution guidelines.
pending and serve returns a placeholder ad with impression_url: null. You can wire up rendering and click-through end-to-end during this window; nothing counts toward verification until the placement flips to active. Lifecycle: Verification.4. Fire the impression pixel (set Referer)
Fire ad.impression_url from your server once the page has been served to the visitor. You must set the Referer header to the URL of the page where the ad was rendered. Server-side runtimes don't set it for you, and the impression endpoint validates it against the placement's registered domain. Pixel mechanics (dedup, caching): Impression pixel reference.
Referer from outgoing fetch as a forbidden request header, so your pixel call will arrive header-less and get rejected. Set export const runtime = "nodejs" in your route, or fire the pixel from a regular Node server. (The Node fetch sample below works in Node 18+.)Node:
await fetch(ad.impression_url, {
headers: { Referer: pageUrl },
});Python:
import requests
requests.get(ad["impression_url"], headers={"Referer": page_url})PHP:
$ch = curl_init($ad['impression_url']);
curl_setopt($ch, CURLOPT_REFERER, $page_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);Why and what happens if you don't: Domain validation.
5. Confirm it's working
The first real impression flips your placement to active in the dashboard. See Verification, or Troubleshooting for the common server-side gotchas (missing Referer, missing IP, test origin in dev).
Reference
- Serve endpoint: full parameter and response reference.
- Impression pixel: dedup window, caching rules.
- Click tracking.
- Domain validation: Referer rule, test-origin blocklist, server-side header samples.