Marathon Elite 2
Lightweight daily trainer with responsive foam and a nylon plate built for weekly marathon mileage.
Adsourcer Developer
Ship sponsored results without building ad UI from scratch. Prefetch on your server, render with a React component or iframe.
Markdown for agents · llms-full.txt
POST /v1/ads/recommend with your publisher API keyclick_urlAPI keys stay server-side
Never pass your publisher API key to the browser. Prefetch recommend results on your backend and forward only the ad payload to the embed.Same card rendered by the embed route. On your site, load it via iframe or import the React component directly.
Marathon Elite 2
Lightweight daily trainer with responsive foam and a nylon plate built for weekly marathon mileage.
Host an iframe pointed at /embed/ads on adsourcer.io or mirror the route in your app. Pass a base64url-encoded payload via the d query parameter.
<iframe src="https://adsourcer.io/embed/ads?d=eyJyZXF1ZXN0X2lkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAxIiwic2Vzc2lvbl9pZCI6ImRlbW8tc2Vzc2lvbiIsImFkcyI6W3siYWRfaWQiOiIwMDAwMDAwMC0wMDAwLTQwMDAtODAwMC0wMDAwMDAwMDAwMDIiLCJ0aXRsZSI6Ik1hcmF0aG9uIEVsaXRlIDIiLCJkZXNjcmlwdGlvbiI6IkxpZ2h0d2VpZ2h0IGRhaWx5IHRyYWluZXIgd2l0aCByZXNwb25zaXZlIGZvYW0gYW5kIGEgbnlsb24gcGxhdGUgYnVpbHQgZm9yIHdlZWtseSBtYXJhdGhvbiBtaWxlYWdlLiIsImNsaWNrX3VybCI6Imh0dHBzOi8vYWRzb3VyY2VyLmlvL2RvY3MvY2xpY2stdHJhY2tpbmciLCJpbWFnZV91cmwiOiIiLCJwcmljZSI6MTM5LCJjdXJyZW5jeSI6IkVVUiIsInNwb25zb3JlZCI6dHJ1ZSwiZGlzY2xvc3VyZSI6IlNwb25zb3JlZCJ9XX0" title="Sponsored recommendations" style="width:100%;min-height:220px;border:0" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox"></iframe>Or send payloads after load with postMessage:
const iframe = document.querySelector("iframe"); window.addEventListener("message", (event) => { if (event.data?.type === "adsourcer:ready") { iframe.contentWindow.postMessage( { type: "adsourcer:render", payload: recommendResponse }, "*", ); } if (event.data?.type === "adsourcer:impression") { // POST to your server → Adsourcer /v1/events/impression fetch("/api/ads/impression", { method: "POST", body: JSON.stringify(event.data), }); }});Import the embed components directly when you control the React tree. Same payload shape as the iframe — copy from publisher integration.
import { AdsourcerAdList } from "@/components/embed"; export function AdSlot({ payload }: { payload: EmbedPayload }) { return ( <AdsourcerAdList payload={payload} onImpression={({ requestId, adId, sessionId }) => { fetch("/api/ads/impression", { method: "POST", body: JSON.stringify({ request_id: requestId, ad_id: adId, session_id: sessionId }), }); }} /> );}import { Adsourcer } from "@adsourcer/sdk"; export async function POST(req: Request) { const { query, sessionId } = await req.json(); const client = new Adsourcer({ apiKey: process.env.ADSOURCER_API_KEY! }); const result = await client.ads.recommend({ query, sessionId, maxResults: 1 }); if (result.decision !== "SHOW") { return Response.json({ request_id: result.request_id, ads: [] }); } return Response.json({ request_id: result.request_id, session_id: sessionId, ads: result.ads, });}The embed fires an impression when an ad is at least 50% visible. Wire onImpression or listen for adsourcer:impression postMessage events, then proxy to POST /v1/events/impression from your server.
If you skip impressions, clicks still attribute via the signed click_url redirect — but view metrics will be undercounted.