Microsoft Ads REST API: GetBillingDocumentsInfo Returns Empty Results
Hi, I think I found a bug or an undocumented permissions issue when I try to query for billing-docs via the microsoft-ads rest api. below is a claude generated summary walking through the EXACT curl commands I used and their various responses. As a precaution I've redacted the environment variables but I can give you the exact ones if it matters (I imagine a dev on you guys' side-of-the-fence could easily cross-reference the "Information deleted by moderator" username and impute the dev-token, etc.)
edit: somewhat painful to redact a lot of the IDs by hand, hopefully I get to hear back from you guys on this bug/permissions hiccup. My guess is that my immediate user/api-token is getting hung up asking for billing-docs belong to other people's "accounts" that don't immediately belong to "me" despite being a "Super Admin" in this situation.
Microsoft Ads REST API: GetBillingDocumentsInfo Returns Empty Results
Summary
The GetBillingDocumentsInfo REST API endpoint returns an empty array {"BillingDocumentsInfo":[]} despite valid authentication, Super Admin permissions, and an active account with billing documents visible in the Microsoft Ads UI.
Environment
- API Version: v13
- Endpoint:
https://clientcenter.api.bingads.microsoft.com/CustomerBilling/v13/BillingDocumentsInfo/Query - Date: November 5, 2025
- Account Region: Brazil (BRL currency)
- Account Timezone: Brasilia
- Customer ID: 12....34
- Account ID: 24.....64
Reproduction Steps
Step 1: Set Environment Variables
export BING_ADS_ACCESS_TOKEN='eyJhb...redacted...9V1l0Us'
export BING_ADS_DEV_TOKEN='119...redacted..19'
export BING_ADS_CUSTOMER_ID='12.....34'
Step 2: Verify User Has Super Admin Permissions ✅
API Call:
curl -X POST "https://clientcenter.api.bingads.microsoft.com/CustomerManagement/v13/User/Query" \
-H "Authorization: Bearer $BING_ADS_ACCESS_TOKEN" \
-H "DeveloperToken: $BING_ADS_DEV_TOKEN" \
-H "CustomerId: $BING_ADS_CUSTOMER_ID" \
-H "Content-Type: application/json" \
-d '{"UserId": null}'
Response (formatted):
{
"User": {
"ContactInfo": {
"Email":
"Information deleted by moderator"
},
"CustomerId": "25.....9",
"Id": "13.....47",
"Name": {
"FirstName": "Placements.io",
"LastName": "Administrator"
},
"UserLifeCycleStatus": "Active",
"UserName":
"Information deleted by moderator"
},
"CustomerRoles": [
{
"RoleId": 41,
"CustomerId": "2...redated...9",
"AccountIds": null
},
{
"RoleId": 41,
"CustomerId": "12....4",
"AccountIds": null
},
{
"RoleId": 41,
"CustomerId": "2.....1",
"AccountIds": null
}
]
}
Key Findings:
- ✅ User ID:
13....47 - ✅ RoleId:
41(Super Admin) - ✅ Has Super Admin access to CustomerId
12........34 - ✅
AccountIds: nullindicates access to all accounts under this customer
Step 3: Verify Account Exists and Is Active ✅
API Call:
curl -X POST "https://clientcenter.api.bingads.microsoft.com/CustomerManagement/v13/AccountsInfo/Query" \
-H "Authorization: Bearer $BING_ADS_ACCESS_TOKEN" \
-H "DeveloperToken: $BING_ADS_DEV_TOKEN" \
-H "CustomerId: $BING_ADS_CUSTOMER_ID" \
-H "Content-Type: application/json" \
-d '{"CustomerId": 12......34, "OnlyParentAccounts": false}'
Response (formatted):
{
"AccountsInfo": [
{
"Id": "24.....64",
"Name": "REDACTED",
"Number": "X7......05",
"AccountLifeCycleStatus": "Active",
"PauseReason": null
},
... REDACTED....
]
}
Key Findings:
- ✅ Account ID
24.....64exists - ✅ Account status is
Active - ✅ Account number:
X7.....05 - 5 other accounts are paused
Step 4: Verify Account Details and BillToCustomerId ✅
API Call:
curl -X POST "https://clientcenter.api.bingads.microsoft.com/CustomerManagement/v13/Account/Query" \
-H "Authorization: Bearer $BING_ADS_ACCESS_TOKEN" \
-H "DeveloperToken: $BING_ADS_DEV_TOKEN" \
-H "Content-Type: application/json" \
-d '{"AccountId": 24.....4}'
Response (formatted):
{
"Account": {
"BillToCustomerId": "12......4",
"CurrencyCode": "BRL",
"AccountFinancialStatus": "ClearFinancialStatus",
"Id": "24.....64",
"Language": "Portuguese",
"LastModifiedTime": "2025-10-08T17:50:48.843",
"Name": "REDACTED",
"Number": "X7......5",
"ParentCustomerId": "12......4",
"PaymentMethodId": "41.......9",
"AccountLifeCycleStatus": "Active",
"TimeZone": "Brasilia",
"PauseReason": null,
"TaxInformation": [
{
"Key": "CNPJ",
"Value": "72......0"
}
],
"BusinessAddress": {
....REDACTED...
},
"AutoTagType": "Preserve",
"SoldToPaymentInstrumentId": "41.....59"
}
}
Key Findings:
- ✅
BillToCustomerId:"12......34"(matches our CustomerId) - ✅
ParentCustomerId:"12.....34"(matches our CustomerId) - ✅
AccountLifeCycleStatus:"Active" - ✅
CurrencyCode:"BRL"(Brazilian Real) - ✅
TimeZone:"Brasilia" - ✅
CountryCode:"BR"(Brazil) - ✅
AccountFinancialStatus:"ClearFinancialStatus" - ✅ Has payment method configured (
PaymentMethodId:"41....59")
Step 5: Attempt to Retrieve Billing Documents ❌
API Call:
curl -X POST "https://clientcenter.api.bingads.microsoft.com/CustomerBilling/v13/BillingDocumentsInfo/Query" \
-H "Authorization: Bearer $BING_ADS_ACCESS_TOKEN" \
-H "DeveloperToken: $BING_ADS_DEV_TOKEN" \
-H "CustomerId: 12.....4" \
-H "Content-Type: application/json" \
-d '{
"AccountIds": [24........64],
"StartDate": "2024-01-01T00:00:00.000",
"EndDate": "2025-12-31T23:59:59.999",
"ReturnInvoiceNumber": true
}'
Response:
{"BillingDocumentsInfo":[]}
Issue:
- ❌ Returns empty array despite valid authentication and permissions
- ❌ Date range spans 2 full years (2024-2025)
- ❌ HTTP status: 200 OK (no error message)
API Documentation Comparison
The curl command matches the official Microsoft documentation requirements:
Request Structure Analysis
| Element | Documentation Requirement | Our Implementation | Match |
|---------|--------------------------|-------------------|--------|
| Endpoint URL | CustomerBilling/v13/BillingDocumentsInfo/Query | CustomerBilling/v13/BillingDocumentsInfo/Query | ✅ |
| HTTP Method | POST | POST | ✅ |
| Authorization Header | Authorization: Bearer {token} | Authorization: Bearer $BING_ADS_ACCESS_TOKEN | ✅ |
| DeveloperToken Header | DeveloperToken: {token} | DeveloperToken: $BING_ADS_DEV_TOKEN | ✅ |
| AccountIds (required) | Array of long values | [24......64] | ✅ |
| StartDate (required) | dateTime in UTC | "2024-01-01T00:00:00.000" | ✅ |
| EndDate (optional) | dateTime in UTC | "2025-12-31T23:59:59.999" | ✅ |
| ReturnInvoiceNumber (optional) | boolean | true | ✅ |
| Date Range Logic | StartDate ≤ EndDate | 2024-01-01 < 2025-12-31 | ✅ |
🚨 GOTCHA: Undocumented CustomerId Header
The request includes a header not mentioned in the official API documentation:
-H "CustomerId: 12.......34"
Important Context:
- This is an undocumented requirement for Microsoft Ads REST APIs
- Many other Microsoft Ads endpoints require this header despite not documenting it
- The API accepts the request (returns
200 OK, not400 Bad Request) - This header specifies which customer context to operate under
Why This Matters: Developers following only the official documentation would not know to include this header, potentially causing unexpected behavior or authorization issues.
Expected vs. Actual Behavior
Expected Behavior
The API should return billing document information for account 24.....64 during the specified date range (2024-2025), including document IDs, dates, amounts, and other metadata as documented.
Actual Behavior
The API returns an empty array {"BillingDocumentsInfo":[]} with HTTP status 200 OK.
Summary of Verified Facts
✅ Working Correctly
- OAuth token is valid (User/Query succeeded)
- User ID
13......47has Super Admin permissions (RoleId: 41) - User has access to CustomerId
12.....34with no account restrictions - Account ID
24.....64exists and is Active -
BillToCustomerIdis"12........34"(matches query) -
ParentCustomerIdis"12........34"(matches query) - Account has clear financial status
- Account has payment method configured
- Request structure matches official API documentation
- Authentication verified through 3 other working endpoints
❌ Not Working
-
GetBillingDocumentsInforeturns empty array - Date range spans 2 full years (2024-2025)
- Other API endpoints work correctly with same authentication
Observations
- HTTP Status: Returns
200 OK(not an authorization error like401or403) - Response Structure: Valid JSON with expected field name
BillingDocumentsInfo - Response Content: Empty array (no billing documents returned)
- Authentication: Proven working through User/Query, AccountsInfo/Query, and Account/Query
- Permissions: Super Admin with unrestricted account access
- Account Configuration: Active account with Brazilian locale (BRL currency, Brasilia timezone, BR country code)
Possible Root Causes
- Regional/Currency Limitations: The API may have incomplete support for Brazilian accounts (BRL currency, Brasilia timezone)
- Undocumented Requirements: Additional configuration or prerequisites not mentioned in documentation
- API Implementation Gap: REST API may differ from SOAP API implementation for this endpoint
- Data Access Restriction: Billing documents may require additional permissions or account settings beyond Super Admin
- Date Handling: Potential timezone conversion issue despite using ISO 8601 format
Additional Information
- OAuth Scope:
https://ads.microsoft.com/msads.manage - User Email: "Information deleted by moderator"
- User ID:
13.......47 - Customer ID:
12......34 - Account ID:
24......64 - Account Number:
X7......05 - Account Currency: BRL (Brazilian Real)
- Account Timezone: Brasilia
- Account Country: BR (Brazil)
Report Generated: November 5, 2025