monetzly
My knees hurt after a run
Ice them, then try Recovery Gel
↑ contextual placement · sponsored
Weaving ads into the conversation…
monetzly
ProductHow it WorksPricing
Login
Join waitlist
  1. Home
  2. Blog
  3. Adding ads to a streaming LLM response

Engineering

Adding ads to a streaming LLM response

21 July 2026 · 6 min read · Monetzly team

The integration is deliberately small: you already have a token stream, and the SDK returns a token stream. Everything else — matching, disclosure, attribution — happens behind that boundary.

This walkthrough uses Node with a streaming chat route, which is what the demo on this site runs. The shape is the same in any runtime that can iterate an async stream.

1. Install and configure

npm install @monetzly/server-sdk

Configure once at module scope, not per request — the client holds a connection you want to reuse across invocations.

import { MonetzlySDK, MonetzlyConfig } from "@monetzly/server-sdk";

const config: MonetzlyConfig = {
  apiKey: process.env.MONETZLY_API_KEY!,
  serverAddress: process.env.MONETZLY_SERVER_ADDRESS!,
  useSSL: true,
  timeout: 30000,
};

const sdk = new MonetzlySDK(config);
Server-side only: the API key must never reach the browser. Keep this in a route handler, an edge function or your backend — never in client code.

2. Wrap the stream

Call connect() before the first injection, then pass your model's token stream through inject(). What comes back is an async generator of the same chunk shape, so your existing forwarding loop keeps working.

await sdk.connect();

const llmStream = await llm.stream(messages);

for await (const chunk of sdk.inject(llmStream, {
  sessionId,
  metadata: { app: "my-assistant" },
})) {
  const text = chunk.text ?? chunk.content ?? "";
  if (text) controller.enqueue(encoder.encode(text));
}
sessionId ties impressions and clicks back to one conversation; pass a stable id for the session, not a per-request uuid.

If no campaign matches the conversation — the common case — the chunks come through unchanged and the user sees exactly what your model produced.

3. Keep the failure path boring

Monetization must never be able to take down the product. Two rules cover almost all of it.

  1. Treat the ad layer as optional: on any error or timeout, fall back to streaming your original response. A missed impression is a rounding error; a failed conversation is a churned user.
  2. Keep the timeout tight relative to your own response latency, so a slow match can never become the reason the first token is late.
let stream: AsyncIterable<{ text?: string; content?: string }> = llmStream;
try {
  await sdk.connect();
  stream = sdk.inject(llmStream, { sessionId });
} catch (err) {
  console.error("monetzly unavailable, streaming unmodified response", err);
}

for await (const chunk of stream) {
  /* forward as before */
}

4. Verify before you ship

  • Run a conversation with obvious commercial intent and confirm a labelled sponsored mention appears.
  • Run a conversation with none and confirm the response is byte-identical to the unwrapped version.
  • Kill network access to the ad service and confirm responses still stream.
  • Check that impressions show up against the session id you passed.
Full quickstartConfiguration reference, environment variables and the verification checklist.Framework recipesThe same integration for LangChain, the Vercel AI SDK, FastAPI and others.

Monetize the conversations you already have

Wrap your existing response stream with the Monetzly SDK and earn on sessions that were never going to convert to a subscription.

Join the waitlistRead the docs

Related

Docs
How-to recipes
Live demo
Fill rate is a feature, not a target
Advertising inside AI chat without losing trust
monetzly

Monetization for AI-native apps.

PRODUCT
OverviewHow it WorksUse CasesPricingFor Advertisers
RESOURCES
DocsGuidesFree ToolsChangelogStatus
COMPANY
AboutBlogContact
SOCIAL
Twitter / XLinkedIn
© 2026 Monetzly, Inc. All rights reserved.