---
title: "Set up ad account billing (monthly invoicing)"
method: POST
path: "/v1/ads/accounts/billing"
tags: ["Ad Accounts"]
---

# Set up ad account billing (monthly invoicing)

`POST /v1/ads/accounts/billing`

## Request

- Base URL: `https://zernio.com/api`
- URL: `https://zernio.com/api/v1/ads/accounts/billing`
- Auth: HTTP bearer

Bills a Google Ads account to a monthly invoicing payments account of a manager above it, and proposes its account budget, so the account can serve without anyone opening Google Ads.

Monthly invoicing only. Google's API cannot add a card or other payment method, so `paymentsAccountId` must be one of the manager's monthly invoicing (consolidated billing) payments accounts listed by GET /v1/ads/accounts/billing. A manager without monthly invoicing has none, and billing has to be added in Google Ads instead.

Creates a billing setup (Google's BillingSetupService) that starts now on that payments account, then an account budget proposal that starts now and never ends: unlimited by default, or capped at `spendingLimit` in the account currency. Google reviews the budget offline, usually within an hour; until then the setup reads `APPROVED_HELD` and ads do not serve. A customer whose current billing setup already has a budget is refused with 409, and so is one billed to another payments account: this call never replaces existing billing. A current setup on the same payments account without a budget (for example after a budget rejection) is reused, so resending the same request finishes it.

Not idempotent and never retried: after a 502 with details.creationStatus=unknown, read GET /v1/ads/accounts/billing before retrying. Google has no dry run for billing setups, so `validateOnly: true` checks the payments account and the existing billing without creating anything (and asks Google to validate the budget when a setup would be reused).

## Request body

- object
  - `accountId` string, required: Google ads SocialAccount id.
  - `adAccountId` string, required: Google Ads customer id to bill, digits only.
  - `paymentsAccountId` string, required: Monthly invoicing payments account from GET /v1/ads/accounts/billing.
  - `spendingLimit` number: Account budget cap in the account currency. Omit for an unlimited budget.
  - `budgetName` string
  - `purchaseOrderNumber` string: Printed on the invoice. No effect on delivery.
  - `validateOnly` boolean

## Response `200`

validateOnly: checks passed, nothing created. billingSetupId is set when an existing setup would be reused.

- object
  - `customerId` string
  - `paymentsAccountId` string
  - `paymentsAccountName` string, nullable
  - `billingSetupId` string, nullable
  - `reusedBillingSetup` boolean
  - `accountBudgetProposalId` string, nullable: Always null for validateOnly.
  - `validateOnly` boolean

## Other responses

- `201`: Billing setup created (or reused) and account budget proposed.
- `400`: Invalid input, a payments account the customer cannot use (the message lists the available ones), or a Google rejection. A rejected budget after a created setup carries details.billingSetupId: resend the same request to finish.
- `401`: Missing or invalid API key. `code` is `missing_credentials` when no Authorization header was sent and `invalid_credentials` when the key is unknown, revoked or expired.
- `403`: The connection has selected ad accounts (its ad-account scope, set with the dashboard picker or GET /v1/connect/{platform}/ads with adAccountIds) and the ad account this request targets is not among them, so the connection may not act on it even though its token can reach it. Code `ad_account_not_selected`. `details` carries `accountId` (the connection), `adAccountId` (the refused ad account) and `selectedAdAccountIds`. Add the ad account to the selection (adAccountIds replaces the whole selection, so pass the ids already selected plus this one) or use a connection that has it selected. Connections without a selection keep reaching every ad account their token can manage.
- `404`: The account or requested resource was not found or is not accessible. An account ID may have been disconnected and removed. Read GET /v1/accounts for current account IDs.
- `409`: The customer already has billing: a current billing setup with a budget, or one on another payments account. Nothing is changed.
- `429`: Google Ads operations budget or quota exhausted; retry later.
- `501`: Only available on Google Ads accounts
- `502`: Google did not confirm the billing setup (details.creationStatus=unknown) or the budget proposal. Read GET /v1/ads/accounts/billing before retrying.

## Changes

- **2026-10-22** `c083c0fa652b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/ads/accounts/billing/post.md)

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) · [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/4e74db179e73?raw)
