Browse the help center

Delivery limits via API

The daily/weekly/monthly/total limit grid on advertisers, campaigns and banners, reading limits back, resetting counters, bulk reads, site and placement limits, and per-action conversion limits.

Reference network manager admin

Delivery limits over REST: the grid of daily, weekly, monthly and total caps on impressions, clicks, conversions, opportunities and budget, how to read them back and reset their counters, the bulk reads, the supply-side limits of sites and placements, and per-action conversion limits. Examples append $AUTH, the hash-authentication parameters (an X-Api-Key header works the same) — see the API overview for authentication.

Who this is for

Developers who script delivery control for a network — network managers, and publisher or advertiser users whose role holds the permissions named below. The calls behave the same for every role; what differs is which entities and dimensions your role may change.

Limits

Delivery limits (impressions, clicks, conversions, budget, ad opportunities — daily, weekly, monthly and total) exist on advertisers, campaigns and banners with an identical endpoint set (sites and placements: see below), and an identical 27 parameters at every level — twenty caps and seven settings:

GET    /rest-api/campaign/{campaignId}/limits.do          read
POST   /rest-api/campaign/{campaignId}/limits/set.do      create if absent, then merge the supplied fields
POST   /rest-api/campaign/{campaignId}/limits/update.do   merge the supplied fields; limits must already exist
POST   /rest-api/campaign/{campaignId}/limits/reset.do    reset named counters
DELETE /rest-api/campaign/{campaignId}/limits.do          delete the limit record outright

All fifteen operations gate on two things: UPDATE access to the entity (through campaign sharing) and the Manage Capping & Limitations permission — enabled to change anything, enabled or read-only to read. They authenticate like every other REST call: hash, session, or the X-Api-Key header.

The limit grid

Twenty of the parameters are one grid: four periods × five metrics, each a cap you can set. Every cap has a counter next to it, which a read returns but a write cannot set.

impressionsclicksconversionsopportunitiesbudget
dailydailyImpressionsLimitdailyClicksLimitdailyConversionsLimitdailyOpportunitiesLimitdailyBudgetLimit
weeklyweeklyImpressionsLimitweeklyClicksLimitweeklyConversionsLimitweeklyOpportunitiesLimitweeklyBudgetLimit
monthlymonthlyImpressionsLimitmonthlyClicksLimitmonthlyConversionsLimitmonthlyOpportunitiesLimitmonthlyBudgetLimit
totaltotalImpressionsLimittotalClicksLimittotalConversionsLimittotalOpportunitiesLimittotalBudgetLimit

Counts are whole numbers capped at 4,000,000,000; budget is a decimal capped at 9,999,999,999.999. Across every cell, 0 and “omitted” both mean no cap — the serving check is limit > 0 && counter >= limit, so a cap of zero never stops delivery, and a cap is reached at equal to, not greater than.

Drop the Limit suffix and you have the twenty counter names — dailyImpressions, weeklyClicks, totalBudget and so on. Daily counters roll over every day, weekly on Monday (ISO week), monthly on the 1st.

Corner case: the counters are read-only through the API on all five entity levels (advertiser, campaign, banner, site, placement). A counter you send to set.do or update.do is not applied — it is listed in ignoredFields with the reason, and the rest of the call still saves. reset.do is the only way to change one (to zero); otherwise counters advance as delivery is recorded.

The other seven parameters set the run dates and pacing:

ParameterFormatPurpose
startDate, endDateyyyy-MM-dd-HH-mm or yyyy-MM-dd HH:mm, GMT+0The limiting period. Parsing is strict — 2026-13-01-00-00 is a 400, not a rolled-over date, and any other shape is refused naming both accepted formats
removeStartDateLimit, removeEndDateLimitbooleanClear an existing start/end date. Wins over a startDate/endDate sent in the same call
evenPacingbooleanSpread delivery through the day rather than front-loading
useLocalTimebooleanRead the startDate / endDate you send in timezoneOffset instead of GMT+0 and answer them back in it. It changes only how the run dates are entered and shown — nothing in delivery reads the flag, so it does not move when the daily counters roll over
timezoneOffsetinteger, minutes east of UTC, -840 to 840The offset the run dates are read in. Only has an effect when useLocalTime is on; the dates you send are converted back to UTC for storage, so startDate=2026-07-01-12-00 with timezoneOffset=90 is stored as 10:30

A read answers the limits with "limitsSet":true; an entity that has none answers 200 with {"campaignId":2001,"limitsSet":false} (advertiserId / bannerId on the other two) rather than an empty body, so “no limits” and “no such entity” (404) do not look alike. The read carries every cap set.do accepts with its current counter — weekly and monthly ones included — plus evenPacing and useLocalTime.

Each cap row of the console Limits card has its own permission — Daily Budget, Weekly Budget, Monthly Budget, Budget (total), and Daily / Weekly / Monthly / Total Impressions Limit, …Clicks Limit, …Conversions Limit and …Opportunities Limit for every period — and the advertiser, campaign and banner endpoints follow it the same way the console does: a read leaves out every cap and counter whose permission your role holds neither enabled nor readonly. A set.do or update.do that changes a cap without its permission enabled leaves that cap as stored and lists it in ignoredFields, its ignoredFieldReasons entry naming the permission; the rest of the request is saved. A cap sent with the value it already has is no change and needs no permission, so a client that re-sends its whole limit set keeps working. A reset.do of a counter without its permission is a 403 naming it. Caps you do hold are not held back by the ones you don’t: a role without Daily Clicks Limit can still set a daily budget.

curl -X POST "https://<your-network-domain>/rest-api/campaign/2001/limits/set.do?$AUTH" \
  -d "totalBudgetLimit=10000.00" \
  -d "dailyBudgetLimit=500.00" \
  -d "dailyImpressionsLimit=50000" \
  -d "evenPacing=true" \
  -d "startDate=2026-07-01-00-00" \
  -d "endDate=2026-07-31-23-59"

All fields, with descriptions and an example request: POST /rest-api/campaign/{campaignId}/limits/set.do — the advertiser and banner levels are listed next to it.

Corner case: neither endpoint is a full replace. Both merge: a field you leave out keeps its stored value. The only difference between them is that set.do creates the limit record when the entity has none — update.do answers 404 instead. When in doubt, use set.do.

Note: useLocalTime follows the same merge rule — leaving it out keeps the entity’s current setting rather than switching local-time resets off. With useLocalTime=true, the startDate/endDate of that request are read in timezoneOffset and stored in UTC; dates you do not send are never moved, so switching local time on, or changing a cap on a local-time limit, leaves the run dates where they were. A read answers UTC unless you pass it the same timezoneOffset: on a local-time limit the dates then come back in that offset, as the console shows them, with timezoneOffset echoed.

Corner case: a shorter period’s cap may not exceed a longer one’s — dailyClicksLimit above monthlyClicksLimit is a 400 with per-field errors — unless the longer one is 0, which means uncapped and so constrains nothing.

Warning: DELETE …/limits.do is destructive. It does not “disable” limiting: it deletes the limit record, thresholds and counters together, and a following GET reports no limits at all. There is no disable-but-keep state — re-enabling means sending every value again with set.do. To stop limiting temporarily, zero the individual caps or move the end date instead.

Corner case: the entity carries two no-conversion limits — “stop after N hours with no conversions” and “stop after N impressions with no conversions” — that the REST layer does not expose. set.do and update.do have no parameter for them, a read does not return them, and they can only be configured from the UI; their counters can be reset over the API (NO_CONVERSIONS_HOURS, NO_CONVERSIONS_IMPRESSIONS below). They also only bite together: delivery stops when both thresholds are crossed, not either. The same goes for request limits on advertisers, campaigns and banners — the reset endpoint accepts TOTAL_REQUESTS and friends there, but there is no *RequestsLimit parameter, because request limits are a site and placement cap (see below).

Reading limits back

GET …/limits.do returns the limit as stored: id, startDate, endDate, evenPacing, useLocalTime, limitsSet: true, and every cap of the grid with its counter — the weekly and monthly ones included (weeklyImpressionsLimit and weeklyImpressions, monthlyBudgetLimit and monthlyBudget, …). A cap or counter that was never set is left out of the body, and so is one whose permission your role does not hold (see above). On a limit held in local time, pass the timezoneOffset the dates were entered in to read them back in it; without it they come back in UTC. The two no-conversion thresholds are not returned, and an entity with no limits answers 200 with limitsSet: false, as above — never 204 or 404.

Resetting counters

reset.do takes a required, repeatable limitCounters parameter — there is no “reset everything” default. Twenty-nine values, matched case-sensitively:

TOTAL_IMPRESSIONS   TOTAL_CLICKS   TOTAL_CONVERSIONS   TOTAL_OPPORTUNITIES   TOTAL_REVENUE   TOTAL_REQUESTS
DAILY_IMPRESSIONS   DAILY_CLICKS   DAILY_CONVERSIONS   DAILY_OPPORTUNITIES   DAILY_REVENUE   DAILY_REQUESTS
WEEKLY_IMPRESSIONS  WEEKLY_CLICKS  WEEKLY_CONVERSIONS  WEEKLY_OPPORTUNITIES  WEEKLY_REVENUE  WEEKLY_REQUESTS
MONTHLY_IMPRESSIONS MONTHLY_CLICKS MONTHLY_CONVERSIONS MONTHLY_OPPORTUNITIES MONTHLY_REVENUE MONTHLY_REQUESTS
NO_CONVERSIONS_HOURS   NO_CONVERSIONS_IMPRESSIONS
ALL_LIMITS   ALL_ACTION_LIMITS   ALL

REVENUE is the counter behind the *Budget* parameters — the grid and the reset enum use different words for the same thing. ALL_LIMITS covers the delivery counters, ALL_ACTION_LIMITS the per-action ones, and ALL both. An unrecognised name is a 400.

curl -X POST "https://<your-network-domain>/rest-api/campaign/2001/limits/reset.do?$AUTH" \
  -d "limitCounters=DAILY_IMPRESSIONS" -d "limitCounters=DAILY_CLICKS"

Warning: a reset is not immediate. The call does not zero the counters directly; it queues a delayed reset that the ad-log flush applies, so the numbers change at the next hourly flush rather than on the response. Do not poll for the new value straight after the call — and expect ALL and ALL_LIMITS in particular to look like no-ops until the flush lands.

Bulk reads

MethodPathParameters
GET/rest-api/campaign/limits.docampaignIds (omit for every accessible campaign), startDateFrom, startDateTo (both yyyy-MM-dd-HH-mm, filtering on the limit’s own start date, inclusive), activeCampaignOnly
GET/rest-api/banners/limits.dobannerIds — required

The campaign response is an envelope: limits (present only when non-empty), withNoLimits (campaigns that matched but have no limit record), and notFound / accessForbidden when some requested IDs failed. Each is a string (a long list is truncated); the same ids also come back as arrays in notFoundIds and accessForbiddenIds. When every requested id is unknown or refused, the call is a 404 or 403 naming the first one instead. Deleted campaigns are always excluded, and the query is hard-capped at 10,000 rows with no paging and no total — split large reconciliations by ID batch.

Corner case: a request is checked against the limit as it would stand afterwards, not only against what it sends: an update.do with only a daily cap above the stored total, or only an endDate before the stored startDate, is a 400 naming the field, and nothing is saved. Only the caps and dates a request sends are judged, so an inconsistency already stored does not block an unrelated change. timezoneOffset is -840 to 840 minutes, and reset.do needs at least one limitCounters value.

The banner response is a bare JSON array with no envelope. Banners without limits are skipped silently, but a single unknown ID fails the whole call with 404, and a single banner you lack UPDATE on fails it with 403 — so one bad ID costs you the entire batch.

Site and placement limits

The Limits card of a site or placement has the same endpoint set, under /rest-api/site/{siteId}/… and /rest-api/placement/{placementId}/…:

GET    /rest-api/site/{siteId}/limits.do          read ({"siteId":…, "limitsSet":false} when none)
POST   /rest-api/site/{siteId}/limits/set.do      create if needed (the card's switch) and apply
POST   /rest-api/site/{siteId}/limits/update.do   change the fields sent (404 when there are no limits)
POST   /rest-api/site/{siteId}/limits/reset.do    limitCounters=DAILY_REQUESTS,TOTAL_CLICKS,…
DELETE /rest-api/site/{siteId}/limits.do          switch limits off

The supply side caps ad requests (daily/weekly/monthly/totalRequestsLimit), impressions, clicks and conversions, each daily, weekly, monthly and total (the counters are read back but, as on the demand side, not writable), and the same startDate/endDate, evenPacing, useLocalTime and timezoneOffset. There is no budget or opportunity cap on a site or placement — sending one is a 400.

curl -X POST "https://<your-network-domain>/rest-api/placement/3001/limits/set.do?$AUTH" \
  -d "dailyRequestsLimit=200000" \
  -d "dailyImpressionsLimit=50000"

All fields, with descriptions and an example request: POST /rest-api/placement/{placementId}/limits/set.do.

Permissions follow the console: a share on the site, Manage Site and Placement Limits to see (read-only is enough) and to save the card, Manage Capping & Limitations to create or remove the limits, and each cap’s own inventory permission (Daily Clicks Limit and its siblings on the inventory side) — a cap you may not see is left out of the read and named, with the permission it needs, in fieldsHiddenByPermission; writing or resetting one you may not change is a 403 naming the permission.

Per-action limits

Cap conversions per individual action (goal) rather than in aggregate. Unlike delivery limits, these exist on five entity types.

MethodPathParameters
GET/rest-api/per-action/limits/{type}/{entityId}.do—
POST/rest-api/per-action/add/limit/{type}/{entityId}.doactionId*, dailyConversionsLimit, totalConversionsLimit
POST/rest-api/per-action/update/limit/{type}/{entityId}.doactionId*, dailyConversionsLimit, totalConversionsLimit
POST/rest-api/per-action/delete.dotype*, entityId*, ids* (the per-action limit record IDs)
POST/rest-api/per-action/limits/{type}/{entityId}/enable.doSwitch per-action limits on (ADVERTISER, CAMPAIGN, BANNER only)
POST/rest-api/per-action/limits/{type}/{entityId}/disable.doSwitch them off — also deletes the entity’s per-action limits, as in the console

{type} is BANNER, CAMPAIGN, ADVERTISER, PLACEMENT or SITE, and it is case-sensitive — campaign is rejected with a 400 before anything else is checked. delete.do takes type, entityId and the limit ids as query parameters, not in the path like the other three; every id must be a limit of that entity, or it is a 404 naming it and nothing is deleted.

At least one of the two caps must be present; both absent is refused, as is a negative value, an unknown actionId (errors.actionId) or a daily cap above a non-zero total (judged against the cap that will be stored, so a lone dailyConversionsLimit above the stored total is refused too). As with delivery limits, 0 means uncapped.

Note: add and update both change only the cap you send; a cap you leave out keeps its stored value, or is 0 (uncapped) on a new limit.

Corner case: add is really an upsert. There can be only one limit per (entity, action) pair, so calling add again for the same actionId changes the existing record rather than failing. Conversely, update against an action that has no limit yet is a 404 (Campaign 2001 has no limit for action 10.) — use add to create one.

Corner case: the limits only cap delivery while the entity’s per-action-limits switch is on. add switches it on; update does not, so a limit updated onto an entity whose flag is off stays inert. enable.do / disable.do toggle it directly for ADVERTISER, CAMPAIGN and BANNER — disable.do also deletes the entity’s per-action limits, as the console does. Those two need Manage Capping & Limitations plus Campaign Per Action Limit and update access to the owning advertiser. The limit calls themselves need the per-action permission of the entity’s side: Campaign Per Action Limit for advertiser, campaign and banner, Inventory Per Action Limit for site and placement.

Corner case: the GET response is {"success":true,"actionLimits":[…]}, each entry carrying id, entityId and the four conversion figures — not actionId. Since delete.do needs the record id and you addressed everything else by actionId, keep the mapping between the two yourself.

Note: a permission failure on add, update or delete is a 403 carrying {"success": false, "permissionDenied": true}, as on GET.

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.