Browse the help center

Sites, zones and placements via API

API endpoints, curl examples and corner cases for the supply side: sites, zones and placements of every type, including copy and move, serving domains, daisy chaining and content replacement.

Reference network manager admin

The supply side of the account: a site holds zones and a zone holds placements, the ad slots that banners are linked to and whose ad tag you publish. This page covers the site, zone and placement endpoints, the fields that change how they behave, and their corner cases. Examples append $AUTH, the hash-authentication parameters (an X-Api-Key header works the same), and send parameters form-encoded unless noted — see the API overview for authentication and Entities via API for the corner cases every entity shares.

Who this is for

Network managers and publishers who build out or maintain inventory from a script, and admins. Several site and placement settings apply only when your role holds the matching permission — see the corner cases below.

Sites

MethodPathPurpose
GET/rest-api/sites.doList all accessible sites, ordered by id (filters: publishingCategories, filter[…]; offset / limit; fields; basicInfo for extra detail)
GET/rest-api/sites/{siteId}.doGet one site — every field sites/update.do writes, under the same name; a field whose console control needs a permission you lack is left out and named in fieldsHiddenByPermission, mapped to that permission’s identifier (categoryId needs Inventory Categorization, enabled or read-only). approvalStatus is the network’s approval of the site (APPROVED, PENDING, REJECTED) — a site that is not approved serves no ads
GET/rest-api/sites/ids.doFetch several sites by ID (siteIds, repeated; required — 400 without it)
GET/rest-api/sites-tree.doFull site → zone → placement tree. On a large network read it in pages with start (entries to skip, 0 or more) and limit (1 or more; anything else is a 400) — sites ordered by id, a page shorter than limit is the last. basicInfo and publishingCategories also apply
POST/rest-api/sites/update.doCreate (no id) or update (with id)
POST/rest-api/sites/{siteId}/delete.doDelete site
GET/rest-api/sites/{siteId}/zones.doZones of one site, ordered by id (filter[…], fields)

Ad-serving and tag domains:

MethodPathPurpose
POST/rest-api/sites/{siteId}/changeAdServingDomain.doChange ad-serving domain (host; omit to reset to the default)
POST/rest-api/sites/{siteId}/changeInvocationCodeDomain.doChange tag domain
GET/rest-api/adsHosts.do, /rest-api/invocationCodeHosts.doValid domain choices
curl -X POST "https://<your-network-domain>/rest-api/sites/update.do?$AUTH" \
  -d "name=Harbour Gazette" \
  -d "inventoryType=WEBSITE" \
  -d "url=https://harbour-gazette.example" \
  -d "email=publisher@harbour-gazette.example" \
  -d "createDefaultZone=true" \
  -d "active=true"

inventoryType decides what the site is: WEBSITE, identified by its url, or APP, identified by its bundle (the app bundle ID) — send the one that matches (see the first corner case below). createDefaultZone=true gives a new site its first zone in the same call. Several site settings apply only with a role permission; they are listed in the corner cases below.

All fields, with descriptions and an example request: POST /rest-api/sites/update.do.

Corner case: url belongs to WEBSITE inventory and bundle (the app bundle ID) to APP, but that pairing is a form rule, not a server rule. The API validates and stores whichever you send, for either inventory type, and nothing clears the other one. It matters downstream: an outgoing OpenRTB bid request reads bundle for any APP site and for any application placement — so a bundle left on a WEBSITE site can reach a DSP. Send only the field that matches your inventory.

Corner case: revenueShare is a percentage from 0 to 100; outside it the update is a 400 naming it and nothing is saved, as is any other field error of sites/update.do.

Corner case: createDefaultZone=true creates one zone named exactly after the site — no suffix — and no placement inside it. It is not create-only: sending it again on an update creates a second zone with the same name.

Corner case: a new site that ends up approved — its creator holds Sites Approval, or the network’s default status for new sites is approved — and whose creator holds Automatic placements creation for approved sites also gets the console’s auto-placements: a zone named <site name> Placements with one placement for every active auto-placement of the network. The answer carries only the site id, so read the site’s zones back instead of assuming the site is empty.

Corner case: GET /rest-api/sites/ids.do requires siteIds — omitting it, or sending it empty, is a 400 naming the missing parameter rather than an empty list. Always send at least one id.

Corner case: the site settings that have a console control of their own are applied only with that control’s permission enabled — adsHost (Set Ad Serving Domain (Inventory)), invocationCodeHost (Set Ad-Serving Domain (Placement)), piggybackStatus (Piggyback Pixel Tracking), privateField (Set Private), maskInventory (Manage Mask Inventory) and cookieCollectingEnabled (Site Cookie Collecting). Without it the stored value stays and the field is listed in ignoredFields as not permitted. With it, adsHost and invocationCodeHost take the same values changeAdServingDomain.do / changeInvocationCodeDomain.do do — one of the network’s domains (adsHosts.do, invocationCodeHosts.do) or blank for the system default; anything else fails with errors.adsHost / errors.invocationCodeHost and nothing is saved. Sending back the domain the site already has is always accepted.

Zones

MethodPathPurpose
GET/rest-api/zones/{zoneId}.doGet one zone — id, siteId / siteName, enableRules, enableTargeting, operationBetweenRuleTypes and the rest of what zones/update.do writes
GET/rest-api/zones/{zoneId}/placements.doPlacements of a zone (needs view access to the zone’s site: 403 otherwise, 404 for a missing or deleted zone)
POST/rest-api/zones/update.doCreate (needs siteId, no id) or update (with id)
POST/rest-api/zones/{zoneId}/delete.doDelete zone
POST/rest-api/zones/{zoneId}/copy.doCopy a zone with its placements into a site, as the console’s clipboard copy: siteId, optional name (later copies numbered 2, 3…), count, keepBannersLinked (default true). Answers newItemIds and copies (id, name, active, and placements with the new placements’ id, name, active)
curl -X POST "https://<your-network-domain>/rest-api/zones/update.do?$AUTH" \
  -d "siteId=3001" \
  -d "name=Main Content Zone" \
  -d "active=true"

A create needs siteId. A zone carries little more than its name, its on/off switch and its pre-targeting and rules switches — everything else about serving lives on the placements beneath it. All fields, with descriptions and an example request: POST /rest-api/zones/update.do.

Corner case: siteId together with id moves the zone to that site. The move needs update rights on both sites; without them the call returns 403 with {"success":false,"error":"permission_denied"} and a message naming the site, and the zone stays where it was. Every placement in a moved zone keeps its placement key, so the ad tags already published for them keep working.

Corner case: toggling a zone’s active flag through this endpoint only takes effect if your role has the zone activation permission — without it the rest of the update is applied but the status is left unchanged and active is listed in ignoredFields (see Activation, approval, status).

Placements

Each placement type has its own create-or-update endpoint (send id to update):

Placement typeEndpoint
Standard (display)POST /rest-api/placements/update/standard.do
Non-standard (pop, interstitial…)POST /rest-api/placements/update/non-standard.do
NativePOST /rest-api/placements/update/native.do
VideoPOST /rest-api/placements/update/video.do
Mobile webPOST /rest-api/placements/update/mobile.do
In-appPOST /rest-api/placements/update/app.do
Click-throughPOST /rest-api/placements/update/click_through.do
E-mailPOST /rest-api/placements/update/email.do

Warning: click_through.do is the only snake_case path in the whole API — its seven neighbours are kebab-case (non-standard.do). Copy it exactly.

Reading, copying and removing:

MethodPathPurpose
GET/rest-api/placements/all.doList all placements (id + name)
GET/rest-api/placements/summary.doPlacement summary, every placement type (filters: placementIds, siteId, zoneId, types, active, publishingCategories, labels, filter[…], offset, limit; fields)
GET/rest-api/placements/{placementId}.doOne placement: the summary fields plus every setting update/* accepts; 404 if missing, 403 without view access to its site
GET / POST/rest-api/placements/{placementId}/openrtb.doThe placement’s OpenRTB card: iabCategories, blockedIabCategories (ids or IAB codes), blockedBannerAttributes (names or OpenRTB ids) and customOpenRtbTagId; only the fields sent change, an empty one clears. Needs Open RTB
POST/rest-api/placements/{placementId}/delete.doDelete placement
POST/rest-api/placements/copy-placement.doCopy a placement: placementId, zoneId, count, keepBannersLinked, optional name (later copies numbered 2, 3…). Answers newItemIds and copies (id, name, active)
POST/rest-api/placements/labels.doAdd, replace or remove labels on many placements — see Bulk labels
POST/rest-api/placements/labels/add.doAdd labels to one placement (placementId, labels)

In both reads size is the ad unit’s title, WIDTHxHEIGHT for a placement sized without an ad unit, or - when it has none; dimensions carries the same as {width, height, adUnitId, adUnitName} (or null). A placement on inventory you see masked comes back masked (PLACEMENT_<id>), judged by your share on that placement’s own site.

What a placement answers for delivery and tags:

MethodPathPurpose
GET/rest-api/placements/{placementId}/matchedAds.doAds currently matching this placement
GET/rest-api/placements/{placementId}/banner-probability.doThe Banners Probability card: each eligible banner with the chance it is served to a simulated visitor (countryCode, browser, operatingSystem, mobileCarrier; ignore drops a dimension from the match; a banner that matches but gets no share of the rotation reads -1) — see Optimize ad rotation
GET/rest-api/placements/{placementId}/tracking-urls.doA click-through placement has no ad tag: one set of click-through, click-pixel and impression-pixel URLs per linked image or video banner — see Application, email and click-through placements
GET/rest-api/placements/{placementId}/video-compliance.doFor a video placement on a site whose video requirements are on: every linked uploaded video and whether it meets them (notServed counts the ones that stay linked but are not served) — see Check video creatives against a publisher’s spec
GET/rest-api/placements/{placementId}/serving-domains.doThe ad-serving domains servingDomain accepts (domains), the one in use (servingDomain) and whether you may change it (editable); same 404 / 403 as the single-placement read
GET/rest-api/placements/{placementId}/default-invocation-params.doThe parameters every tag of the placement carries by default (type / value pairs). POST adds one (type, value) and DELETE removes one; GET /rest-api/placements/default-invocation-param-types.do lists the types. Update access to the site is needed to change them — see Ad tags, invocation codes and the API

Daisy chaining:

MethodPathPurpose
GET/rest-api/placements/{placementId}/chaining.doRead the daisy-chain (waterfall) configuration
POST/rest-api/placements/{placementId}/chaining.doSet up daisy chaining: enabled, type, lookupDepth, rotatingEnabled / rotatingCount, viewPerEntityEnabled / viewPerEntityCount, groupByDeliveryPriority. POST …/chaining/delivery-priority-filter.do sets the countries one delivery priority of a grouped chain serves (deliveryPriority, enabled, countries) — see Set up daisy chaining and passbacks
POST/rest-api/placements/chaining/update-priority.doSet a banner’s or campaign’s waterfall priority

Content replacement:

MethodPathPurpose
GET/rest-api/placements/{placementId}/content-replacement.doThe placement’s content replacement rules (id, target, replacement), total and contentReplacementEnabled
POST/rest-api/placements/{placementId}/content-replacement/create.doAdd a content replacement rule: target (required, up to 255 characters, unique within the placement), replacement (optional, up to 65,535 bytes)
POST/rest-api/placements/{placementId}/content-replacement/{ruleId}/update.doChange a rule’s target and/or replacement; a field not sent keeps its value
POST/rest-api/placements/{placementId}/content-replacement/{ruleId}/delete.doRemove a content replacement rule

Create a standard display placement:

curl -X POST "https://<your-network-domain>/rest-api/placements/update/standard.do?$AUTH" \
  -d "zoneId=4001" \
  -d "type=SITE_PLACEMENT" \
  -d "name=Homepage Leaderboard 728x90" \
  -d "adUnitId=3" \
  -d "adPosition=ABOVE_THE_FOLD" \
  -d "active=true"

Corner case: on create, zoneId and type are both required, and type must match the endpoint (SITE_PLACEMENT for update/standard.do). Leave one out and the call is a 400 with error: invalid_placement_type and the message Pass id to update a placement, or zoneId and type to create one. A type that belongs to another endpoint is the same code with the message This operation creates SITE_PLACEMENT, not VIDEO_PLACEMENT.; sending the id of a placement of another kind, or a type that differs from the stored one, says Placement 4001 is VIDEO_PLACEMENT; this operation takes SITE_PLACEMENT. An unknown id is a 404 (placement_not_found) and an unknown zoneId on create a 404 (zone_not_found), each naming the id. The ad slot size comes primarily from adUnitId (look up IDs via GET /rest-api/ad-units.do); if you omit it, a new standard placement silently defaults to Leaderboard 728×90. Sizes can also be set directly with size.width / size.height. An adUnitId that does not exist, or is not an ad unit of the placement’s type, is a 400 and nothing is changed. On update/app.do, adCategory / operatingSystem left out keep their stored values.

Beyond zoneId, type and the size, the settings that most often change what a placement does:

  • defaultCode — the passback HTML served when no ad matches; needs Placement Default Code.
  • refreshInterval / rotateInterval — in seconds, 0 = off.
  • disableAutoLinking — switches create-time auto-linking off (see the corner case below). enableLinking is the separate hard-linkage flag — not its inverse.
  • labels + labelUpdateType — the same names and values banners and campaigns use; needs Placement Labels.
  • App placements: operatingSystem (IOS / ANDROID) and adCategory (BANNER, INTERSTITIAL, NATIVE_AD, REWARDED_VIDEO); applicationOperatingSystem / applicationAdCategory set the same values.
  • Video placements: the lists videoPlaybackMethod and videoApiFramework take OpenRTB names or ids, comma-separated; an empty value clears a list and leaving it out keeps it.
  • Native placements: the nativePreferences.* sizes are all optional, and on an update a size you do not send keeps its current value.

Many settings apply only with a role permission — see Activation, approval, status for the full list.

All fields, with descriptions and an example request, one entry per placement type: update/standard.do, update/non-standard.do, update/mobile.do, update/video.do, update/native.do, update/app.do, update/click_through.do, update/email.do.

Corner case: because the whole placement model is expanded onto every endpoint, the generated reference lists the video and app fields on all eight. They bind everywhere, but only take effect on a placement of the matching type.

Corner case — labels. A label name that does not exist yet is created only when your role holds Create Labels; without it the call is a 400 naming the missing labels (Labels X, Y not found and you don't have permission to create new labels) and nothing is saved. This applies everywhere a label is given by name: a placement’s, a campaign’s and a banner’s labels, a banner’s autoLinkLabels, placements/labels/add.do and the bulk */labels.do endpoints. placements/labels/add.do also needs Placement Labels and update rights on the placement’s site. On update/* a caller without Placement Labels has labels and labelUpdateType left unapplied and listed in ignoredFields as not permitted; so does defaultCode without Placement Default Code, while the rest of the placement is saved.

Corner case: sending an existing placement’s id with a different zoneId moves the placement to that zone, on the same or another site. The move needs update rights on both sites; without them the call returns 403 with {"success":false,"permission_denied":<id>} and a message naming the site, and the placement stays where it was. An unknown zoneId is a 404. The move is written to the action log, and the placement’s banner links stay as they were. So does its placement key (key): it is set once, when the placement is created, and a move never changes it — every ad tag already published for the placement keeps working.

Corner case: POST /rest-api/placements/chaining/update-priority.do (id, bannerId or campaignId, priority) reorders the placement’s daisy chain and changes nothing else on it; the answer’s priority is the one stored (a value past the end of the chain moves the entry last). Neither bannerId nor campaignId is a 400, an entry that is not in the chain a 404. It needs the daisy-chaining permission and update rights on the placement’s site.

Corner case — serving domain. servingDomain must be one of the domains GET /rest-api/placements/{placementId}/serving-domains.do lists — the ones the console’s Domain picker offers — on every update/* endpoint. Anything else is a 400 naming the placement’s domains, and nothing is saved. Without Set Ad-Serving Domain (Placement) the field is not checked or applied; it is listed in ignoredFields as not permitted.

Corner case: creating an e-mail placement through update/email.do needs the Email Placements permission, and an e-mail placement must keep an ad unit (adUnitId).

Corner case: the four content-replacement operations need the Placement Content Replacement permission (a 403 naming it otherwise) plus view (list) or update (changes) rights on the placement’s site. The rules only apply while the placement’s contentReplacementEnabled is true, and reach live traffic on the ad server’s next cache reload.

Corner case: auto-linking runs on create only, never on update, and disableAutoLinking=true switches it off. Leaving it off is not a promise of links, though: the creative must be size- and type-compatible, and the banner (or its campaign) must have new-placement auto-linking enabled. Its categories and its labels are independent filters (see Auto-linking): with categories set, the parent site must carry one of them; with labels set, the placement must carry one of them; with both set, both must hold; and a banner or campaign with labels but no categories links to every new placement that carries one of its labels, whatever the site’s categories (banners of private advertisers are skipped on that route). With neither set nothing links, and on a network that does not use publisher categories only the label-only banners ever auto-link. Read the links back rather than assuming.

Corner case: POST /rest-api/placements/copy-placement.do with a placementId or zoneId that does not exist, or that was deleted, is refused with a 4xx that names the missing entity and copies nothing. count outside 1–20 is a 400 naming the limit.

Related pages

Please disable Adblock
Are you using adblock? Disable it and reload the page to proceed to the destination page.

A registration has already been submitted from this page. Please try again in 10 minutes.