Home/AD/Documentation
Live · derived from the code

AD, technically: how it works and how to integrate

Everything a campaign needs is running: accounts, campaigns, creatives, zones, the serving engine, the panel and reports. This page is generated from the same code that serves — every limit, macro, event and route below is read from the source on each build, never typed by hand.

What it is

What AD is, and who it is for

AD is a direct ad server: no auction and no black box. A publisher sells the ad space on a site they already run; an advertiser picks the exact zones, sets the targeting and launches. One account can play either role — or both.

Advertisers

Create a campaign, add creatives, pass review, buy a placement on a zone from the marketplace and watch impressions and clicks land in reports.

Publishers

Register a site, define zones with a size, a sale model and a price, paste one tag, and keep 80% of every sale. Placing your own ads on your own zones is free.

Both at once

An advertiser account becomes a publisher the moment it registers a site; nothing is duplicated. The panel shows the tabs of each role you hold.

Access

How to get in

Sign in on forhosting.com and choose “Manage my AD” in your account menu. The panel opens with a short session — a credential that expires in minutes (never more than 60) and never leaves a permanent key in the browser. When it expires, reopen it from the same menu.

For integrations, create an API key from the panel (Profile) or with POST /tenants/:id/keys. Two scopes: tenant (full access to your own account) and read (read-only, for dashboards and bots). The key is shown once; if it is lost, create another and revoke the old one.

Our staff can open your panel “as client” to help you: that session lasts at most 15 minutes and carries the name of the person who opened it. The house never operates your account with a permanent key.

Advertisers

Campaigns and targeting

A campaign is the container: name, dates, optional budgets and the targeting shared by its creatives. It is born as a draft; you set it active, pause it, or end it. Only active creatives of an active campaign are served — pausing the campaign stops delivery at once.

CriterionHow it works
Country, region, cityA list of countries; optionally one region and one city. A city requires its region; a region requires its country. If the visitor's location is unknown and the campaign asks for geo, the ad is not served — never served by accident.
Browser languageA list of language codes (up to 30) that the visitor's browser reports — which need not be the site's language. A visitor whose language you did not list is not served, so leave one campaign with no languages: it picks up everyone else.
Deviceany, mobile or desktop.
Operating systemA list of: iPhone, iPad, iPod, Windows, Android, BlackBerry, Ubuntu, Linux, CrOs, Mac OS X.
ReferrerThe page the visitor came from must contain the text you set (case-insensitive).
DatesStart and end of the campaign. Each creative can also carry its own dates; the effective window is the intersection of both.
Frequency capPer creative: at most N impressions per visitor, counted in a first-party cookie that lives 3 days.
Hard limitsPer creative: total impressions, impressions per day and total clicks. When a limit is reached the creative stops serving within 5 minutes.

Between the eligible creatives the engine picks at random, weighted by the weight you give each one. A creative with a delivery limit is paced: every 5 minutes its weight is re-tuned so the budget spreads across the campaign's days instead of burning out in the morning. Pacing only slows down — it can never invent traffic.

A creative only serves on a zone where it has a paid order (see “Buying placements”). Campaign, creative, zone and order are all visible in the panel (Campaigns, Creatives, Buy placements).

Advertisers

Creatives: six types, one tag

Every creative has a click URL, an optional fixed size and a weight. The limits in this table are the ones the API enforces on upload — they are read from the code, not written here.

TypeWhat you uploadLimits
image · imageOne file: PNG, JPEG, GIF, WebP, AVIF.Up to 2 MB and 2000×1800 px. If the creative declares a fixed size, the file must measure exactly that.
text · text linkA title and an optional body, no file.Rendered as a link in the zone's own style.
html5 · HTML5A ZIP with index.html at the root (or inside a single folder), or one HTML file.ZIP up to 10 MB. Served in an iframe with a strict content policy: no requests to other origins.
video · videoOne file: MP4, WebM. Optional poster and sound toggle.Up to 30 MB. Plays muted with autoplay in our player; start and end are tracked.
vignette · interstitialAn image (same rules as image) or a video.Shown as a full-screen overlay when the visitor clicks a link of the zone's trigger; the impression counts when the overlay opens.
script · scriptYour own HTML/JS with the macros below, plus up to 5 images.Only on zones that allow the script format. Third-party code runs on the publisher's page, so manual review is the only barrier — it is never skipped.

The HTML5 contract

Your index.html is loaded in an iframe with the click destination in the query string as clickTag. Read it and use it as the href of your clickable area — that URL is signed and counts the click; a hand-written link does not.

// index.html — the click goes where the engine says
var clickTag = new URLSearchParams(location.search).get("clickTag");
document.getElementById("ad").href = clickTag;

If your creative needs to grow, tell the page its real height with postMessage. The tag also tells the creative the width of the slot on load and on every resize, and sends visible the first time the slot enters the viewport — the right moment to start an animation. Heights up to 10000 px are applied.

// creative → page: ask for the real height (applied up to 10000 px)
parent.postMessage({ fh: "resize", nh: document.documentElement.scrollHeight }, "*");

// page → creative: { fh: "size" | "visible" }
window.addEventListener("message", function (ev) {
  if (ev.data && ev.data.fh === "visible") { /* start your animation */ }
});

A minimal creative that does both is ready to upload as-is: download the example ZIP

Macros for script creatives

In a script creative the engine replaces these placeholders when the zone is published. A template from the panel is the same thing with extra placeholders you fill in a form.

MacroReplaced by
[CLICKTAG] · [TRACKLINK]The signed click URL — use it as href. Without it the click is not counted.
[LINK]The raw destination URL, for code that needs it without the tracker.
[TARGET]_blank or _self, as set on the creative.
[ID]The creative's id.
[TITLE] · [TITOLO]The creative's title (HTML-escaped).
[IMG0][IMG4]The URL of each uploaded image, in order.
[TIMESTAMP] · [RANDOM]A timestamp and a random number, fixed when the zone is published — for cache-busting your own pixels.

Third-party tracking and consent

Any creative can carry a tracking code (a pixel or script from a measurement vendor). It is emitted after the ad with every src turned into data-src, so nothing loads until the tag allows it.

If you set the vendor's IAB TCF v2 id, the code loads only after the visitor's consent for that vendor, with ${GDPR} and ${GDPR_CONSENT_n} filled in. The tag waits up to 10 seconds for the site's consent manager; without a vendor id the code loads as a plain element.

Manual review

Every creative is born en_revision and is reviewed by a person before it can serve. Approved, you set it active or paused; rejected, you see the reason and can edit and resubmit. Nothing from an unreviewed creative — not the markup, not the script, not the tracking code — ever reaches a visitor.

Changing the click URL, the content or the file of an approved creative sends it back to review: what was approved is what gets served, never something else.

Advertisers

Buying placements

The marketplace lists every zone on sale: site, size, formats accepted, sale model and the price the publisher set. You choose a zone, a creative of a format the zone accepts, a budget and a start date. The quote and the charge use the same formula:

ModelYou pay perYou get
cpmthousand impressionsimpressions = budget × 1000 / price
cpcclickclicks = budget / price
cpddaydays = budget / price

The minimum order is $5; the quote refuses anything below. An order is a prepaid purchase of volume — the money moves once, when you buy. Send an idempotencyKey and a retried request returns the same order instead of a second one.

How you pay

WayHow it works
Account balanceThe order is paid in the same call from your For Hosting balance. If the balance is short the order stays pending and the response links to top up; you can retry the payment later.
ManualThe order is created pending; our staff marks it paid after receiving the payment outside the panel. It does not serve until then.
House adsYour own creative on your own zone: the order is born paid at zero cost. Same record, no money.

When an order is marked paid, 80% of its final price is credited to the publisher of the zone — on the whole order, not prorated by delivery. The zone is republished immediately and your creative starts serving in the next minute.

Publishers

Sites, zones, tag and payouts

Sites

Register a site by domain (Become a publisher). It is born pending and a person verifies it before its zones can sell — an unverified domain cannot earn a share. Retiring a site starts a 90-day cooldown on the domain: nobody else can register it meanwhile and inherit its history.

Zones

A zone is the sellable slot: a name, a size in pixels (or -1 for responsive width), the formats it accepts, a sale model with its price, and whether it is on sale in the marketplace. You can set a fallback creative of your own that serves when nothing else is eligible — it skips targeting and caps.

Two optional behaviours run in the visitor's browser: auto-refresh (a new request every N seconds, minimum 5; a hidden tab never refreshes, and a slot that comes back empty keeps the previous ad) and parameter forwarding (the page's query string travels with the click to the advertiser's destination). An interstitial zone also declares which links trigger the overlay — by default p a, nav a, h2 a — and the seconds before it can be closed.

The tag

Paste it where the ad should appear. The zone id comes from the panel (Sites & zones). The same tag serves every format the zone accepts; an interstitial zone uses the standard tag too.

Standard (one div and one script, asynchronous):

<div data-fh-ad="zon_XXXXXXXXXXXXXXXXXXXXXXXX"></div>
<script src="https://api.ad.forhosting.com/ad-tag.js" async></script>

Legacy, for CMSs that cannot run asynchronous scripts:

<script src="https://api.ad.forhosting.com/ad-serve?zone=zon_XXXXXXXXXXXXXXXXXXXXXXXX&mode=js"></script>

Text link: a URL that counts the impression and redirects to the advertiser:

https://api.ad.forhosting.com/ad-serve?zone=zon_XXXXXXXXXXXXXXXXXXXXXXXX&mode=link

The tag updates itself: its URL carries no version and never changes, so an improvement on our side reaches every site within about undefined minutes and nobody edits a template (it serves v6 today, in the x-tag-version header). It waits for your page to finish loading before requesting anything, so ads never compete with your own content. Its only marks in your page are the data-fh-ad attribute and the overlay id — checked against the common ad-blocking lists, with zero matches. Frequency caps use a first-party cookie.

Payouts

Your 80% share of each paid order accrues in the panel (Payouts). When the accrued amount reaches $10, request the payout with the method from your profile (paypal, bank, other); our staff pays outside the panel and records the reference.

States: accruedrequestedprocessingpaid; a failed disbursement goes back to you with the reason so you can request it again with corrected details.

Everyone

Reports

Impressions, clicks and video start/end are counted at the edge, on every request. Before they count, traffic is filtered: known crawlers by user agent, datacenter networks, requests with a very low bot score, and any IP repeating the same request within 2 seconds. A filtered request still gets its ad or its redirect — only the counter is protected.

Every 5 minutes the counts are rolled up into daily rows per creative, zone and referrer host. The current day can lag by up to that much; closed days never change.

The panel (Reports) shows totals and a daily series, CTR (clicks ÷ impressions × 100) and eCPM (value delivered × 1000 ÷ impressions), for the advertiser side or the publisher side, and a top list by creative, zone, campaign, site or referrer host. Only the host of the referrer is stored, never the URL.

Integrate

API reference

Base URL https://api.ad.forhosting.com. Send your key as a Bearer token; bodies and responses are JSON. Every answer has the shape {"success":true,"data":…} or {"success":false,"error":{"code","message"}} with the matching HTTP status.

curl https://api.ad.forhosting.com/me \
  -H "Authorization: Bearer ads_ten_…"

Credential scopes

ScopeWhat it can do
sessionWhat the panel uses: your own account, full access, expires in minutes. Issued by the portal when you open the panel.
tenantYour own account, full access, permanent. For your integrations.
readYour own account, read only. For dashboards and bots that must not change anything.
systemThe house: any account (with an explicit tenantId), reviews, site verification, manual payments and payouts. Staff use a system session that also expires.

A tenant, read or session credential always operates its own account — a tenantId sent by the client is ignored. An id that belongs to someone else returns 404, not 403: the API never confirms that it exists.

Routes

Every route the service announces, with the scope the router requires — derived from the router itself on each build.

MethodRouteScope
GET/public
GET/ad-servepublic
GET/ad-clickpublic
GET/ad-video-eventpublic
GET/ad-a/*public
GET/ad-p/*public
GET/ad-preview/*public
GET/ad-tag.jspublic
POST/tenantssystem
GET/tenantssystem
GET/tenants/:idany
PATCH/tenants/:idwrite
POST/tenants/:id/sessionssystem
POST/sessions/staffsystem
DELETE/sessions/selfany
DELETE/sessions/:idsystem
GET/meany
POST/tenants/:id/keyswrite / system
GET/tenants/:id/keysread / system
DELETE/tenants/:id/keys/:keyIdwrite / system
GET/me/payout-profileread
PUT/me/payout-profilewrite
POST/campaignswrite
GET/campaignsread
GET/campaigns/:idread
PATCH/campaigns/:idwrite
DELETE/campaigns/:idwrite
POST/campaigns/:id/duplicatewrite
POST/creativeswrite
GET/creativesread
GET/creatives/:idread
PATCH/creatives/:idwrite
DELETE/creatives/:idwrite
PUT/creatives/:id/assetwrite
POST/creatives/:id/duplicatewrite
POST/creatives/bulkwrite
GET/moderation/queuesystem
GET/moderation/preview-url/:idany
POST/creatives/:id/approvesystem
POST/creatives/:id/rejectsystem
POST/creatives/:id/emergency-blocksystem
POST/siteswrite
GET/sitesread
GET/sites/pendingsystem
GET/sites/:idread
PATCH/sites/:idwrite
DELETE/sites/:idwrite
POST/zoneswrite
GET/zonesread
GET/zones/:idread
PATCH/zones/:idwrite
DELETE/zones/:idwrite
GET/zones/:id/tagread
GET/zones/:id/quoteany
POST/zones/:id/publishsystem
GET/marketplaceany
POST/checkoutwrite
GET/ordersread
GET/orders/:idread
GET/orders/pendingsystem
POST/orders/:id/paywrite
POST/orders/:id/mark-paidsystem
GET/payoutsread
GET/payouts/pendingsystem
POST/payouts/:id/requestwrite
POST/payouts/:id/statussystem
POST/payouts/:id/mark-paidsystem
GET/statsread
GET/stats/topread
GET/settingsany
PUT/settingssystem
GET/templatesany
POST/templateswrite
PATCH/templates/:idwrite
DELETE/templates/:idwrite
POST/templates/:id/renderany
GET/geo/countriesany
GET/geo/regionsany

public: no credential — the serving path · any: any valid credential, on its own account · read: tenant, session or read · write: tenant or session (read is refused) · system: the house only

Errors worth knowing: 401 unauthorized (missing or expired credential), 403 forbidden (the scope cannot do this), 404 not_found, 400 bad_request with the reason in the message, 409 conflict (a state transition that is not allowed), 402 insufficient_balance when paying an order, and 503 payments_disabled if sales are paused.

Start

Open your panel

Sign in on forhosting.com and choose “Manage my AD” in your account menu. Publishers add a site and get their tag; advertisers create a campaign and buy a placement.

FAQ

A few technical questions

Can I run a real campaign today?

Yes — end to end: create the campaign and creative in the panel, pass review, buy a placement, and the tag serves it with tracking. Placements on your own sites activate instantly and free.

Where do I get the tag?

Panel → Sites & zones → get tag. Every zone has its own tag; the standard variant is one div plus one script.

Why did my creative not serve immediately?

Every creative passes a quick manual review before it can serve — that protects the sites your ad runs on. Rotation and caps also apply: a capped or paced creative deliberately skips some requests, and a changed zone takes up to a minute to refresh at the edge.