Skip to Content
API referenceAds APIOverview

Ads API

The Ads API creates and runs ad accounts, campaigns, ad sets and ads for the advertisers you represent.

Adzen places the ads in AI answers, on publisher pages and in community feeds, next to content that matches what the advertiser offers.

Object model

Organization organization key └── Ad account one per advertiser, advertiser keys └── Campaign flight dates, budgets └── Ad set price, surfaces └── Ad creative, landing page
ObjectWhat it holds
OrganizationYour company account. The organization key reaches every ad account in it.
Ad accountOne advertiser, its spend cap and its advertiser keys.
CampaignWhen it runs, the daily and lifetime budgets, and the parameters on every landing URL.
Ad setThe price and surfaces. Every ad in the set inherits them. Geo targets are set with their own endpoint.
AdThe creative and its landing page.

Base URL

https://api.adzen.ai/v1/ad-manager

Production accepts requests from US IP addresses. Requests from other addresses get 403.

Authentication

Send your key as a bearer token:

curl https://api.adzen.ai/v1/ad-manager/organization \ -H 'Authorization: Bearer YOUR_API_KEY'

Use the bearer form. The X-API-Key: YOUR_API_KEY header also works, but a rejected key then gets a different error body (see Errors). If a request carries both, X-API-Key is used.

There are two kinds of key:

KeyReachesIssued by
Organization keyEvery ad account in your organizationAdzen. After a rotation the old key works for 14 days. See Rotating keys.
Advertiser keyOne ad accountYou, with the organization key. See Advertiser API keys.

For key types and rotation, see Authentication.

Getting started

Build an advertiser in this order. Each step returns the new object with the state shown.

StepCallState after the call
1POST /ad_accountsstatus: active, review_status: pending. Adzen reviews the account.
2POST /campaignsstatus: paused
3POST /ad_setsstatus: active
4POST /filesThe image is stored. Optional: an ad can run without one.
5POST /adsstatus: paused, review_status: in_review. Adzen reviews the ad.
6POST /ads/{id}/activatestatus: active
7POST /campaigns/{id}/activatestatus: active

You can run every step while the account and the ad are in review. The ad serves once Adzen approves the ad account and the ad (review_status: approved on both). Each campaign, ad set and ad reports whether it can serve in effective_status, and why not in serving_issues. See Serving status.

Conventions

TopicRule
IDsA type prefix and a positive integer: org_12, acct_61, cmpn_412, adset_8271, ad_2864, file_12, key_93. Responses always use this form. On input the bare number (412) is accepted too. An id with another type’s prefix is refused.
Your own idsEvery ad account, campaign, ad set and ad takes an optional external_id. Look objects up with ?external_id=.
MoneyInteger micros of USD. $2.50 is 2500000.
TimestampsUnix seconds.
DaysInsights, statements and invoices use your organization’s time zone. A campaign’s daily budget uses the zone the campaign was created with: see Budgets.
UpdatesPOST /{resource}/{id} with only the fields you are changing.
Status changesPOST /{resource}/{id}/activate, /pause or /archive. See Status changes.

Request bodies

  • Create and update bodies are a JSON object, sent with Content-Type: application/json (or another application/*+json type). A body that is not JSON, is empty, or has no JSON Content-Type is 400 invalid_json. JSON that is not an object is 400 invalid_value with param body.
  • A field the endpoint does not take, or a read-only field (id, object, status, effective_status, serving_issues, created_time, updated_time), is 400 invalid_value. param names the field.
  • null clears an optional field that has no default. To return attribution_window_days to its default, send 30.
  • An update with {} changes nothing and returns the object as it is.
  • activate, pause and archive take no body.
  • JSON bodies are limited to 2 MiB. A larger body is 413 payload_too_large.
  • Validation stops at the first invalid field, and param names it. Geo targets are the exception: every invalid location is listed in one response.
  • A successful create returns 200 with the new object.

Status changes

  • activate, pause and archive return 200 with the updated object.
  • Repeating the current status is a no-op and returns 200.
  • Archive is final. Archiving an archived object returns 200. Activating or pausing an archived object is 409 archived.

Lists

Every list endpoint returns the same envelope:

{ "object": "list", "data": [{ "id": "cmpn_412", "object": "campaign" }], "first_id": "cmpn_412", "last_id": "cmpn_412", "has_more": false }

Every list takes these parameters:

ParameterDescription
limit1 to 500, default 100
afterAn id of the listed type. Returns the page after it.
beforeAn id of the listed type. Returns the page before it. Sending both after and before is 400 (param is before).
orderdesc (default, highest id first) or asc

Some lists take more:

ParameterListsDescription
external_id/ad_accounts, /campaigns, /ad_sets, /adsExact match on your own id
updated_afterEvery list except /ad_accounts/{id}/api_keysUnix seconds. Returns objects with updated_time at or after this time. On /files it matches created_time. See Keeping in sync.

Results are ordered by id, not by updated_time. Page with after set to the last_id of the previous page while has_more is true. A query parameter a list does not take is ignored.

Idempotency

Send an Idempotency-Key header on every POST, including POST /files. A retry with the same key and the same request within 24 hours gets the result shown in the table below.

  • The key is 1 to 255 visible characters. Anything else is 400 invalid_idempotency_key.
  • Only POST requests use it.
  • Keys are scoped to your API key.
  • “The same request” means the same method, path, If-Match header and body. JSON bodies are compared as JSON, so field order does not matter.
  • A request runs to the end even if your connection drops. Retry with the same key to get its response.
First request’s outcomeA retry with the same key and request
2xx, 400, 413, 415 or 422Gets the stored response with Idempotent-Replayed: true. Nothing runs again.
Any other status (for example 404, 409, 429 or 5xx)Runs again
Still running409 idempotency_key_in_use with Retry-After: 1
Created an API key409 secret_shown_once. See Create a key.

The same key with a different request is 409 idempotency_key_reused. A request you correct after a 400 is a different request: send it with a new key.

A POST can get 503 temporarily_unavailable with Retry-After: 5. Nothing ran. Retry with the same key.

Errors

{ "error": { "type": "invalid_request_error", "code": "invalid_value", "message": "fixed_cpm_micros must be a multiple of 1000 from 100000 ($0.10) to 1000000000 ($1,000)", "param": "fixed_cpm_micros" } }

param is null when no single field is at fault. Branch on type and code. message is text for people; do not parse it.

When the key is sent in X-API-Key, a rejection of the key itself (401, and 403 or 503 from the key check) has the body {"error": "...", "message": "..."} instead. Bearer callers always get the envelope above.

StatustypecodeWhenRetry?
400invalid_request_errorinvalid_valueA field or query parameter is out of range or of the wrong type, or a body field is unknown or read-only. param names it, like include.cities[0].No. Fix the request.
400invalid_request_errorinvalid_jsonThe body is not JSON, is empty, or has no JSON Content-Type.No
400invalid_request_errorinvalid_queryThe query string cannot be read.No
400invalid_request_errorinvalid_idempotency_keyIdempotency-Key is not 1 to 255 visible characters.No
400invalid_request_errorinvalid_imageAn uploaded file is empty, over 25 MB, or not a PNG or JPEG. See Images.No
400invalid_request_errortoo_many_rowsAn insights response would hold more than 20,000 rows.No. Narrow the request.
400invalid_request_errorscope_too_largeAn insights level holds more than 20,000 campaigns or ads.No. Ask per ad account or per campaign.
400invalid_request_errorinvalid_locationA geo target location is not recognized. See Geo targets.No
401authentication_errormissing_api_keyNo key was sent.No
401authentication_errorinvalid_api_keyThe key is unknown, revoked or expired, or it is a key for another Adzen product (UGC API, AI API).No
403permission_erroraccount_disabledThe key’s organization or ad account is disabled.No
403permission_errororganization_key_requiredAn advertiser key called an endpoint that takes only the organization key.No
403nonenoneThe request came from an IP address outside the US. The body is not the error envelope.No
404not_found_errornot_foundNo such object in your organization, or no such path. When the id came from your request body, param names the field, like campaign_id or creative.file_id.No
405invalid_request_errormethod_not_allowedThe path does not take this method.No
409conflict_errorexternal_id_conflictAnother object already uses this external_id. param is external_id.No
409conflict_errorarchivedThe object, or the parent you are adding to, is archived.No
409conflict_errorad_account_archivedThe ad account is archived. Nothing in it can be created or changed.No
409conflict_errorflight_endedYou activated a campaign whose end_time has passed. Set a later end_time first.No
409conflict_errorad_busyAnother change to the same ad has not finished.Yes, with backoff
409conflict_errormanaged_by_adzenAdzen manages this object; it can be read but not changed.No
409conflict_errororganization_inactiveYour organization is not active.No
409conflict_errorad_account_inactiveThe ad account is not active and cannot get new keys.No
409conflict_errorkey_limit_reachedThe ad account already has 20 active keys.No
409conflict_erroridempotency_key_reusedThe Idempotency-Key was used with a different request.No. Use a new key.
409conflict_erroridempotency_key_in_useThe first request with this key is still running.Yes, after Retry-After (1 second)
409conflict_errorsecret_shown_onceA retry of an API key create that already succeeded.No
413invalid_request_errorpayload_too_largeThe body is over the size limit (2 MiB for JSON).No
429rate_limit_errorrate_limit_exceededOver a rate limit.Yes, after Retry-After
500api_errorinternal_errorA temporary error.Yes, with backoff
503api_errorauthentication_unavailableThe service is briefly unavailable. The key was not rejected: keep using it.Yes, after Retry-After (1 second) when set
503api_errortemporarily_unavailableWrites are briefly unavailable. Nothing ran.Yes, after Retry-After (5 seconds)
502 or 503api_errorupstream_unavailableA temporary error. Send the same request again, with the same Idempotency-Key on a POST.Yes, with backoff
Other 5xxapi_error when the body is the envelopevariesA server or network error.Yes, with exponential backoff, after Retry-After when set

Geo targets have codes of their own. See Geo target errors.

Request IDs

Every response from the API carries a Request-Id header. A rejection of the key itself (401, and 403 or 503 from the key check) and the non-US 403 can come without one. Adzen generates ids as req_ and 32 hex characters. You can send your own Request-Id (or X-Request-Id), 1 to 128 characters of letters, digits, -, _, . or :, and the response echoes it. Quote the id when you contact Adzen about a request.

Rate limits

LimitRequests per minute
Per endpoint, per key600
Per key, all endpoints1,200
Hourly insights at ad set or ad level, or broken down by ad_set or ad, per key30
  • An endpoint is the method plus the route template. Pausing two campaigns counts twice against POST /campaigns/{id}/pause.
  • Limits count per API key. Each advertiser key has its own.
  • Each window is one clock minute and starts at second 0.
  • Requests answered 429 and idempotent replays count too.

Over a limit, the API returns 429 rate_limit_exceeded with Retry-After, the seconds until the next minute starts.

The headers on every response to an authenticated request describe the limit with the fewest requests left:

HeaderValue
X-RateLimit-LimitRequests allowed in the current minute
X-RateLimit-RemainingRequests left in it
X-RateLimit-ResetUnix seconds when it resets

See Rate limits for every product’s limits.

Endpoints

MethodPathPurpose
GET/organizationCheck the key
GET/organization/insightsMetrics for the whole organization
GET POST/ad_accountsList; create
GET POST/ad_accounts/{id}Retrieve; update
POST/ad_accounts/{id}/activate, /pause, /archiveChange status
GET POST/ad_accounts/{id}/api_keysList; create advertiser keys
POST/ad_accounts/{id}/api_keys/{key_id}/revokeRevoke an advertiser key
GET POST/campaignsList; create
GET POST/campaigns/{id}Retrieve; update
POST/campaigns/{id}/activate, /pause, /archiveChange status
GET POST/ad_setsList; create
GET POST/ad_sets/{id}Retrieve; update
POST/ad_sets/{id}/activate, /pause, /archiveChange status
GET POST PATCH/ad_sets/{id}/geo_targetsRead, replace or edit geo targets
GET POST/adsList; create
GET POST/ads/{id}Retrieve; update
POST/ads/{id}/activate, /pause, /archiveChange status
GET POST/filesList; upload an image
GET/files/{id}Retrieve an image
POST/conversionsSend conversions
GET/{object}/{id}/insightsMetrics for an ad account, campaign, ad set or ad
Last updated on