RevenueFlex GAM ReportingGoogle Ad Manager reports and inventory

API guide

Every report on the catalogue — and your network's own saved reports, ad units, placements, teams, apps and MCM child publishers — can be worked with by a program. Authenticate with an API key you created on your account page; the key stands for you — all the Google accounts you connected and the networks they see — and can do only what its capabilities allow, on the network it is pinned to or the one a call names.

Authentication

Send the key in a header on every request — either form works:

X-Api-Key: rfx_…
Authorization: Bearer rfx_…

Keys are shown once, at creation. Each has a name and an expiry you choose — at most one year, or never (such a key stays valid until you revoke it); revoke a key from the account page at any time. A key cannot create or revoke keys, choose the network or delete the account — those stay on the page.

Capabilities

When you create a key you tick the capabilities it holds. A call needing a capability the key lacks answers 403 {"error":"this API key lacks the capability …"}. Keys created before capabilities existed hold reports only.

reportsrun and download the catalogue reports
saved-reportslist and run the saved reports of the chosen network
inventory:readread ad units, placements, teams and mobile apps of the chosen network
inventory:writecreate ad units and placements, change their status, teams and placement membership
mcm:readlist the network's MCM child publishers
mcm:writeinvite child publishers, resend or renew invitations, end agreements

Which network

You may connect several Google accounts side by side; each brings the Ad Manager networks it can see. GET /api/gam/accounts lists the accounts with their networks, GET /api/gam/networks the networks with the account each comes through. There is no default network. Every call names the network it works on:

  1. a network field in the JSON body, a ?network= query parameter, or an X-Network header (the network code, e.g. network=12345678);
  2. else the network the key is pinned to (chosen when the key was created; a pinned key that names another network gets 403 network_pinned);
  3. else, only when your connected accounts see exactly one network, that one — otherwise 400 network_required, whose message lists the codes to choose from.

A named network must be one of the listed ones — 404 network_unknown otherwise. The service picks the connected Google account that sees it; you never say which account to use.

GET /api/gam/download?report=network-daily&preset=yesterday&network=12345678
POST /api/gam/inventory/ad-units      {"network":"12345678","name":"…","sizes":["320x50"]}

Reports on several networks at once. Report calls (/api/gam/jobs and /api/gam/download) also accept network=all — every network your connected accounts see (a pinned key's "all" is its own network) — or a comma-separated list, network=12345678,23456789. The report is submitted once per network and the answer is a batch (below); a network that could not be started is listed under notSubmitted with its reason and the rest go ahead.

Everything a key does happens in your network with your Ad Manager rights — a call Ad Manager would refuse for your Google account is refused here too (502 with Ad Manager's reason). Every write is recorded in your change log (GET /api/gam/actions, and on the account page), whether it succeeded or failed.

One call: download a report

Submits the report, waits for Ad Manager to build it (up to about three minutes) and streams the file. The simplest way for a script when the report is quick.

GET /api/gam/download?report=ad-unit-daily&preset=yesterday&format=csv
GET /api/gam/download?report=country-daily&from=2026-08-01&to=2026-08-31&format=zip
curl -H "X-Api-Key: rfx_…" -o ad-units.csv \
  "https://appmanager.revenueflex.com/api/gam/download?report=ad-unit-daily&preset=yesterday"
reporta catalogue id (GET /api/gam/catalog lists them)
presettoday, yesterday, last7, last30, thisMonth, lastMonth — or give from and to as YYYY-MM-DD, inclusive, at most 92 days (7 for hourly reports)
formatcsv (default, text/csv) or zip (application/zip, one CSV inside)

Should Ad Manager take longer than the wait, the answer is 504 still_running with a JSON body naming the ticket (or batch) URL; the report keeps running and can be fetched through it for 24 hours. The CSV is Ad Manager's own CSV_DUMP export: a header row of column names, then one row per line; revenue columns are in micros of the network's currency.

Three calls: submit, poll, download

For larger reports, or to avoid a long-lived connection. POST /api/gam/jobs answers at once with a ticket: a handle you probe until Ad Manager has built the report, then download. Tickets are stored on the server, survive its restarts, and stay fetchable for 24 hours after submission; at most 10 may be open (submitted and not yet downloaded) per account.

POST /api/gam/jobs            {"report":"ad-unit-daily","from":"2026-08-01","to":"2026-08-31","format":"zip","network":"12345678"}
  → {"id":"…","state":"IN_PROGRESS","networkCode":"12345678","statusUrl":"/api/gam/jobs/…","downloadUrl":"/api/gam/jobs/…/download?format=zip"}
GET  /api/gam/jobs/{id}       → {"state":"IN_PROGRESS" | "COMPLETED" | "FAILED", "error":"…" when failed, …}
GET  /api/gam/jobs/{id}/download?format=zip           (409 in_progress while still running)

With network=all or a list, the answer is a batch of tickets — one per network — with its own handle:

POST /api/gam/jobs            {"report":"ad-unit-daily","preset":"yesterday","format":"zip","network":"all"}
  → {"batch":"…","state":"IN_PROGRESS","reports":[{"id":"…","networkCode":"12345678","state":"IN_PROGRESS","statusUrl":"…","downloadUrl":"…"}, …],
     "notSubmitted":[{"networkCode":"…","error":"…","code":"…"}],          (only when some network could not start)
     "statusUrl":"/api/gam/batches/…","downloadUrl":"/api/gam/batches/…/download?format=zip"}
GET  /api/gam/batches/{id}    → the same shape; "state" is IN_PROGRESS while any report is, COMPLETED when at least one can be fetched, else FAILED
GET  /api/gam/batches/{id}/download?format=zip|csv

A batch download is one ZIP with a CSV per network (named by network code, plus errors.txt when a network failed), or — format=csv — one CSV with the header once and a leading network_code column. The single tickets of a batch can also be fetched one by one. GET /api/gam/download with network=all waits for the whole batch and streams the same bundle.

Saved reports — capability saved-reports

The reports you saved in Ad Manager's own Reports page. They run exactly as saved — with their date range replaced when you give one.

GET /api/gam/saved
  → {"networkCode":"…","saved":[{"id":14356061544,"name":"…","compatible":true,"dateRangeType":"LAST_WEEK","dimensions":[…],"columns":[…]}, …]}
GET /api/gam/download?saved=14356061544                              (the saved date range)
GET /api/gam/download?saved=14356061544&from=2026-08-01&to=2026-08-31   (that range instead)
POST /api/gam/jobs   {"savedId":14356061544,"preset":"lastMonth","format":"zip"}

A saved report whose compatible is false was built with features the API version this service uses cannot run; Ad Manager reports it that way and the call answers 409. Saved reports carry no day limit of their own; the daily download cap still applies.

Inventory — capabilities inventory:read / inventory:write

GET /api/gam/inventory/networkthe named network: name, time zone, currency, root ad unit id
GET /api/gam/inventory/ad-unitsad units; filters status (ACTIVE | INACTIVE | ARCHIVED), parentId, q (name contains), limit (≤ 500), offset; answers {"total","offset","limit","results":[…]}
GET /api/gam/inventory/ad-units/{id}one ad unit: id, name, adUnitCode, parentId, status, description, targetWindow, sizes, hasChildren, appliedTeamIds, effectiveTeamIds, lastModified
POST /api/gam/inventory/ad-units{"name":"…","sizes":["320x50","300x250"],"parentId":"…","adUnitCode":"…","description":"…","targetWindow":"TOP","teamIds":["…"]}name and at least one size are required; parentId defaults to the root ad unit; Ad Manager derives the code when none is given. Answers the created ad unit.
POST /api/gam/inventory/ad-units/{id}/status{"status":"ACTIVE" | "INACTIVE" | "ARCHIVED"}
POST /api/gam/inventory/ad-units/{id}/teams{"teamIds":["…"]} — the complete new team list (empty clears)
GET /api/gam/inventory/placementsplacements; filters status, q, limit, offset
GET /api/gam/inventory/placements/{id}one placement with its adUnitIds
POST /api/gam/inventory/placements{"name":"…","description":"…","placementCode":"…","adUnitIds":["…"]}
POST /api/gam/inventory/placements/{id}/ad-units{"add":["…"],"remove":["…"]} — membership change; answers the placement
POST /api/gam/inventory/placements/{id}/status{"status":"ACTIVE" | "INACTIVE" | "ARCHIVED"}
GET /api/gam/inventory/teamsthe network's teams (id, name, status, hasAllInventory, hasAllCompanies)
GET /api/gam/inventory/appsthe mobile apps registered in the network (id, name, stores, storeId, approvalStatus, archived)

Nothing is ever deleted: Ad Manager has no delete for ad units or placements, and neither has this API — archive instead. Status changes and membership changes are applied by Ad Manager as a whole and answer {"changed":n} or the placement.

Every change is reversible

Before each ad unit or placement change the service reads the object's state and records, with the change-log entry, exactly what would put it back. GET /api/gam/actions shows each entry with reversible and, when it can be reversed, a revertUrl:

GET  /api/gam/actions
  → [{"id":91,"network":"12345678","via":"apikey","action":"AD_UNIT_INACTIVE","target":"123","status":"OK","detail":"1 changed (was ACTIVE)",
      "at":"…","reversible":true,"revertUrl":"/api/gam/actions/91/revert"}, …]
POST /api/gam/actions/{id}/revert                       (capability inventory:write; the change's own network)
  → {"reverted":91,"action":"AD_UNIT_INACTIVE","result":{…the reversing call's answer…}}
changehow it is reversed
ad unit / placement createdarchived (Ad Manager cannot delete; the entry stays reversible until then)
status changedset back to the status it had before
teams changedthe team list it had before is applied
placement membership changedthe ad units that actually left are added back, the ones that came in are removed
MCM invitation / actionnot reversible (reversible:false)

The reversal is itself a logged change (REVERT_…, with reverts naming the original), and can be reversed in turn. A change already reversed (revertedBy) answers 409 already_reverted; a failed or non-reversible one 409 not_reversible. The reversal is made in the state of the network at that moment — if something else changed the object in between, the recorded before-state is still what is applied.

MCM child publishers — capabilities mcm:read / mcm:write

GET /api/gam/mcm/childrenthe child publishers of the named (parent) network: companyId, name, email, childNetworkCode, invitationStatus, accountStatus, approved / proposed delegation type, proposedRevenueSharePercent
POST /api/gam/mcm/children{"name":"…","email":"…","delegationType":"MANAGE_ACCOUNT" | "MANAGE_INVENTORY","revenueSharePercent":30} — invites the publisher; Ad Manager sends the e-mail. Answers the child publisher record.
POST /api/gam/mcm/children/{companyId}/action{"action":"resend"} (resend the invitation), {"action":"reinvite","delegationType":"…","revenueSharePercent":…,"email":"…"} (a new proposal), {"action":"end"} (end the agreement)

Other endpoints

GET /api/gam/catalogthe reports, presets, formats and capabilities (no authentication)
GET /api/gam/tokenis this key still good? Always 200: {"status":"expired","reason":"unknown|expired|revoked|account disabled"} when the key cannot be used, else {"status":"active","capabilities":[…],"network":…,"expiresAt":…,"expiresInSeconds":…,"name":…} — for a key that never expires expiresAt and expiresInSeconds are null and neverExpires is true. The one endpoint a bad key may call (no authentication beyond the key itself)
GET /api/gam/statusthe account behind the key or session: e-mail, enabled, connected, Google accounts, networks (count), the key in use, and creditsdownloadsToday (200 per UTC day, with resetsAt), openReports (10 waiting for download), reportRequestsThisHour (5), activeKeys (20), each as {used, limit, remaining}
GET /api/gam/mewho the key belongs to, how many Google accounts are connected and how many networks they see
GET /api/gam/networksevery network your connected Google accounts can see — code, name, timeZone, accountId, accountEmail; refreshed from Ad Manager on each call
GET /api/gam/accountsthe connected Google accounts — id, email, connected, tokenAt (when Google last issued access) and the networks each last listed — plus addUrl, the page that connects another account (connecting and disconnecting is done in the browser, with Google's consent screen)
POST /api/gam/accounts/refreshask Google for a fresh access token for every connected account, or one: {"accountId":2}. Answers {"accounts":[{"id","email","ok":true,"refreshedAt"} | {"id","email","ok":false,"error","code","reconnectUrl"…}]} — an account whose Google access is gone says google_reconnect and the account is marked not connected until it signs in again
GET /api/gam/logthe account's download log
GET /api/gam/actionsthe account's change log: every write, its target, who made it (page or key), whether it succeeded, and whether it can be reversed (above)

Limits and errors

HTTPcodemeaning
400network_requiredthe call named no network and your accounts see more than one — the message lists them
400report_unknown, range_invalid, range_too_long, bad_requesta bad parameter; the message says which
401key_unknown, key_expired, key_revoked, account_disabled, not_signed_inthe key (or session) cannot be used; GET /api/gam/token tells the same without an error
403capability_missing, network_pinned, session_onlythe key lacks the capability; a pinned key named another network; the call is for the account page only (keys, delete account)
404network_unknown, ad_unit_unknown, placement_unknown, saved_report_unknown, ticket_unknown, batch_unknown, action_unknown, account_unknownno such thing in your network / account
409google_reconnectGoogle no longer honours the account's access (revoked at myaccount.google.com/permissions, password changed, token expired) — sign in with Google again at reconnectUrl; the account is marked not connected
409google_not_connected, no_networkno connected Google account reaches the network / none sees any network
409in_progress, already_reverted, not_reversiblethe download was asked for before the report finished; the change was reversed already / cannot be
429too_many_open_reports, daily_limit, too_many_keys, too_many_requestsa limit above
502ad_manager_rejected, ad_manager_error, google_token_error, download_failedAd Manager refused or failed the call, or Google could not issue a token for a reason other than revoked access — its own reason is quoted
504still_runningthe one-call download waited its three minutes; the body names the ticket or batch URL to fetch within 24 hours
500internala fault of the service — please tell us