启用 Billing 后 Google Maps Places API 仍返回 OVER_QUERY_LIMIT
结论
OVER_QUERY_LIMIT 不一定意味着当前页面发送了大量请求。即使 Billing Account 显示为 enabled,只要它没有关联到 API key 所属的 Google Cloud 项目,或者该项目的 Places/Maps 配额很低、已被其他 key 用完,Google Maps Platform 仍可能返回这个状态。
建议先核对以下四项:
- 当前网页实际使用哪个 API key。
- 这个 key 属于哪个 Google Cloud 项目。
- Billing Account 是否已关联到该项目,而不只是计费账号本身处于启用状态。
- 项目是否启用了所需 API,相关配额是否被设为
0或很小的值。
仅仅重新生成 key 通常解决不了问题。如果新 key 仍属于同一个计费关系错误或配额异常的项目,报错还会出现。
为什么请求很少也会超限
Billing 已启用,但没有关联到正确项目
Google Cloud 的计费关系如下:
API key → Google Cloud project → Billing Account
Billing Account 显示为 active/enabled,只能说明该计费账号可用,不能证明当前项目已经与它关联。
常见情况有:
- Billing Account 已创建,但项目没有关联它。
- Billing 关联的是项目 A,网页中的 key 却属于项目 B。
- 开发期间创建了多个同名项目,检查时选错了项目。
- 项目以前关联过 Billing,后来被解除,或者付款方式出现问题。
- 计费关系刚刚修改,配置还没有完全生效。
排查时应从网页实际加载的 API key 入手,反向确认它所属的项目,不能只查看 Billing Account 页面。
配额由项目共享
Google Maps Platform 的部分配额按项目或具体 API 统计,不只计算当前浏览器中这个 key 的请求。
即使当前页面只测试了几次,配额也可能受到以下情况影响:
- 同一项目中的其他 API key 正在消耗配额。
- key 被提交到公开仓库,或暴露在没有限制的网页中,遭到第三方调用。
- 开发服务器产生了循环请求。
- 自动化测试、其他环境或其他成员也在使用该项目。
- 每日配额曾被手动设为较低的数值。
- 短时间内的请求量超过每分钟或每秒限制。
OVER_QUERY_LIMIT 可能表示日配额不足,也可能表示触发了短时间速率限制。题目中的错误明确提到 daily request quota,因此应先检查 Billing 关联和每日配额。如果错误只是偶尔出现,再重点检查请求速率。
Places 自动补全并非提交一次才请求一次
react-places-autocomplete 会在用户输入时持续查询建议。输入一个地址可能产生多次请求,而且页面上还有 Source 和 Destination 两个输入框。
正常输入一般不会很快用完合理的配额,但以下代码问题会增加请求量:
- 程序反复修改输入值。
- 组件不断卸载和重新挂载。
onChange触发额外查询。- 没有防抖,快速输入时每个字符都会触发请求。
- 页面重复加载 Google Maps 脚本。
- React 开发环境中的生命周期或副作用写法导致重复调用。
应通过浏览器开发者工具的 Network 面板查看实际请求数,不要用点击次数估算。
建议的排查顺序
1. 确认浏览器实际加载的 API key
检查 public/index.html 或最终生成的 HTML,确保 Maps JavaScript API 只加载一次:
<script
async
defer
src="https://maps.googleapis.com/maps/api/js?key=MY_KEY_HERE&libraries=places">
</script>
检查以下内容:
key=后的值是否为预期 key。- 是否包含
libraries=places。 - 其他组件、模板或 npm 包是否再次加载了脚本。
- 部署环境是否还在使用旧构建产物或旧环境变量。
- Network 面板中的实际请求 URL 是否使用了正确的 key。
不要在公开问题、日志或截图中粘贴完整的 API key。
2. 从 API key 页面确认所属项目
在 Google Cloud Console 的 Credentials 页面找到该 key,记下当前项目的名称和 Project ID。
接下来的检查都要在这个 Project ID 下完成,包括:
- Billing
- APIs & Services
- Quotas
- Metrics
- Credentials
项目名称可能重复,因此 Project ID 更适合用来确认项目。
3. 确认项目已关联 Billing Account
进入该项目的 Billing 页面,确认页面明确显示该项目已关联到有效的 Billing Account。
注意区分以下两种状态:
Billing Account: Active
和:
Current project is linked to this Billing Account
前者并不代表后者。还要检查:
- 付款方式是否有效。
- Billing Account 是否已暂停或关闭。
- 项目是否因预算通知、组织策略或付款问题受到限制。
预算告警通常不会自动停止服务,除非另行配置了自动化措施或组织策略,所以不能只查看预算数值。
修改 Billing 关联后,配置传播可能需要一些时间。等待后可以重新加载页面测试,但不能用等待代替配置检查。
4. 启用对应 API
使用 Maps JavaScript API Places Library 的页面通常需要检查:
Maps JavaScript APIPlaces API
如果项目使用新的 Places 服务或调用其他端点,还要根据实际请求启用相应 API。Google Cloud Console 中的 API 名称和产品迁移状态可能变化,应以失败请求对应的 API 及控制台提示为准。
API 未启用时,更常见的是 REQUEST_DENIED 或特定加载错误,而不是 OVER_QUERY_LIMIT,但排查时仍应一并确认。
5. 检查 Quotas 和 Metrics
在 APIs & Services 中分别打开相关 API,查看 Quotas 和 Metrics。
重点查看:
- Requests per day
- Requests per minute
- Requests per minute per user
- 是否设置了自定义 quota override
- 当前用量是否来自意外流量
- 报错时是否出现请求量突增
如果每日配额被手动设为 0、1 或其他很小的数值,应调整到合理范围。允许设置的最大值取决于项目、API、计费状态以及 Google 当时的政策,不能假定所有项目都有相同上限。
如果配额页面显示用量很低,客户端却仍提示日配额超限,通常是因为检查了错误的项目或 API,或者网页实际使用的并不是当前检查的 key。
正确限制浏览器 API key
前端使用的 key 必然会出现在浏览器中,无法通过隐藏代码来保护。应同时设置应用限制和 API 限制。
典型配置如下:
Application restrictions:
Websites (HTTP referrers)
Allowed referrers:
http://localhost:3000/*
https://example.com/*
https://www.example.com/*
API restrictions:
Maps JavaScript API
Places API
允许列表应根据实际部署域名填写,不要直接照搬示例。
限制配置错误通常会导致 RefererNotAllowedMapError、ApiNotActivatedMapError 或 REQUEST_DENIED,而不是典型的 OVER_QUERY_LIMIT。不过,限制 key 可以避免它泄露后被他人用于消耗项目配额。
如果 key 曾被提交到公开 Git 仓库,或暴露在不受控的环境中,应按以下顺序处理:
- 创建新的浏览器 key。
- 立即添加 HTTP referrer 和 API 限制。
- 更新应用配置并重新部署。
- 确认新 key 已生效,再删除或停用旧 key。
减少自动补全请求
可以为输入添加简单的防抖,避免用户快速输入时立即更新每一次查询。具体写法取决于 react-places-autocomplete 的版本和项目结构。下面是一个通用示例:
class FindaRide extends Component {
sourceTimer = null;
handleSourceAddressChange = address => {
clearTimeout(this.sourceTimer);
this.sourceTimer = setTimeout(() => {
this.setState({
source: address,
latitude: null,
longitude: null,
errorMessage: ''
});
}, 300);
};
componentWillUnmount() {
clearTimeout(this.sourceTimer);
}
}
这种写法也会延迟受控输入框的显示。实际项目中,通常更适合分别管理“立即显示的输入值”和“用于查询的值”。是否需要自行实现防抖,还要看当前组件版本是否支持 debounce 属性。
如果支持,可以优先使用组件自带的配置:
<PlacesAutocomplete
value={this.state.source}
onChange={this.handleSourceAddressChange}
onSelect={this.handleSourceAddressSelect}
debounce={300}
>
{/* render props */}
</PlacesAutocomplete>
用户选择建议后,再用选中的结果完成后续处理。不要在每次输入变化时调用 Place Details 或 Geocoding:
handleSourceAddressSelect = async address => {
try {
const results = await geocodeByAddress(address);
const { lat, lng } = await getLatLng(results[0]);
this.setState({
source: address,
latitude: lat,
longitude: lng,
errorMessage: ''
});
} catch (error) {
this.setState({
errorMessage: error.message || 'Unable to resolve address'
});
}
};
对应组件如下:
<PlacesAutocomplete
value={this.state.source}
onChange={this.handleSourceAddressChange}
onSelect={this.handleSourceAddressSelect}
debounce={300}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
{...getInputProps({
placeholder: 'Source'
})}
/>
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => (
<div
key={suggestion.placeId}
{...getSuggestionItemProps(suggestion)}
>
{suggestion.description}
</div>
))}
</div>
)}
</PlacesAutocomplete>
使用 key={suggestion.placeId} 也比禁用 react/jsx-key 检查更稳妥。
如何判断具体是哪一类问题
可以根据报错特征缩小排查范围:
- 始终返回
OVER_QUERY_LIMIT,且明确提示 daily quota:先检查项目的 Billing 关联、每日配额,以及实际 key 所属的项目。 - 快速输入时偶尔出现,稍后自行恢复:更可能是每分钟或短时间速率限制。
- 只有部署域名失败,本地正常:检查 HTTP referrer 限制。
- 提示 API 未激活:启用 Maps JavaScript API 和请求所需的 Places API。
- 控制台用量远高于实际测试量:检查 key 是否泄露、脚本是否重复加载,以及同项目中的其他调用方。
- 控制台完全没有对应请求记录:网页可能使用了另一个项目的 key,或者线上部署的仍是旧版本。
排查的重点应放在整条配置链上:浏览器中的 key、key 所属项目、项目关联的 Billing Account,以及该项目的配额。只要其中一环不匹配,即使 Billing Account 本身显示 enabled,Places API 仍可能返回 OVER_QUERY_LIMIT。
备注:内容仅供参考。
