PAYATHON 2026

Azure Billing API 调用返回 404 Error,如何解决?

技术老齐

结论

Subscription ID 拼写错误通常不是问题所在。更常见的情况是,GitHub 示例使用了已经过期的 Billing API 路径或 API 版本,或者接口不适用于当前订阅的计费类型和资源范围。

不要直接复制旧示例中的请求地址。建议使用 Azure Resource Manager 下的 Cost Management API,并逐项确认:

  • Azure 环境和 ARM 地址是否正确;
  • API 路径与 api-version 是否仍受支持;
  • 订阅是否属于当前登录的租户;
  • 调用方是否有权读取成本数据;
  • 订阅的计费类型是否支持该接口;
  • Microsoft.CostManagement 或 Microsoft.Consumption 资源提供程序是否已注册。

为什么会返回 404

Azure Billing、Consumption 和 Cost Management 是几组不同的 API。旧示例可能调用的是历史版本的 Usage API,而目前常用的接口位于以下命名空间:

Microsoft.CostManagement
Microsoft.Consumption
Microsoft.Billing

请求路径、资源范围或 API 版本不匹配时,Azure Resource Manager 通常会返回 404 Not Found。常见响应码包括:

InvalidResourceType
ResourceNotFound
NoRegisteredProviderFound

有些 Azure 接口还会在调用方无权查看目标资源时返回 404,以免暴露资源是否存在。因此,遇到 404 时也要检查权限。

推荐的排查顺序

1. 记录完整请求和响应

不要只看 HTTP 状态码,至少应记录:

  • 完整请求 URL,但要隐藏访问令牌;
  • HTTP 方法;
  • api-version;
  • 响应正文;
  • x-ms-request-id;
  • x-ms-correlation-request-id。

例如,成本查询接口要求使用 POST,不能使用 GET:

POST https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2023-03-01

接口路径使用了旧域名、遗漏 /providers/...,或者 HTTP 方法不正确,都可能导致 404。

2023-03-01 是 Cost Management Query API 的一个常见版本示例。实际使用前,应根据目标 Azure 环境和官方 API 版本列表确认该版本是否可用。

2. 确认订阅属于当前租户

访问令牌中的租户必须和订阅所属租户一致。可以先通过 ARM 查询订阅:

GET https://management.azure.com/subscriptions/{subscriptionId}?api-version=2020-01-01
Authorization: Bearer {accessToken}

如果这个请求也返回 404,应重点检查:

  • 登录时使用的 Tenant ID;
  • 服务主体是否位于正确的租户;
  • Subscription ID 是否属于该租户;
  • 订阅是否已被禁用、转移或删除;
  • 是否混用了不同的 Azure 云环境。

公有云通常使用:

https://management.azure.com/

Azure 中国区等主权云使用不同的 ARM 地址和令牌受众,两者不能混用。

3. 检查资源提供程序

先查询注册状态:

az provider show \
  --namespace Microsoft.CostManagement \
  --query registrationState \
  --output tsv

az provider show \
  --namespace Microsoft.Consumption \
  --query registrationState \
  --output tsv

如果尚未注册,可以执行:

az provider register --namespace Microsoft.CostManagement
az provider register --namespace Microsoft.Consumption

注册可能需要一段时间。注册完成后,重新获取访问令牌并再次调用接口。

4. 检查权限和成本数据访问设置

调用方至少需要在目标范围内拥有读取成本数据的权限。常见角色包括:

Cost Management Reader
Reader
Billing Reader

所需角色取决于请求范围:

  • /subscriptions/{subscriptionId}:检查订阅级 RBAC;
  • /providers/Microsoft.Billing/billingAccounts/...:检查计费账户权限;
  • 管理组范围:检查管理组权限。

对于 Enterprise Agreement、Microsoft Customer Agreement、合作伙伴管理的订阅或部分间接订阅,可能还要在计费系统中启用费用查看权限。即使调用方有权读取订阅资源,也不一定能查看实际金额。

5. 确认接口与计费类型匹配

Billing Account、Billing Profile 和 Invoice Section 等范围并不是所有订阅都有。下面的路径只适用于支持相应计费层级的账户:

/providers/Microsoft.Billing/billingAccounts/{billingAccountId}

如果误把 Subscription ID 当作 Billing Account ID,通常会收到 404。

如果只是查询某个订阅的成本汇总,可以先使用订阅范围的 Cost Management Query API:

/subscriptions/{subscriptionId}/providers/Microsoft.CostManagement/query

如果需要逐条查询用量明细,则应评估 Consumption Usage Details API:

/subscriptions/{subscriptionId}/providers/Microsoft.Consumption/usageDetails

这两个接口的数据结构和使用场景不同。不能只替换路径,然后继续沿用旧的数据模型。

Cost Management Query 调用示例

以下请求用于查询指定时间范围内按天汇总的税前成本:

POST https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2023-03-01
Authorization: Bearer {accessToken}
Content-Type: application/json

{
  "type": "ActualCost",
  "timeframe": "Custom",
  "timePeriod": {
    "from": "2026-08-01T00:00:00Z",
    "to": "2026-08-29T23:59:59Z"
  },
  "dataset": {
    "granularity": "Daily",
    "aggregation": {
      "totalCost": {
        "name": "PreTaxCost",
        "function": "Sum"
      }
    }
  }
}

C# 示例:

using System.Net.Http.Headers;
using System.Text;

var subscriptionId = "<subscription-id>";
var accessToken = "<access-token>";

var url =
    $"https://management.azure.com/subscriptions/{subscriptionId}" +
    "/providers/Microsoft.CostManagement/query?api-version=2023-03-01";

var json = """
{
  "type": "ActualCost",
  "timeframe": "Custom",
  "timePeriod": {
    "from": "2026-08-01T00:00:00Z",
    "to": "2026-08-29T23:59:59Z"
  },
  "dataset": {
    "granularity": "Daily",
    "aggregation": {
      "totalCost": {
        "name": "PreTaxCost",
        "function": "Sum"
      }
    }
  }
}
""";

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", accessToken);

using var content = new StringContent(
    json,
    Encoding.UTF8,
    "application/json");

using var response = await client.PostAsync(url, content);
var responseBody = await response.Content.ReadAsStringAsync();

Console.WriteLine($"HTTP {(int)response.StatusCode} {response.StatusCode}");
Console.WriteLine(responseBody);

response.EnsureSuccessStatusCode();

获取令牌时,受众或 scope 必须对应 Azure Resource Manager。使用 Microsoft Identity 客户端库时,公有云通常请求:

https://management.azure.com/.default

如何根据错误正文定位

如果响应中出现:

NoRegisteredProviderFound

一般说明资源提供程序尚未注册,或者指定的 api-version 在当前区域或 Azure 环境中不可用。

如果出现:

ResourceNotFound

应重点检查资源范围、租户和订阅状态,同时确认是否混用了 Subscription ID、Billing Account ID 和 Billing Profile ID。

如果出现:

InvalidResourceType

通常是 API 路径中的 provider 或资源类型有误,常见原因是继续使用了旧示例中的地址。

如果响应没有明确的错误正文,应保留 x-ms-request-id 和请求时间,再通过 Azure 支持渠道查询服务端请求记录。

注意事项

  • GitHub 示例可以编译,不等于其中的 API 地址和版本仍然有效。
  • 不要用字符串替换的方式将旧 Billing API 强行改成 Cost Management API。请求方法、权限要求和响应模型都可能不同。
  • 成本数据通常不会实时生成。新订阅或刚产生的用量可能暂时没有数据,但正常情况下更可能返回空结果,而不是因为”尚无费用”返回 404。
  • 不要把服务主体密码、证书和访问令牌写入日志或提交到代码仓库。
  • 如果订阅由合作伙伴或其他组织管理,计费账户配置可能会限制成本可见性,此时需要计费管理员授权。

备注:内容仅供参考。