购买和释放号码
购买号码需要两次调用:先搜索某个国家的在售号码,然后下单购买你想要的号码。所有操作都需要一个具有 numbers 范围的 API 密钥,且组织中的首次购买需要完成身份验证。
搜索国家
搜索始终限定在一个国家内,因此 country_code 是必填项。可以通过 number_type、capabilities 或国内号码的 prefix 进一步缩小范围。
// The search is always country-scoped, so country_code is required.
const page = await bird.numbers.available.list({
country_code: "GB",
capabilities: ["sms", "voice"],
});
for (const candidate of page.data) {
console.log(candidate.number, candidate.number_type);
}# The search is always country-scoped, so country_code is required.
page = client.numbers.available.list(country_code="GB", capabilities=["sms", "voice"])
for candidate in page.data:
print(candidate.number, candidate.number_type)for candidate, err := range client.Numbers.Available.List(context.Background(), bird.NumbersAvailableListParams{
CountryCode: "GB",
Capabilities: []string{"sms", "voice"},
}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(candidate.Number, candidate.NumberType)
}// The search is always country-scoped, so country_code is required.
$page = $bird->numbers->available->list([
'country_code' => 'GB',
'capabilities' => ['sms', 'voice'],
]);
foreach ($page as $candidate) {
echo $candidate->getNumber(), ' ', $candidate->getNumberType(), "\n";
}curl "https://us1.platform.bird.com/v1/numbers/available?country_code=GB" \
-H "Authorization: Bearer bk_us1_..."使用与你的密钥 bk_{region}_ 前缀匹配的区域主机:https://us1.platform.bird.com 或 https://eu1.platform.bird.com。
每条结果描述了该号码的信息:
{
"data": [
{
"number": "+447700900123",
"country_code": "GB",
"number_type": "mobile",
"capabilities": ["sms", "voice"],
"pricing": {
"setup_price": { "amount": "0.00995", "currency_code": "USD" },
"recurring_price": { "amount": "0.00995", "currency_code": "USD" },
"recurring_period": { "unit": "month", "count": 1 },
"intervals_per_charge": 12
}
}
],
"next_cursor": null,
"prev_cursor": null
}每条结果还包含一个 pricing 对象,其中是你所在组织的税前价格,以账单币种计价:
setup_price在你下单购买号码时一次性收取。公布的费用为零时,其amount为"0"。无开通费时,该字段本身为null;recurring_price为null时也是如此,因为该号码无法下单。recurring_price是每recurring_period的费率。intervals_per_charge表示一次计费覆盖多少个recurring_period。税前周期性费用等于recurring_price乘以intervals_per_charge,在号码分配到你的工作区时以及每次续费时收取。对于需要等待运营商处理的订单,号码在订单完成时分配。recurring_period为一个月且intervals_per_charge: 12时,一次计费 12 个月。
API 参考文档展示了一个包含价格的示例结果。包含 pricing.recurring_price: null 的搜索结果不会提供周期性价格。请在下单前联系销售,获取该国家和号码类型的定价。
控制面板和 API 使用相同的组织价格,包括到账单币种的汇率换算。读取价格不会锁定报价:在后续扣费之前发生的资费或汇率变动可能导致金额变化。如果价格查询失败,搜索将返回 503 和 E14009。请使用指数退避重试查询;查询失败绝不代表该号码不收费。
我们自有库存会优先返回并正常分页。最后一页可能包含运营商实时提供的号码,因此刚刚列出的号码在你下单时可能已经被买走。
要在下单前确认号码仍然可用,请通过 GET /v1/numbers/available/{number} 重新读取该号码。返回 404 表示该号码当前不可购买,无论是运营商已撤回还是我们自有库存已将其预留。重新检查与搜索共用同一个 numbers_availability 请求速率限制,因此只对即将下单的候选号码做重新检查,而不是对每个结果都检查一遍。
下单购买号码
将搜索结果中的号码传给 POST /v1/numbers/orders。关于重试保护,请参阅幂等性。
const order = await bird.numbers.orders.create({ number: "+447700900201" });
// Most orders finish inside the request. One that has to wait on a carrier
// comes back without a number_id. Poll it until it is completed or failed.
if (order.status === "completed") {
console.log("allocated as", order.number_id);
} else {
console.log("still", order.status, "; poll", order.id);
}order = client.numbers.orders.create(number="+447700900201")
# Most orders finish inside the request. One that has to wait on a carrier
# comes back without a number_id. Poll it until completed or failed.
if order.status == "completed":
print("allocated as", order.number_id)
else:
print("still", order.status, "; poll", order.id)order, err := client.Numbers.Orders.Create(context.Background(), bird.NumbersOrdersCreateParams{
Number: "+447700900201",
})
if err != nil {
log.Fatal(err)
}
// An order that has to wait on a carrier comes back without a NumberId.
// Poll it until it is completed or failed.
fmt.Println(order.Status, order.Id)$order = $bird->numbers->orders->create(
(new NumbersOrderCreate())->setNumber('+447700900201'),
);
// Most orders finish inside the request. One that has to wait on a carrier
// comes back without a number_id. Poll it until it is completed or failed.
if ($order->getStatus() === 'completed') {
echo 'allocated as ', $order->getNumberId(), "\n";
} else {
echo 'still ', $order->getStatus(), '; poll ', $order->getId(), "\n";
}curl -X POST "https://us1.platform.bird.com/v1/numbers/orders" \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{"number": "+447700900123"}'大多数订单在请求内即可完成,返回 201,号码已归你所有:
{
"id": "nor_01m0da22b0e39anhzyhtw3gzdg",
"number": "+447700900123",
"country_code": "GB",
"number_type": "mobile",
"status": "completed",
"number_id": "nda_7eqywfwzxwa1za9n8wp7e1xkr8",
"failure_reason": null,
"completed_at": "2026-08-19T15:25:56.479320Z",
"created_at": "2026-08-19T15:25:56.448051Z",
"updated_at": "2026-08-19T15:25:56.479320Z"
}number_id 是后续所有操作的标识:读取号码和释放号码。
轮询未完成的订单
需要等待运营商处理的订单会返回 202,此时 number_id 仍为 null。反复读取该订单,直到 status 变为 completed 或 failed。
const order = await bird.numbers.orders.get("nor_01krdgeqcxet5s7t44vh8rt9mg");
// failure_reason says what went wrong, and only ever on a failed order.
console.log(order.status, order.failure_reason ?? "");order = client.numbers.orders.get("nor_01krdgeqcxet5s7t44vh8rt9mg")
# failure_reason says what went wrong, and only ever on a failed order.
print(order.status, order.failure_reason or "")order, err := client.Numbers.Orders.Get(context.Background(), "nor_01krdgeqcxet5s7t44vh8rt9mg")
if err != nil {
log.Fatal(err)
}
// FailureReason says what went wrong, and only ever on a failed order.
fmt.Println(order.Status)$order = $bird->numbers->orders->get('nor_01krdgeqcxet5s7t44vh8rt9mg');
// failure_reason says what went wrong, and only ever on a failed order.
echo $order->getStatus(), ' ', $order->getFailureReason() ?? '', "\n";curl "https://us1.platform.bird.com/v1/numbers/orders/nor_01m0da22b0e39anhzyhtw3gzdg" \
-H "Authorization: Bearer bk_us1_..."订单依次经过 charging、ordering 和 pending,然后最终确定。当状态为 failed 时,failure_reason 会用简明语言说明失败原因。已扣除的开通费不会退还,因此失败的订单可能仍会产生费用;如遇此情况请联系支持团队。
释放号码
释放号码会停止周期性扣费,你也将无法再使用该号码。只有 dedicated 状态的号码才能被释放,释放后的号码不会立即重新上架出售。
// Releasing stops the recurring charge and the number stops working for you.
// Only a dedicated number can be released; a shared one answers E14002.
await bird.numbers.release("nda_01krdgeqcxet5s7t44vh8rt9mg");# Releasing stops the recurring charge and the number stops working for you.
# Only a dedicated number can be released; a shared one answers E14002.
client.numbers.release("nda_01krdgeqcxet5s7t44vh8rt9mg")// Only a dedicated number can be released; a shared one answers E14002.
if err := client.Numbers.Release(context.Background(), "nda_01krdgeqcxet5s7t44vh8rt9mg"); err != nil {
log.Fatal(err)
}// Releasing stops the recurring charge and the number stops working for you.
// Only a dedicated number can be released; a shared one answers E14002.
$bird->numbers->release('nda_01krdgeqcxet5s7t44vh8rt9mg');curl -X POST "https://us1.platform.bird.com/v1/numbers/nda_7eqywfwzxwa1za9n8wp7e1xkr8/release" \
-H "Authorization: Bearer bk_us1_..."释放成功后返回 204,无响应体。如果希望保留号码直到已付费周期结束,请改用取消操作:管理你的电话号码对两种方式均有说明。
订单被拒绝时
四种拒绝原因涵盖了几乎所有购买失败的情况。
402 加 E03000 表示钱包余额不足以支付该号码。充值后重新下单。此时不会创建订单,因此不会产生扣费。
412 表示该组织尚未完成首次购买所需的身份验证。完成验证后重试。
409 加 E14000 表示在你选择期间该号码已被他人购买。重新搜索并选择另一个号码。
409 加 E14001 表示你有太多订单仍在处理中。等待其中一个完成后再下单。
后续步骤
- 号码概览介绍号码上各字段的含义。
- Numbers API 参考文档记录了所有操作和字段。
- 幂等性介绍幂等键如何确保重试下单的安全性。