PAYATHON 2026

启用 server API key 和 billing 后仍出现 OVER_QUERY_LIMIT

数据小许

结论

请求虽然已经创建 server API key 并启用 billing,但示例 URL 中没有 key 参数。这类请求可能会被视为未认证请求,无法使用对应项目的计费配额。控制台显示的 10% 用量也不一定包含这些请求。

OVER_QUERY_LIMIT 也不只是指每日配额已经用完。短时间内请求过于密集,超过每秒或每分钟限制时,同样可能返回这个状态。

正确携带 API key

请使用 HTTPS,并在请求中明确传入 API key:

https://maps.googleapis.com/maps/api/geocode/xml?address=Switzerland%20Bern&components=country%3ACH&key=YOUR_API_KEY

使用 JSON 时,请求可以写成:

https://maps.googleapis.com/maps/api/geocode/json?address=Switzerland%20Bern&components=country%3ACH&key=YOUR_API_KEY

这里必须包含 key=YOUR_API_KEY。只在项目中创建 key、启用 Geocoding API 或开启 billing,不会让匿名请求自动关联到该项目。

sensor=false 是旧版接口曾要求提供的参数,现在通常不再需要。是否仍受支持,应以当前使用的 Geocoding API 版本文档为准。

为什么控制台配额与实际结果不一致

可能的原因有:

  • 请求没有携带 API key,因此没有计入预期的项目或凭据。
  • 查看了错误的项目或 API,或者控制台筛选的是其他凭据。
  • 每日配额还有剩余,但瞬时请求频率已经超限。
  • 多台服务器或多个进程共用一个 key,实际总请求量超过了单机统计。
  • key 设置了 IP 地址限制,但请求的实际出口 IP 与配置不符。
  • billing 已经开启,但没有绑定到发送请求的项目。
  • 对应项目没有启用 API,或配额被手动调低。
  • 重试逻辑没有退避,收到限流响应后立刻连续重试,进一步增加了请求量。

不同年代、账户类型和 Google Maps Platform 配置对应的配额规则可能不同,不能只根据旧的“每天 2500 次”来判断。请以项目控制台当前显示的 Geocoding API 配额为准。

建议的排查步骤

1. 确认请求确实携带 key

记录程序最终发出的 URL 或请求参数,同时在日志中隐藏完整 key:

key=AIza...REDACTED

如果请求经过代理、网关或第三方 HTTP 客户端,还要检查 key 参数是否在转发过程中被删除。

2. 检查完整错误响应

不要只读取 <status>。某些错误响应还会包含 error_message:

<GeocodeResponse>
  <status>OVER_QUERY_LIMIT</status>
  <error_message>...</error_message>
</GeocodeResponse>

error_message 不一定每次都会出现,但在有值时,通常有助于判断问题出在凭据、计费还是配额。HTTP 状态码、响应头和响应时间也应该一起记录。

3. 核对项目配置

在 Google Cloud Console 中逐项确认:

  • 当前 API key 属于哪个项目。
  • billing 是否绑定到同一个项目。
  • 该项目是否启用了 Geocoding API。
  • 配额页面显示的是否确实是 Geocoding API。
  • 是否设有每分钟、每用户或其他细分配额。
  • API key 的 application restrictions 是否与服务器出口 IP 一致。
  • API restrictions 是否允许调用 Geocoding API。

修改 key 限制后,配置可能要过一小段时间才会生效。

4. 统计实际请求频率

除了每日总量,还要按秒和按分钟统计以下信息:

timestamp
server_instance
outbound_ip
api_key_id
http_status
geocoding_status
retry_count

重点检查定时任务是否集中启动、多个并发 worker 是否同时发送请求,以及失败后是否立即重试。即使每天只有几百次调用,只要集中在几秒内发出,也可能触发限流。

5. 使用指数退避

遇到 OVER_QUERY_LIMIT 后,不要立即无限重试。可以使用带随机抖动的指数退避:

async function geocodeWithRetry(request, maxRetries = 5) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await request();

    if (response.status !== "OVER_QUERY_LIMIT") {
      return response;
    }

    if (attempt === maxRetries) {
      throw new Error("Geocoding request remained over quota");
    }

    const baseDelay = 500 * (2 ** attempt);
    const jitter = Math.floor(Math.random() * 250);
    await new Promise(resolve => setTimeout(resolve, baseDelay + jitter));
  }
}

指数退避只能缓解临时的频率限制。如果每日配额已经用完、billing 配置有误或凭据无效,持续重试并不能解决问题。

代码示例

服务器端可以显式构造查询参数,减少参数遗漏和编码错误:

const params = new URLSearchParams({
  address: "Switzerland Bern",
  components: "country:CH",
  key: process.env.GOOGLE_MAPS_API_KEY
});

const url =
  `https://maps.googleapis.com/maps/api/geocode/json?${params.toString()}`;

const response = await fetch(url);
const result = await response.json();

if (result.status !== "OK") {
  console.error({
    status: result.status,
    errorMessage: result.error_message,
    httpStatus: response.status
  });
}

不要把 API key 硬编码进源码,也不要将其提交到版本库。服务器 key 应限制可调用的 API,并根据实际部署环境设置出口 IP 限制。

最需要先修正的地方

当前示例请求是:

http://maps.googleapis.com/maps/api/geocode/xml?address=Switzerland+Bern+&components=country:CH&sensor=false

至少需要改成:

https://maps.googleapis.com/maps/api/geocode/xml?address=Switzerland%20Bern&components=country%3ACH&key=YOUR_API_KEY

如果加入 key 后仍然出现 OVER_QUERY_LIMIT,请优先检查短时间内的请求频率、共用该 key 的其他调用方、服务器的实际出口 IP,以及控制台中的细分配额。

备注:内容仅供参考。