Add the Pixboard rail to your site
The Pixboard rail is a slim, first-party ad column you run on your own site. Because the ads are served from your domain -- not a third-party script -- they render even for readers using an ad blocker, and you earn a share of the revenue they generate.
There are two ways in. Almost everyone should use the SDK (two lines). If your site isn't Node, the same SDK runs at the edge on your own domain. A fully manual, build-it-yourself path also exists for anyone who wants it.
What you get, in one minute
- A first-party ad rail: the browser only ever talks to your domain, so nothing is third-party for a filter list to strip.
- You earn 65% of the ad revenue your pages generate, paid out once you're owed $25 (your live earnings are always on your publisher dashboard).
- You never touch metering, fraud protection, image/video proxying, or the hover card -- the SDK does all of it.
You'll need three values from your Pixboard dashboard (pixboard.ad/publishers/keys):
your publisher id (pub), your API key, and your path prefix. The key is
server-side only -- it never goes in a browser.
The fastest path: the SDK (Node / Express)
npm i pixboard-rail
import { pixboardRail } from 'pixboard-rail';
app.use(pixboardRail({
pub: 'yoursite',
key: process.env.PIXBOARD_KEY, // server-side only -- keep it in the environment
path: 'YOUR_PATH', // your blocker-safe path prefix
}));
That mounts the whole rail -- the ad feed, the image/click/video proxy, the badge, and the
web-component script -- under /YOUR_PATH/, all same-origin and first-party.
Then on your page, paste the exact snippet from your dashboard (see the warning below):
<script src="/YOUR_PATH/<hash>.js"></script>
<px-YOUR_PATH></px-YOUR_PATH>
That's it. The component fetches the first-party feed, paints the tiles, and owns the hover card.
⚠️ Never hand-write the snippet. The script filename is hashed and the element tag is
px-<your path>-- there is no/YOUR_PATH/rail.jsand no<pixboard-rail>. Copy the snippet exactly as your dashboard (orrailSnippet()) gives it. A stale or hand-typed snippet 404s the script and the rail silently never appears. If you upgrade the package, re-paste the snippet -- the derived names can change between versions.
Not a Node site? Run the SDK at the edge (PHP, .NET, Rails, static, WordPress)
The same hardened SDK ships a framework-neutral core that runs in a Cloudflare Worker /
Deno / Vercel Edge / Netlify Edge function on your own domain. Your app is untouched --
you just route /YOUR_PATH/* to the function. The browser still only talks to your domain,
so it's fully first-party.
import { createRailCore } from 'pixboard-rail/core';
import clientSrc from 'pixboard-rail/client-src';
let core;
export default {
async fetch(request, env, ctx) {
core ??= createRailCore({ pub: env.PIXBOARD_PUB, key: env.PIXBOARD_KEY, path: env.PIXBOARD_PATH, clientSrc });
const res = await core.handle(request, { waitUntil: ctx.waitUntil.bind(ctx) });
return res ?? fetch(request); // a rail path is handled; everything else goes to your origin
},
};
Route yoursite.com/YOUR_PATH/* at the function, keep your key in the platform's secrets,
and paste the same page snippet. A static/Jamstack site works the same way -- the edge
function is the backend for the rail path, so no server is required.
Make the ads match the page (optional, recommended)
Give the rail a coarse topic for the current page -- a section or genre, not per-article keywords -- and Pixboard biases the ads toward it. It's the page, never the reader: no cookies, no profile.
<px-YOUR_PATH context="webdev,javascript,hosting"></px-YOUR_PATH>
Use a small, fixed set of topic words per section (e.g. finance, webdev, gaming).
A matching ad rises sharply; unmatched ads still rotate in, so the rail never goes empty.
Leaving context off just gives the normal rotation -- it never hurts to send it.
Placement and appearance
- Responsive. The rail hides below a viewport width you set with
breakpoint(default 400px) and doesn't fetch a feed while hidden, so a narrow phone never pays for it:<px-YOUR_PATH breakpoint="400"></px-YOUR_PATH>(breakpoint="0"always shows). - Docking to an edge. If you fix the rail to a screen edge, reserve a matching gutter so
it never covers content:
@media (min-width: 400px) { body { padding-left: 60px } }. - Theming the hover card. Set CSS custom properties on
:root-- no!importantneeded:--pxr-card-bg,--pxr-card-fg,--pxr-card-border,--pxr-card-radius,--pxr-card-shadow. (These are stable across versions.) - Labelling. The rail renders the "Ads by Pixboard" badge, but no text label of its own.
If your ad policy promises the column is labelled, add a "Sponsored" label yourself,
outside the
<px-YOUR_PATH>element (the component replaces its own contents on render, so don't nest anything inside it).
What gets counted, and how you're paid
An impression is a deliberate look -- counted only once a reader has held a tile's hover card open for about one second (once per ad per page load), never on a passing mouse-over and never just on render. A tile nobody dwells on is not a look. This is metering for attention, not raw renders.
You don't implement any of this, and you can't inflate it: every look is booked against a tamper-proof token that Pixboard issues and verifies -- the SDK only relays it. You earn 65% of the revenue your looks generate; payouts run once you're owed $25. Your live earnings and payout status are always on your publisher dashboard.
If your key is domain-bound: pass domain: 'yoursite.com' to pixboardRail(...) (or set
PIXBOARD_DOMAIN for the edge core) so your server-to-server calls are recognized and your
looks are counted. If nothing is counting and you have a bound key, this is almost always why.
Upgrading the SDK
When you bump the package version, re-paste the page snippet from your dashboard or
railSnippet(). The element tag, script filename, and CSS classes are derived per-publisher
and can change between versions -- keeping an old snippet leaves the rail silently absent with
no error. If a page ever requests an old-style path, your server logs one line telling you to
re-paste.
Advanced: the manual path (no SDK)
You can integrate without the SDK by calling the JSON API from your server, rendering the tiles yourself, and reporting looks back -- but you then own the first-party proxy, the SSRF safety, the hover card's CSS containing-block handling, and the metering. The SDK exists so you don't have to. If you want the full manual walkthrough, sign in and see pixboard.ad/publishers/implement.
Quick API reference (both calls are server-to-server, with your x-pixboard-key):
GET https://pixboard.ad/api/rail?pub=YOUR_ID&n=14&context=...-- fetch the ad list.POST https://pixboard.ad/rail/impressions-- report the looks you counted.
Quick troubleshooting
- The rail isn't there at all. Almost always a stale/hand-typed snippet -- re-paste it
exactly from your dashboard. The script is at a hashed filename, not
rail.js. - Ads show but nothing is counted. If your key is domain-bound, set the
domainoption. - The hover card is clipped. Let the SDK own the card -- it renders it outside the rail
on purpose so no ancestor's
overflow/transformcan clip it. Don't re-parent it. - Still stuck? Sign in and use the support desk, or reply to your onboarding email.