UGC Reactions from Real Creators
Real creator library videos at $11 and images at $8. Custom production from $25/video with a $250 minimum. Commercial licensing included.
翻訳待ち
Sponsored product placements for AI apps. The @zeekend/sdk source.
翻訳待ち
Loading the full README.
Read the full README ↗Sponsored product placements for AI apps. When someone tells your assistant their pan is scratched, Zeekend can put the right pan in front of them, labeled, at the moment it helps.
The one-line way — installs the package AND finds your chat's message list and drops the component in for you, no copy/paste:
npx @zeekend/sdk init
It edits your source, so here is exactly what it touches before you run it. Two
writes, both in bin/init.cjs and both worth reading first: it inserts the
component into the one file that renders your message list, and appends your
keys to .env. Nothing else, and no network call beyond the install.
It does not show you the edit before making it. Run it on a clean git tree so
git diff tells you everything it did — or skip it entirely and use the manual
snippet below, which produces the same result. Preferring not to let a script
edit your code is a reasonable position and costs you nothing here.
Works when your chat UI renders messages as {messages.map(...)} (true for
most React chat apps, including useChat() from the Vercel AI SDK). If it
can't find that pattern with confidence, it says so and prints the manual
snippet below instead of guessing.
Or by hand:
npm i @zeekend/sdk
import { ZeekendSlot } from '@zeekend/sdk/react'
<ZeekendSlot publisherKey="pub_test" messages={messages} />
Drop it under your message list. That is the whole integration.
Using an AI coding assistant instead? Paste this:
Read https://exchange.zeekend.com/skill.md and install Zeekend in this app.
(or /skill-dashboard.md for the version that also wires up
publisher.zeekend.com reporting.)
pub_test is the sandbox key. No signup, no waiting. It fills on almost every
turn so you can see it working in your own app in about two minutes, and it bills
nobody. When you have seen it work, get a live key at
zeekend.com and change that one string.
Sandbox fill rates are not real. Live fill is closer to 5 to 10%, because most turns genuinely deserve nothing.
messages needs to beYour existing array. [{ role, content }], and the common variants
({role, text}, {from: 'user'|'bot'}, Anthropic-style content blocks). If you
use useChat() from the Vercel AI SDK, pass its messages straight in.
Everything else is derived from it: which turn this is, what the question was, whether the assistant is still streaming. There is no turn id to keep stable and no streaming flag to thread through.
Only publisherKey and messages are required.
<ZeekendSlot
publisherKey="pub_live_..."
messages={messages}
conversationId={thread.id}
relevance={0.6} // optional floor 0-1; unset, each campaign's own applies
dimensions={{ maxWidth: 640 }} // leave one axis unbounded
blockCategories={['gambling', 'crypto']}
onNoFill={reason => showBackupNetwork()}
/>
relevance is optional. Leave it out and each campaign's own cutoff applies,
which is what the exchange tunes. Set it, here or from your dashboard, only to
be stricter than that: higher means fewer, better matches and lower fill. Move
it on your own numbers, not ours.
Pacing is set from your dashboard, not here: the first turn a placement may
appear on, turns between placements, and placements per session. Unset, the
first placement can appear on turn 2. Passing minTurns, turnGap or
maxPerSession here still works, as a local ceiling: whichever of yours and the
dashboard's is stricter wins.
includePrevious (default false) also sends the user's previous message,
clipped to 500 characters, so a follow-up like "something classic" can be matched
to the watch it is about. It sends a turn the SDK otherwise never does, so turn it
on only once your privacy notice covers it.
keepPinned (default true) keeps the conversation at the bottom when the ad
appears. The ad is drawn after your app has scrolled to the end of the answer, so
without this it lands just below the fold. It only scrolls when the reader was
already at the bottom; someone who scrolled up to reread is left where they are.
Set false if your app scrolls to new content itself.
A placement arrives as a card, a catalogue, or a sponsored line: a short
labelled sentence after the answer, naming one product. The advertiser chooses
which, per campaign. serve() renders all of them.
A sponsored line has three parts, and who owns which one is the point:
| the lead-in | your words, in your assistant's voice |
| the claim | the advertiser's words, all of them inside the link |
| the label | "Sponsored", after the claim rather than before it |
The lead-in defaults to "You might also want to look at". It is the one string here worth writing yourself, because it is the only part in your voice — and an advertiser buying the words that introduce their own ad is exactly what the split exists to prevent.
zk.serve({ mount, messages, inlineLeadIn: 'Worth a look:' })
<script src="https://exchange.zeekend.com/z.js"
data-key="pub_live_..."
data-lead-in="Worth a look:"></script>
Rendering slots yourself? A sponsored line arrives as slot.format === 'inline'
with the sentence in slot.inline. headline and body still ride along, so
you can render a card instead without a second request.
This is the beginning of the README. Read the rest at the original. Read the full README ↗