Integrate with the API (browser)

Fetch the ad as JSON from the user's browser, render it however you want, and fire the impression pixel when it's visible.

Use this if you need full control over how the ad looks (custom templates, design system match, animations) but you're still fetching from the browser. If you render the ad on your server, see Integrate with the API (server).

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

Call the serve endpoint from your client JavaScript. Add ?test=1 while developing to receive ads without recording impressions. Full parameter reference: Serve endpoint parameters.

const res = await fetch(
  `https://adventory.to/api/ad/serve?p=${PLACEMENT_ID}`
);
const { ad } = await res.json();

if (!ad) {
  // No-fill: hide the slot or show fallback content.
  return;
}

3. Render the ad

The response gives you everything you need to build the UI. Full shape and types: Serve response reference.

  • product_name: bold title, e.g. "Acme Analytics"
  • headline: tagline / supporting copy
  • logo: image URL (may be null)
  • cta_text: button label
  • colors.background, colors.text, colors.cta_background, colors.cta_text: brand hex colors for the container, text, and CTA button
  • placement_type: banner or card, in case you render both
<div style={{
  background: ad.colors.background,
  color: ad.colors.text,
}}>
  {ad.logo && <img src={ad.logo} alt="" />}
  <strong>{ad.product_name}</strong>
  <p>{ad.headline}</p>
  <a
    href={ad.click_url}
    target="_blank"
    rel="noopener sponsored"
    style={{
      background: ad.colors.cta_background,
      color: ad.colors.cta_text,
    }}
  >
    {ad.cta_text}
  </a>
</div>

The sponsored rel value marks the CTA as a paid placement, per the Google guidance for paid links. The attribution link in the next step uses rel="noopener" only because it's a disclosure, not a paid link.

4. Show attribution

Every paid impression must show one of ad.attribution.texts (e.g. "Ad", "Sponsored") linked to ad.attribution.url, visibly near the ad. This is a publisher requirement, not a suggestion.

<a
  href={ad.attribution.url}
  target="_blank"
  rel="noopener"
  style={{ fontSize: 10 }}
>
  {ad.attribution.texts[0]}
</a>

Full attribution rules: Attribution guidelines.

5. Fire the impression pixel

Fire ad.impression_url once the ad has been visible for at least 1 second. Use IntersectionObserver to watch the ad container, then trigger the pixel with an Image object (the universal tracking-pixel idiom: no CORS concern, fire-and-forget):

const adEl = document.querySelector("#my-ad");
let timer = null;
let fired = false;

const io = new IntersectionObserver(([entry]) => {
  if (entry.isIntersecting && !fired && timer === null) {
    timer = window.setTimeout(() => {
      new Image().src = ad.impression_url;
      fired = true;
      io.disconnect();
    }, 1000);
  } else if (!entry.isIntersecting && timer !== null) {
    clearTimeout(timer);
    timer = null;
  }
}, { threshold: 0.5 });

io.observe(adEl);

The browser sets the Referer header for you; as long as the page is on your registered domain, the impression is recorded. Pixel mechanics (1-hour dedup window, caching rules): Impression pixel reference.

6. Confirm it's working

The first real impression flips your placement to active in the dashboard. See Verification for the lifecycle, or Troubleshooting if nothing records.

Reference