PUPPYIP / DEVELOPER DOCS
客户 API 开发文档
PuppyIP 客户 API 使用 HMAC-SHA256 签名。沙盒和正式环境使用相同路径、请求头、签名规则和响应包络;联调完成后,只需要替换 Base URL、App ID 和 API Secret。
接入步骤
- 1. 正式环境“我的 API”页面提供沙盒联调入口。进入测试环境后,登录客户中心,进入“我的 API”,创建
sbx_app_开头的 App ID,并立即保存 API Secret。 - 2. 把
PUPPYIP_API_BASE_URL、PUPPYIP_API_APP_ID和PUPPYIP_API_SECRET放进服务端环境变量,不要写进前端页面。 - 3. 先请求
GET /api/v1/account验证签名,再请求GET /api/v1/offers获取可用产品。 - 4. 创建订单或续费时,为一次业务操作生成一个
Idempotency-Key。同一次操作的超时或网络重试必须复用原 key 和完全相同的原始 body;只有发起新的业务操作时才更换 key。 - 5. 下单成功表示订单已受理。保存返回的
order_no,按响应中的poll_after_seconds查询订单详情,直到订单完成或退款。 - 6. 服务端请求建议设置稳定的
User-Agent,例如PuppyIP-Integration/1.0,避免默认脚本标识被安全策略误判。 - 7. 沙盒调通后,回到正式环境创建
live_app_开头的 App ID。上线只替换正式环境变量。复制代码示例后,只需要替换环境变量,签名函数、请求头和接口路径不需要重写。
网站接入流程
使用 API 连接您的网站与 PuppyIP,完成商品展示、购买、交付和续费。客户账户、零售价、收款、账本和客服由您的网站管理。
先建立客户与订单的对应关系
API 密钥代表您的 PuppyIP 账户,account、wallet、orders 和 proxies 返回该账户的数据。请在自己的服务端保存“本地客户 → 本地订单 → order_no → proxy_id”的对应关系,并在展示或操作前验证客户归属。不要把整个 API 账户的订单或 IP 列表直接返回给每位客户。
note 仅为订单备注,不作为客户权限或查询条件,当前响应也不返回 note。API Secret 和连接凭据不得进入公开页面或日志。
-
通过 /offers/available 查询可售商品,也可使用 /offers 获取商品列表。遍历分页,按 offer_code 更新商品;用 include=cidr_stock,line_group 获取号段库存和普通/推荐分类。
-
确认报价与库存
GET /api/v1/offers按数量和时长查询报价,您的零售价自行管理。指定号段时同时检查 cidr_stock 与 max_quantity;0 为无库存,null 为未知。查询不锁价、不预留库存。
-
收款与余额
GET /api/v1/wallet客户向您的网站付款;API 下单和续费从您的 PuppyIP 钱包扣款。两套资金记录分别核对,API 不会替您向客户收款或充值。
-
提交前保存本地订单、Idempotency-Key 和原始 JSON 字节。超时重试使用相同键、相同请求体和新签名;返回 201 后保存 order_no,不把受理当作交付完成。
-
按 poll_after_seconds 查询,遇到 429 遵循 Retry-After。只交付 delivery.available 允许读取的连接;部分交付模式下还需检查 complete 与 remaining_count。
-
保存 proxy_id,读取最新 host、port、凭据和到期时间,并更新自己的记录。回调发送已开启时,收到通知先验签再查询当前状态;否则继续轮询。API 不会自动修改客户软件的连接配置。
-
续费与失败处理
POST /api/v1/proxies/renew按本地客户归属选择 proxy_ids;逐项检查成功、失败、待确认列表。待确认项不要另起一笔续费。退款字段只说明该笔 PuppyIP 业务的退款,不代表您已向自己的客户退款。
API v1 不提供子客户注册、收款、充值、退款申请、更换 IP 或试用 IP。客户业务流程由您的应用管理。凭据修改需要单独权限,且仅在开放此操作的环境可用。
环境切换
建议把连接信息放在环境变量中。测试使用 PUPPYIP_API_BASE_URL=https://sandbox.puppyip.com 和 sbx_app_ 开头的 App ID;上线改为 PUPPYIP_API_BASE_URL=https://puppyip.com 和 live_app_ 开头的 App ID。测试和上线不需要改请求头、签名函数或接口路径。
PUPPYIP_API_BASE_URL=https://sandbox.puppyip.com
PUPPYIP_API_APP_ID=sbx_app_EXAMPLE_SANDBOX_APP_ID
PUPPYIP_API_SECRET=replace-with-your-secret
当前可用接口
| Endpoint | 用途 | 所需权限 |
|---|---|---|
GET /api/v1/account | 查询账户 | account:read |
GET /api/v1/wallet | 查询钱包余额 | wallet:read |
GET /api/v1/offers | 查询商品 | offers:read |
POST /api/v1/orders | 创建订单 | orders:write |
GET /api/v1/orders | 查询订单列表 | orders:read |
GET /api/v1/orders/{order_no} | 查询订单详情 | orders:read |
GET /api/v1/proxies | 查询 IP 列表 | proxies:read |
GET /api/v1/proxies/{proxy_id} | 查询单个 IP | proxies:read |
POST /api/v1/proxies/renew | 续费 IP | proxies:write |
GET /api/v1/locations | 查询地区 | offers:read |
GET /api/v1/locations/tree | 查询地区树 | offers:read |
GET /api/v1/tags | 查询商品标签 | offers:read |
GET /api/v1/offers/available | 查询可售商品 | offers:read |
GET /api/v1/announcements | 查询公告 | account:read |
POST /api/v1/proxies/credentials | 修改连接凭据 | proxies:credentials |
GET /api/v1/proxies/credential-operations/{operation_id} | 查询凭据修改结果 | proxies:credentials |
GET /api/v1/proxies/renewal-operations/{operation_id} | 查询异步续费 | proxies:write |
GET /api/v1/webhook | 查询回调设置 | webhooks:write |
POST /api/v1/webhook | 设置回调 | webhooks:write |
DELETE /api/v1/webhook | 停用回调 | webhooks:write |
请求头
| Header | 说明 | 示例 |
|---|---|---|
| X-API-AppId | 账号页生成的 App ID。 | live_app_EXAMPLE_LIVE_APP_ID |
| X-API-Timestamp | 绝对时间戳,必须带时区,允许 5 分钟内的时钟偏差。 | 2026-06-28T12:00:00+00:00 |
| X-API-Nonce | 每次请求唯一随机值,重复使用会被拒绝。 | nonce-empty-body |
| X-API-Signature | 用 API Secret 对 canonical string 计算出的 HMAC-SHA256 小写十六进制值。 | e3c27a2055ad38f16861790294802b9fac81e02e8d72fbeaa87299598785832d |
| Idempotency-Key | 创建订单、续费、修改凭据,以及已开放的回调配置或停用均必填。去除首尾空白后为 8–160 个字符,只允许英文字母、数字及 . _ : -;缺失或格式无效返回 HTTP 400 / idempotency_key_required。重试请复用原 key 和原始 body。订单和续费按账号重放,轮换 App ID 也不重复执行;回调设置按订阅所属的 API 密钥分别重放。订单、续费和回调设置重放已保存响应;凭据修改返回原操作的当前状态。相同 key 加不同 body 会返回冲突。 | order-demo-001 |
| User-Agent | 建议设置稳定的服务端客户端标识,避免默认脚本标识被安全策略误判。 | PuppyIP-Integration/1.0 |
| X-PuppyIP-Sandbox-Scenario | 沙盒下单可选 success、delayed、partial、rejected、final_partial、indeterminate;沙盒续费可选 completed、partial、failed、unresolved。不传保留默认模拟。正式下单拒绝非空值,正式续费忽略此请求头。 | partial |
Canonical String
签名前先把请求拆成 7 行,用换行符 \n 连接:
METHOD
host
path
canonical_query
timestamp
nonce
sha256(body)
METHOD 使用大写,例如 GET 或 POST。
host 来自 Base URL 的主机名,使用小写,不包含协议。
path 必须包含开头斜杠,例如 /api/v1/account。
canonical_query 对 query 的 key 和 value 分别 URL 解码后再 rawurlencode,按 key、value 升序排序,重复 key 不合并。没有 query 时这一行留空。
body 使用原始请求体计算 SHA-256。GET 请求没有 body 时,使用空字符串的 SHA-256。
GET /api/v1/account 固定示例
GET
puppyip.com
/api/v1/account
2026-06-28T12:00:00+00:00
nonce-empty-body
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
上面这组固定示例使用文档测试 Secret puppyip-docs-test-secret 计算出的签名是
e3c27a2055ad38f16861790294802b9fac81e02e8d72fbeaa87299598785832d。
代码示例
以下示例使用环境变量。请不要把 API Secret 写入前端页面、公开仓库或日志。
curl
curl 只负责发送请求。请先用下方 PHP 或 Node.js 签名函数,为这一次请求生成当前的 TIMESTAMP、NONCE 和 SIGNATURE;不要复制上面的固定算法校验值发送请求,也不要把 Secret 写进 shell 历史。
curl "$PUPPYIP_API_BASE_URL/api/v1/account" \
-H "X-API-AppId: $PUPPYIP_API_APP_ID" \
-H "X-API-Timestamp: $TIMESTAMP" \
-H "X-API-Nonce: $NONCE" \
-H "X-API-Signature: $SIGNATURE" \
-H "Accept: application/json" \
-H "User-Agent: PuppyIP-Integration/1.0"
PHP
$baseUrl = rtrim(getenv('PUPPYIP_API_BASE_URL') ?: 'https://sandbox.puppyip.com', '/');
$appId = getenv('PUPPYIP_API_APP_ID') ?: '';
$secret = getenv('PUPPYIP_API_SECRET') ?: '';
$method = 'GET';
$target = $baseUrl.'/api/v1/account';
$parts = parse_url($target);
$path = $parts['path'] ?? '/';
$rawQuery = $parts['query'] ?? '';
$body = '';
$timestamp = gmdate('Y-m-d\TH:i:s\Z');
$nonce = 'nce_'.bin2hex(random_bytes(16));
$host = strtolower($parts['host'] ?? '');
function canonicalQuery(string $rawQuery): string
{
if ($rawQuery === '') {
return '';
}
$pairs = array_map(function (string $pair): array {
[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
return [rawurlencode(rawurldecode($key)), rawurlencode(rawurldecode($value))];
}, explode('&', $rawQuery));
usort($pairs, fn (array $left, array $right): int => $left[0] <=> $right[0] ?: $left[1] <=> $right[1]);
return implode('&', array_map(fn (array $pair): string => $pair[0].'='.$pair[1], $pairs));
}
$canonical = implode("\n", [
$method,
$host,
$path,
canonicalQuery($rawQuery),
$timestamp,
$nonce,
hash('sha256', $body),
]);
$signature = hash_hmac('sha256', $canonical, $secret);
$headers = [
'X-API-AppId: '.$appId,
'X-API-Timestamp: '.$timestamp,
'X-API-Nonce: '.$nonce,
'X-API-Signature: '.$signature,
'Accept: application/json',
'User-Agent: PuppyIP-Integration/1.0',
];
$handle = curl_init($target);
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
]);
$response = curl_exec($handle);
if ($response === false) {
throw new RuntimeException(curl_error($handle));
}
echo $response;
Node.js
import crypto from 'node:crypto';
const baseUrl = process.env.PUPPYIP_API_BASE_URL ?? 'https://sandbox.puppyip.com';
const appId = process.env.PUPPYIP_API_APP_ID ?? '';
const secret = process.env.PUPPYIP_API_SECRET ?? '';
const method = 'GET';
const url = new URL('/api/v1/account', baseUrl);
const body = '';
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const nonce = `nce_${crypto.randomBytes(16).toString('hex')}`;
const rfc3986 = (value) => encodeURIComponent(value).replace(
/[!'()*]/g,
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
);
const compare = (left, right) => left < right ? -1 : (left > right ? 1 : 0);
const canonicalQuery = (rawQuery) => {
if (rawQuery === '') return '';
return rawQuery
.split('&')
.map((pair) => {
const separator = pair.indexOf('=');
const key = separator === -1 ? pair : pair.slice(0, separator);
const value = separator === -1 ? '' : pair.slice(separator + 1);
return [rfc3986(decodeURIComponent(key)), rfc3986(decodeURIComponent(value))];
})
.sort((left, right) => compare(left[0], right[0]) || compare(left[1], right[1]))
.map(([key, value]) => `${key}=${value}`)
.join('&');
};
const rawQuery = url.search.startsWith('?') ? url.search.slice(1) : url.search;
const canonical = [
method,
url.hostname.toLowerCase(),
url.pathname,
canonicalQuery(rawQuery),
timestamp,
nonce,
crypto.createHash('sha256').update(body).digest('hex'),
].join('\n');
const signature = crypto
.createHmac('sha256', secret)
.update(canonical)
.digest('hex');
const headers = {
'X-API-AppId': appId,
'X-API-Timestamp': timestamp,
'X-API-Nonce': nonce,
'X-API-Signature': signature,
Accept: 'application/json',
'User-Agent': 'PuppyIP-Integration/1.0',
};
const response = await fetch(url, { method, headers });
console.log(await response.json());
响应包络
所有接口都返回统一 JSON 包络。成功时 success=true,业务数据在 data;失败时 code 是稳定错误码,trace_id 可用于联系支持排查。
{
"success": true,
"code": "ok",
"message": "OK",
"data": {},
"timestamp": "2026-06-28T12:00:00+00:00",
"trace_id": "trc_EXAMPLE_TRACE_ID"
}
/api/v1/account查询账户
查看当前 API 密钥所属的账户信息。
所需权限account:read
请求参数
无需额外业务参数。请求仍需携带签名请求头。
- 每项操作仅访问当前 API 密钥所属账户。API 不开放更换 IP 或试用 IP。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/account
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.account_id | string | 公开账户标识,作为完整字符串保存,不要解析。 |
data.email | string | 账户邮箱。 |
data.email_verified | boolean | 账户邮箱是否已验证。 |
data.status | string | active(启用)或 inactive(停用)。 |
data.created_at | string|null | 创建时间,ISO 8601 UTC;无记录时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"account_id": "acct_0123456789abcdef0123456789",
"email": "[email protected]",
"email_verified": true,
"status": "active",
"created_at": "2026-11-09T00:00:00+00:00"
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/wallet查询钱包余额
查看 API 购买使用的钱包余额与币种。
所需权限wallet:read
请求参数
无需额外业务参数。请求仍需携带签名请求头。
- 核对余额时使用响应的币种与整数金额。此 API 不提供充值操作,沙盒余额没有实际资金价值。
- 金额计算使用整数最小货币单位或精确十进制运算,不要使用二进制浮点数;小数金额字段为保留两位小数的字符串。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/wallet
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.currency | string | 本响应金额所使用的币种。 |
data.balance_cents | integer | 钱包余额,整数最小货币单位,100 分等于 1 元。 |
data.balance | string | 同一钱包余额,保留两位小数的字符串。 |
data.frozen_cents | integer | 钱包冻结金额,整数最小货币单位。 |
data.cash_value | boolean | 生产钱包为 true,沙盒模拟余额为 false。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"currency": "CNY",
"balance_cents": 10000,
"balance": "100.00",
"frozen_cents": 0,
"cash_value": true
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/offers查询商品
购买前查询可售商品、价格、时长与可选号段。
所需权限offers:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
page_sizeinteger |
query | 可选 | 每页条数:1–200;使用分页时默认 200。 |
page_tokenstring |
query | 可选 | 原样传入上一页的 next_page_token。第一页不传,最长 4,096 个字符。 |
country_codestring |
query | 可选 | 匹配商品返回的 country_code,例如 US。country_code 与 country 选用一个即可。 |
countrystring |
query | 可选 | country_code 的别名;同时传入时,两项条件都必须匹配。 |
regionstring |
query | 可选 | 匹配商品返回的 region 原值。region 与 city 选用一个即可。 |
citystring |
query | 可选 | region 的别名;同时传入时,两项条件都必须匹配。 |
linestring |
query | 可选 | 按商品显示名称搜索包含此文字的商品。 |
ip_typestring |
query | 可选 | 匹配返回的 IP 类型。当前代码:1 为广播,2 为原生住宅。 |
quantityinteger |
query | 可选 | 用于计算单价的数量,默认 1,限制在 1–2000;不是按库存充足数量筛选。 |
duration_daysinteger |
query | 可选 | 用于报价的时长,默认 30 天,限制在 1–1,095 天;仅返回支持该时长的商品。 |
line_groupstring |
query | 可选 | ordinary(普通)或 recommended(推荐)。分页前筛选,仅筛选不会自动增加响应字段。 |
selectionstring |
query | 可选 | 可选值 highest_stock,返回与 /offers/available 相同的可售商品列表;不传时使用 /offers 的默认列表。 |
location_idstring |
query | 可选 | 地区目录返回的 location_id,格式为 loc_ 加 26 位小写十六进制字符。有效格式但不存在的标识返回空列表。 |
tagstring |
query | 可选 | ordinary(普通)或 recommended(推荐),与其他筛选同时匹配。 |
includestring |
query | 可选 | 按需扩展响应:cidr_stock、line_group、catalog,可用逗号组合。不传保留默认响应。 |
- 报价按账户价格、查询数量与时长计算,不预留库存;购买前价格和库存可能变化。请勿将不同商品的库存相加。库存值 null 表示未知,不代表 0 或无限。
- 金额计算使用整数最小货币单位或精确十进制运算,不要使用二进制浮点数;小数金额字段为保留两位小数的字符串。
- 原样传回 next_page_token,并保持筛选条件与交付模式不变。标记有效期为 3 天。has_more 为 true 时即使本页为空也应继续;分页不代表冻结快照。
- 沙盒提供普通、推荐和 CIDR 测试商品,号段库存包含 3、0、null;null 表示未知,不能当作 0 或总库存。数量限制按每次请求校验,不模拟全局库存耗尽;返回连接仅用于测试,不代表所选号段的真实出口。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/offers?page_size=200
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.currency | string | 本响应金额所使用的币种。 |
data.payment_mode | string | wallet,API 购买使用账户钱包。 |
data.offers | object[] | 本页商品。即使为空数组,has_more 为 true 时仍需继续翻页。 |
data.offers[].offer_code | string | 下单使用的公开商品标识,不要解析。 |
data.offers[].name | string | 面向客户的商品名称。 |
data.offers[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.offers[].country_name | string | 国家显示名称。 |
data.offers[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.offers[].line_name | string | 面向客户的地区或线路名称,不是稳定标识。 |
data.offers[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.offers[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.offers[].unit_price_cents | integer | 按所查询数量与时长计算的单个 IP 价格,整数最小货币单位。 |
data.offers[].unit_price | string | 同一单价,保留两位小数的字符串。 |
data.offers[].base_duration_days | integer | 当前单价对应的天数。 |
data.offers[].duration_days | integer[] | 商品支持的购买天数数组。 |
data.offers[].max_quantity | integer | 当前商品在可售限制下的总购买上限,不代表每个号段都有此数量。 |
data.offers[].cidrs | string[] | 可选号段数组;空数组表示不提供指定号段选项。 |
data.offers[].cidr_stock按需返回 | object<string, integer|null> | 仅 include=cidr_stock 时返回。每个号段对应可售数量,未知为 null;空映射为 {},0 表示无可售库存。 |
data.offers[].stock_updated_at按需返回 | string|null | 仅 include=cidr_stock 时返回。库存快照时间,ISO 8601 UTC 或 null;沙盒为 null。 |
data.offers[].line_group按需返回 | string | 仅 include=line_group 时返回。ordinary(普通)或 recommended(推荐)。 |
data.offers[].location_id按需返回 | string|null | 仅 include=catalog 时返回公开地区标识,未知为 null。 |
data.offers[].tag按需返回 | string | 仅 include=catalog 时返回 ordinary 或 recommended。 |
data.page_size | integer | 本次响应采用的每页上限,不是本页实际记录数。 |
data.has_more | boolean | 是否可以继续请求下一页。 |
data.next_page_token | string|null | 下一页的不可解析标记,没有下一页时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"currency": "CNY",
"payment_mode": "wallet",
"offers": [
{
"offer_code": "off_0123456789abcdef0123456789",
"name": "United States - Los Angeles",
"country_code": "US",
"country_name": "United States",
"region": "Los Angeles",
"line_name": "United States - Los Angeles",
"ip_type": "2",
"protocol": "0",
"unit_price_cents": 2000,
"unit_price": "20.00",
"base_duration_days": 30,
"duration_days": [
30,
60,
90
],
"max_quantity": 12,
"cidrs": [
"198.51.100.0/24"
]
}
],
"page_size": 200,
"has_more": false,
"next_page_token": null
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
产品类型、协议与库存
ip_type 通常为字符串:"1" 表示广播 IP,"2" 表示原生住宅 IP;缺失时可为 null,按未知类型处理。历史数据或沙盒也可能返回 broadcast、residential、native_residential、origin_residential、static_residential 等文本值。筛选时使用产品返回的原值;不要把未知值自动归为广播或原生,也不要用“类型 1 / 类型 2”作为客户商品名称。
普通/推荐属于产品线路分类,不是 ip_type 的广播/原生分类。请求 include=line_group 后,每个产品额外返回 line_group:ordinary 表示普通,recommended 表示推荐。分类与官网一致,按 offer_code 识别;cidrs 中的号段属于该产品,不要按名称、价格、购买时长或 CIDR 字符串猜分类。
用 line_group=ordinary 只查看普通产品,line_group=recommended 只查看推荐产品;不传或留空时不按分类筛选。非空值仅接受这两个小写枚举,其他值或数组返回 HTTP 422 / validation_error。筛选参数不会自动增加返回字段;需要读取分类时仍加 include=line_group。
同时获取分类与库存使用 include=cidr_stock,line_group,多项以逗号分隔,顺序不影响扩展内容。旧请求的返回字段保持不变;原 include=cidr_stock 仍只增加库存及观察时间。下单继续使用返回的 offer_code 和可选 cidr,不需要提交 line_group。
分页时保留相同分类和其他筛选条件,并继续携带需要的 include 扩展。分页 token 绑定分类,不能把普通产品的 token 用于推荐产品或未筛选目录,否则返回 invalid_page_token。修改筛选时从第一页开始;所有查询参数仍按现有规则参与签名。
当前静态代理默认同时支持 HTTP 和 SOCKS5。protocol 为兼容保留,目前不用于选择或限制连接协议;收到 "0" 时无需按该值区分协议。使用交付的 host、port、username、password,在客户端选择 HTTP 或 SOCKS5 连接。connection 和 socks5_uri 是连接格式,不是连通性检测结果。
max_quantity 是本条报价当前可购买的总上限,不是每个号段的数量,也不是已为你预留的库存。不同商品的 max_quantity 不可相加作为地区总库存。默认 /offers 列表中的 quantity 用于计算报价,不按该数量过滤库存;按数量查询可售商品请使用 /offers/available。
cidrs 是可选号段标识列表,列在其中不代表该段仍有库存。GET /api/v1/offers 的 cidr 查询参数不会筛选库存,也不会把 max_quantity 改成单段数量。指定号段购买时使用下面的逐段数量,并且仍需满足本条报价的总上限;创建订单会再次校验。
请求 GET /api/v1/offers?include=cidr_stock 后,每个产品额外返回 cidr_stock 和 stock_updated_at。未请求 cidr_stock 扩展时不增加这两个字段。翻页时继续带上该扩展,并将所有查询参数纳入正常的 query 签名。
cidr_stock 是以号段为键的对象:正整数表示该段在当前快照中可购买的上限,0 表示当前不可购买,null 表示未知。不要把 null 当作 0、无限库存或总库存;不要为未知号段显示确定数量。没有可选号段时返回 {}。逐段额度共享同一报价的总额度,不能绕过 max_quantity。
stock_updated_at 是库存快照的观察时间,使用 UTC ISO 8601 格式;未知时为 null。读取 API 不会实时刷新库存或预留资源。两次查询之间库存可能变化,应按最新结果更新展示,并以创建订单及后续交付状态为准。沙盒返回模拟库存,该时间字段为 null。
以下示例筛选推荐产品,并同时请求分类和逐号段库存;响应仅列出相关字段,数值和网段均为示例。
GET /api/v1/offers?line_group=recommended&include=cidr_stock,line_group&page_size=200
{
"offer_code": "offer-code-from-response",
"line_group": "recommended",
"ip_type": "2",
"protocol": "0",
"max_quantity": 20,
"cidrs": ["198.51.100.0/24", "203.0.113.0/24", "192.0.2.0/24"],
"cidr_stock": {
"198.51.100.0/24": 12,
"203.0.113.0/24": 0,
"192.0.2.0/24": null
},
"stock_updated_at": "2026-11-09T08:00:00+00:00"
}
/api/v1/orders创建订单
使用钱包购买一个或多个商品,再查询订单获取交付结果。
所需权限orders:write
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
Idempotency-Keystring |
header | 必填 | 写入操作必填。8–160 个字符,仅允许英文字母、数字、点、下划线、冒号和连字符。重试保留原键与完全相同的请求体。 |
X-PuppyIP-Sandbox-Scenariostring |
header | 可选 | 仅沙盒下单:success、delayed、partial、rejected、final_partial 或 indeterminate。不传使用默认即时模拟;正式下单拒绝非空值。 |
offer_codestring |
body | 可选 | 单商品格式必填,最长 80 个字符,取自商品列表的 offer_code;不能与 items 同时传入。 |
quantityinteger |
body | 可选 | 单商品格式必填,范围 1–2000;多商品各项数量合计也不能超过 2000。 |
cidrstring|null |
body | 可选 | 可选,填写该商品 cidrs 中的号段,最长 64 个字符。不传或为 null 时自动分配。 |
itemsobject[] |
body | 可选 | 替代 offer_code/quantity/cidr 的多商品格式,包含 1–50 项;两种格式不能混用,各项共用顶层 duration_days。 |
items[].offer_codestring |
body | 必填 | 使用 items 时每项必填,填写商品 offer_code,最长 80 个字符。 |
items[].quantityinteger |
body | 必填 | 使用 items 时每项必填,范围 1–2000,各项合计不能超过 2000。 |
items[].cidrstring|null |
body | 可选 | 可选,填写该商品 cidrs 中的号段,最长 64 个字符。不传或为 null 时自动分配。 |
duration_daysinteger |
body | 必填 | 正数且为 30 的整数倍,不超过 1,095 天,并且所有所选商品均支持该时长。 |
notestring|null |
body | 可选 | 可选订单备注,最长 1,000 个字符。随订单保存,但这些 API 响应不会返回此字段。 |
external_order_nostring|null |
body | 可选 | 你方订单号,可空;最长 80 个字符,以英文字母或数字开头,仅允许英文字母、数字、点、下划线、冒号和连字符。 |
- HTTP 201 表示订单已创建,不代表交付完成。保存 order_no,并按 poll_after_seconds 查询。同一商品不能重复指定同一号段,也不能在同次请求中混用自动分配与指定号段。
- 金额计算使用整数最小货币单位或精确十进制运算,不要使用二进制浮点数;小数金额字段为保留两位小数的字符串。
- 超时后保留原 Idempotency-Key 与完全相同的请求体字节,并使用新的时间戳和 nonce 重试。不要仅因结果尚未完成就换一个键重新购买或续费。
- external_order_no 在当前账户与环境内唯一;同订单号、相同原始请求体重试返回原创建结果,不会再次扣款。不同请求体返回 409 external_order_conflict。查询只在 include=external_order_no 时增加该字段。
- 沙盒 final_partial 至少购买 2 个 IP:交付一部分并退还已确定未交付部分,最终状态 delivered、payment_status=partially_refunded。indeterminate 保持 procurement_blocked,不交付、不自动退款或重复执行。旧 partial 场景仍表示暂时部分交付,随后完成。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
POST /api/v1/orders
Content-Type: application/json
Idempotency-Key: example-create-order-001
{
"offer_code": "off_0123456789abcdef0123456789",
"quantity": 1,
"duration_days": 30
}
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.external_order_no按需返回 | string|null | 你方订单号。创建时仅传入此参数才返回;查询时仅 include=external_order_no 才返回,未关联时为 null。 |
data.order_no | string | 公开订单号,用于查询订单详情。 |
data.status | string | 常见值:pending_payment、paid、procuring、partially_delivered、procurement_blocked、delivered、refunded、cancelled;请结合交付与退款字段判断。 |
data.payment_status | string | 支付状态,例如 unpaid、paid、partially_refunded、refunded、failed 或 cancelled;不能仅凭 HTTP 成功判断。 |
data.payment_method | string | 订单记录的支付方式,例如 wallet;网站订单可能使用其他支付方式。 |
data.currency | string | 订单业务金额的币种。退款币种可能不同,请单独读取 refund.currency。 |
data.subtotal_cents | integer | 订单小计,整数最小货币单位。 |
data.total_cents | integer | 订单合计,整数最小货币单位。 |
data.total | string | 合计金额,保留两位小数的字符串。 |
data.created_at | string|null | 创建时间,ISO 8601 UTC;无记录时为 null。 |
data.items | object[] | 客户购买的订单明细。 |
data.items[].name | string | 面向客户的商品名称。 |
data.items[].quantity | integer | 本项购买的 IP 数量。 |
data.items[].duration_days | integer | 购买天数。 |
data.items[].offer_code | string|null | 公开商品标识,无关联商品时为 null。 |
data.items[].unit_price_cents | integer | 订单记录的单个 IP 价格,整数最小货币单位。 |
data.items[].total_price_cents | integer | 订单记录的本项合计,整数最小货币单位。 |
data.delivery | object | 交付数量与读取状态。 |
data.delivery.expected_count | integer | 原始购买的 IP 数量。 |
data.delivery.delivered_count | integer | 订单当前记录的连接数量,使用前仍需检查 available。 |
data.delivery.complete | boolean | 订单交付是否完成;部分退款已完成的订单,交付数量可能少于原始购买数量。 |
data.delivery.available | boolean | 当前交付模式下是否允许读取连接信息。 |
data.poll_after_seconds | integer|null | 建议再次查询订单前等待的秒数;不再建议轮询交付时为 null。 |
data.refund | object|null | 退款进度,无退款信息时为 null。退款成功可能仅覆盖订单的一部分。 |
data.refund.status | string|null | succeeded(成功)、pending(处理中)、failed(失败)或 null。 |
data.refund.destination | string|null | 记录明确为站内钱包时返回 wallet,否则为 null;null 不代表已原路退回。 |
data.refund.amount_cents | integer|null | 记录的退款金额,整数最小货币单位;无法确认单一金额时为 null,null 不等于 0。 |
data.refund.currency | string|null | 记录的退款金额币种,未知为 null,不要用订单币种代替。 |
data.refund.refunded_at | string|null | 记录的退款时间,ISO 8601 UTC 或 null。 |
data.proxies | object[] | 可读取的连接记录;订单尚不允许当前交付模式读取时为空数组。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
响应示例 HTTP 201
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"order_no": "PSO20261109000000EXAMPLE",
"status": "procuring",
"payment_status": "paid",
"payment_method": "wallet",
"currency": "CNY",
"subtotal_cents": 2000,
"total_cents": 2000,
"total": "20.00",
"created_at": "2026-11-09T00:00:00+00:00",
"items": [
{
"name": "United States - Los Angeles",
"quantity": 1,
"duration_days": 30,
"offer_code": "off_0123456789abcdef0123456789",
"unit_price_cents": 2000,
"total_price_cents": 2000
}
],
"delivery": {
"expected_count": 1,
"delivered_count": 0,
"complete": false,
"available": false
},
"poll_after_seconds": 1,
"refund": null,
"proxies": []
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
创建多地区订单
POST /api/v1/orders
Idempotency-Key: order-demo-001
{
"duration_days": 30,
"items": [
{"offer_code": "sandbox_us_static", "quantity": 2},
{"offer_code": "sandbox_jp_static", "quantity": 1}
]
}
items 最多 50 行,整笔订单总数量不能超过 2000。同一个产品不能重复提交相同 CIDR,也不能同时混用随机分配和指定 CIDR。旧版单地区格式 offer_code + quantity + duration_days 继续兼容,但不能与 items 同时出现。duration_days 必须是 30 的正整数倍,且符合所选产品支持的时长。
下单备注(可选)
下游接入方可在 POST /api/v1/orders 的 JSON 顶层传入 note,区分自用与代客户购买。note 为可选字符串,最多 1000 个字符,保存时去除首尾空白;不传、null、空字符串或纯空白均按原来的无备注下单处理。非字符串或超长备注返回 HTTP 422、code=validation_error,不创建订单、不扣款。
POST /api/v1/orders
Idempotency-Key: order-note-demo-001
{
"offer_code": "sandbox_us_static",
"quantity": 2,
"duration_days": 30,
"note": "客户A / 业务单号12345"
}
旧版单地区格式和 items 多地区格式都支持同一个顶层 note,不放在 items 内。一笔订单的备注应用到该订单首次交付的全部 IP;交付后可在网站 IP 管理的现有备注栏查看、搜索、修改或清空。后续交付重试和同步不会恢复或覆盖已修改的备注。备注仅用于本账号业务标记,不改变价格、扣费、库存或 IP 归属,历史订单不自动补写。
正式环境与沙盒使用相同备注规则。旧客户端无需增加参数;原有下单、订单和 IP 查询响应字段保持不变,不新增 note 返回字段。签名仍使用实际发送的完整原始 JSON(包含 note),不要先去除备注空白再单独签名。重试必须保持原始 body;同一 Idempotency-Key 改传其他备注会返回 409 idempotency_key_conflict,不能通过重发购买请求修改备注,也不要为修改备注换 key 重新下单。
/api/v1/orders查询订单列表
分页查看当前账户的订单,包括在网站购买的订单。
所需权限orders:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
page_sizeinteger |
query | 可选 | 每页条数:1–100;使用分页时默认 50。 |
page_tokenstring |
query | 可选 | 原样传入上一页的 next_page_token。第一页不传,最长 4,096 个字符。 |
X-PuppyIP-Delivery-Modestring |
header | 可选 | 此 GET 请求可选传入 partial,允许在其他数量仍等待处理时读取已可用的连接。 |
order_nostring |
query | 可选 | 按 PuppyIP order_no 精确筛选,最长 80 个字符。 |
external_order_nostring |
query | 可选 | 按你方 external_order_no 精确筛选,最长 80 个字符。不会自动增加响应字段。 |
statusstring |
query | 可选 | 按此接口返回的公开 status 精确筛选,最长 40 个字符;未知状态返回空列表。 |
created_fromstring |
query | 可选 | 包含边界的开始时间,ISO 8601 且必须带时区。支持最多 6 位小数秒、四位年份 0001–9999 和不超过 ±14:00 的偏移;统一按 UTC 比较。 |
created_tostring |
query | 可选 | 包含边界的结束时间,格式同开始时间,不能早于开始时间。 |
includestring |
query | 可选 | 仅 include=external_order_no 时增加你方订单号;未设置的记录返回 null。不传则不增加字段。 |
- 列表返回摘要,不包含连接凭据或单项价格。状态按当前交付模式筛选。has_more=true 时,即使本页为空,也需继续翻页。
- 部分交付仅通过 GET 请求的可选请求头开启。不传时,原有交付行为与响应字段保持不变。
- 原样传回 next_page_token,并保持筛选条件与交付模式不变。标记有效期为 3 天。has_more 为 true 时即使本页为空也应继续;分页不代表冻结快照。
- 筛选条件同时生效,后续页必须重复等价条件与交付模式;更改或移除条件返回 invalid_page_token。日期区间包含两端。
- external_order_no 在当前账户与环境内唯一;同订单号、相同原始请求体重试返回原创建结果,不会再次扣款。不同请求体返回 409 external_order_conflict。查询只在 include=external_order_no 时增加该字段。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/orders?page_size=50
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.orders | object[] | 本页订单摘要,连接凭据请从订单详情获取。 |
data.orders[].external_order_no按需返回 | string|null | 你方订单号。创建时仅传入此参数才返回;查询时仅 include=external_order_no 才返回,未关联时为 null。 |
data.orders[].order_no | string | 公开订单号,用于查询订单详情。 |
data.orders[].status | string | 常见值:pending_payment、paid、procuring、partially_delivered、procurement_blocked、delivered、refunded、cancelled;请结合交付与退款字段判断。 |
data.orders[].payment_status | string | 支付状态,例如 unpaid、paid、partially_refunded、refunded、failed 或 cancelled;不能仅凭 HTTP 成功判断。 |
data.orders[].payment_method | string | 订单记录的支付方式,例如 wallet;网站订单可能使用其他支付方式。 |
data.orders[].currency | string | 订单业务金额的币种。退款币种可能不同,请单独读取 refund.currency。 |
data.orders[].total_cents | integer | 订单合计,整数最小货币单位。 |
data.orders[].total | string | 合计金额,保留两位小数的字符串。 |
data.orders[].created_at | string|null | 创建时间,ISO 8601 UTC;无记录时为 null。 |
data.orders[].items | object[] | 客户购买的订单明细。 |
data.orders[].items[].name | string | 面向客户的商品名称。 |
data.orders[].items[].quantity | integer | 本项购买的 IP 数量。 |
data.orders[].items[].duration_days | integer | 购买天数。 |
data.orders[].items[].offer_code | string|null | 公开商品标识,无关联商品时为 null。 |
data.orders[].delivery | object | 交付数量与读取状态。 |
data.orders[].delivery.expected_count | integer | 原始购买的 IP 数量。 |
data.orders[].delivery.delivered_count | integer | 订单当前记录的连接数量,使用前仍需检查 available。 |
data.orders[].delivery.complete | boolean | 订单交付是否完成;部分退款已完成的订单,交付数量可能少于原始购买数量。 |
data.orders[].delivery.available | boolean | 当前交付模式下是否允许读取连接信息。 |
data.orders[].delivery.remaining_count按需返回 | integer | 仅 X-PuppyIP-Delivery-Mode: partial 时返回。扣除已交付和已确认退款数量后仍待处理的数量,最终状态订单为 0。 |
data.orders[].poll_after_seconds | integer|null | 建议再次查询订单前等待的秒数;不再建议轮询交付时为 null。 |
data.orders[].refund | object|null | 退款进度,无退款信息时为 null。退款成功可能仅覆盖订单的一部分。 |
data.orders[].refund.status | string|null | succeeded(成功)、pending(处理中)、failed(失败)或 null。 |
data.orders[].refund.destination | string|null | 记录明确为站内钱包时返回 wallet,否则为 null;null 不代表已原路退回。 |
data.orders[].refund.amount_cents | integer|null | 记录的退款金额,整数最小货币单位;无法确认单一金额时为 null,null 不等于 0。 |
data.orders[].refund.currency | string|null | 记录的退款金额币种,未知为 null,不要用订单币种代替。 |
data.orders[].refund.refunded_at | string|null | 记录的退款时间,ISO 8601 UTC 或 null。 |
data.page_size | integer | 本次响应采用的每页上限,不是本页实际记录数。 |
data.has_more | boolean | 是否可以继续请求下一页。 |
data.next_page_token | string|null | 下一页的不可解析标记,没有下一页时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"orders": [
{
"order_no": "PSO20261109000000EXAMPLE",
"status": "delivered",
"payment_status": "paid",
"payment_method": "wallet",
"currency": "CNY",
"total_cents": 2000,
"total": "20.00",
"created_at": "2026-11-09T00:00:00+00:00",
"items": [
{
"name": "United States - Los Angeles",
"quantity": 1,
"duration_days": 30,
"offer_code": "off_0123456789abcdef0123456789"
}
],
"delivery": {
"expected_count": 1,
"delivered_count": 1,
"complete": true,
"available": true
},
"poll_after_seconds": null,
"refund": null
}
],
"page_size": 50,
"has_more": false,
"next_page_token": null
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/orders/{order_no}查询订单详情
查看支付、交付和退款进度,并获取可用的连接信息。
所需权限orders:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
order_nostring |
path | 必填 | 创建订单返回的 order_no,仅能查询当前账户自己的订单。 |
X-PuppyIP-Delivery-Modestring |
header | 可选 | 此 GET 请求可选传入 partial,允许在其他数量仍等待处理时读取已可用的连接。 |
includestring |
query | 可选 | 仅 include=external_order_no 时增加你方订单号;未设置的记录返回 null。不传则不增加字段。 |
- 通过 delivery.available 判断能否读取连接。退款的 currency 与 amount_cents 应独立于订单合计读取,未知退款金额不能当作 0。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
- 部分交付仅通过 GET 请求的可选请求头开启。不传时,原有交付行为与响应字段保持不变。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/orders/PSO20261109000000EXAMPLE
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.external_order_no按需返回 | string|null | 你方订单号。创建时仅传入此参数才返回;查询时仅 include=external_order_no 才返回,未关联时为 null。 |
data.order_no | string | 公开订单号,用于查询订单详情。 |
data.status | string | 常见值:pending_payment、paid、procuring、partially_delivered、procurement_blocked、delivered、refunded、cancelled;请结合交付与退款字段判断。 |
data.payment_status | string | 支付状态,例如 unpaid、paid、partially_refunded、refunded、failed 或 cancelled;不能仅凭 HTTP 成功判断。 |
data.payment_method | string | 订单记录的支付方式,例如 wallet;网站订单可能使用其他支付方式。 |
data.currency | string | 订单业务金额的币种。退款币种可能不同,请单独读取 refund.currency。 |
data.subtotal_cents | integer | 订单小计,整数最小货币单位。 |
data.total_cents | integer | 订单合计,整数最小货币单位。 |
data.total | string | 合计金额,保留两位小数的字符串。 |
data.created_at | string|null | 创建时间,ISO 8601 UTC;无记录时为 null。 |
data.items | object[] | 客户购买的订单明细。 |
data.items[].name | string | 面向客户的商品名称。 |
data.items[].quantity | integer | 本项购买的 IP 数量。 |
data.items[].duration_days | integer | 购买天数。 |
data.items[].offer_code | string|null | 公开商品标识,无关联商品时为 null。 |
data.items[].unit_price_cents | integer | 订单记录的单个 IP 价格,整数最小货币单位。 |
data.items[].total_price_cents | integer | 订单记录的本项合计,整数最小货币单位。 |
data.delivery | object | 交付数量与读取状态。 |
data.delivery.expected_count | integer | 原始购买的 IP 数量。 |
data.delivery.delivered_count | integer | 订单当前记录的连接数量,使用前仍需检查 available。 |
data.delivery.complete | boolean | 订单交付是否完成;部分退款已完成的订单,交付数量可能少于原始购买数量。 |
data.delivery.available | boolean | 当前交付模式下是否允许读取连接信息。 |
data.delivery.remaining_count按需返回 | integer | 仅 X-PuppyIP-Delivery-Mode: partial 时返回。扣除已交付和已确认退款数量后仍待处理的数量,最终状态订单为 0。 |
data.poll_after_seconds | integer|null | 建议再次查询订单前等待的秒数;不再建议轮询交付时为 null。 |
data.refund | object|null | 退款进度,无退款信息时为 null。退款成功可能仅覆盖订单的一部分。 |
data.refund.status | string|null | succeeded(成功)、pending(处理中)、failed(失败)或 null。 |
data.refund.destination | string|null | 记录明确为站内钱包时返回 wallet,否则为 null;null 不代表已原路退回。 |
data.refund.amount_cents | integer|null | 记录的退款金额,整数最小货币单位;无法确认单一金额时为 null,null 不等于 0。 |
data.refund.currency | string|null | 记录的退款金额币种,未知为 null,不要用订单币种代替。 |
data.refund.refunded_at | string|null | 记录的退款时间,ISO 8601 UTC 或 null。 |
data.proxies | object[] | 可读取的连接记录;订单尚不允许当前交付模式读取时为空数组。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"order_no": "PSO20261109000000EXAMPLE",
"status": "delivered",
"payment_status": "paid",
"payment_method": "wallet",
"currency": "CNY",
"subtotal_cents": 2000,
"total_cents": 2000,
"total": "20.00",
"created_at": "2026-11-09T00:00:00+00:00",
"items": [
{
"name": "United States - Los Angeles",
"quantity": 1,
"duration_days": 30,
"offer_code": "off_0123456789abcdef0123456789",
"unit_price_cents": 2000,
"total_price_cents": 2000
}
],
"delivery": {
"expected_count": 1,
"delivered_count": 1,
"complete": true,
"available": true
},
"poll_after_seconds": null,
"refund": null,
"proxies": [
{
"proxy_id": "px_0123456789abcdef0123456789",
"status": "active",
"host": "198.51.100.10",
"port": 10080,
"protocol": "0",
"username": "example_user",
"password": "example_password",
"connection": "198.51.100.10:10080:example_user:example_password",
"socks5_uri": "socks5://example_user:[email protected]:10080",
"ip_type": "2",
"country_code": "US",
"region": "Los Angeles",
"line_name": "US Los Angeles 2",
"starts_at": "2026-11-09T00:00:00+00:00",
"expires_at": "2026-12-09T00:00:00+00:00"
}
]
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
订单详情包含 order_no、status、payment_status、payment_method、currency、subtotal_cents、total_cents、total、created_at、items、delivery、poll_after_seconds、refund、proxies。列表只返回安全摘要,不返回代理凭据。refund 为 null 或退款状态对象,含 status、destination、amount_cents、currency、refunded_at。refund.status=succeeded 表示记录的退款已成功,不代表整单已退;是否全退看 payment_status。无法确认的金额、币种或去向返回 null,不能当作 0 元或未退款。amount_cents 必须与其 currency 一起读取,不要按订单币种或当前汇率推算。
/api/v1/proxies查询 IP 列表
分页获取账户下可读取的 IP 连接信息。
所需权限proxies:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
page_sizeinteger |
query | 可选 | 每页条数:1–100;使用分页时默认 100。 |
page_tokenstring |
query | 可选 | 原样传入上一页的 next_page_token。第一页不传,最长 4,096 个字符。 |
X-PuppyIP-Delivery-Modestring |
header | 可选 | 此 GET 请求可选传入 partial,允许在其他数量仍等待处理时读取已可用的连接。 |
proxy_idstring |
query | 可选 | 按 proxy_id 精确筛选,最长 80 个字符;与 proxy_ids[] 同时传入时取交集。 |
order_nostring |
query | 可选 | 按 PuppyIP order_no 精确筛选,最长 80 个字符。 |
hoststring |
query | 可选 | 按返回的 host 精确筛选,最长 253 个字符。 |
host_containsstring |
query | 可选 | 按地址片段筛选,最长 45 个字符,仅允许十六进制字符、点和冒号;不支持 % 或 _ 通配符。 |
statusstring |
query | 可选 | 按此接口返回的公开 status 精确筛选,最长 40 个字符;未知状态返回空列表。 |
country_codestring |
query | 可选 | 按返回的 country_code 精确筛选,最长 16 个字符。 |
regionstring |
query | 可选 | 按返回的 region 精确筛选,最长 255 个字符;与 city 同时传入时必须相同,否则返回 422。 |
citystring |
query | 可选 | region 的别名,最长 255 个字符;两者同时传入时必须相同。 |
ip_typestring |
query | 可选 | 按返回的 ip_type 原值精确筛选,最长 40 个字符。 |
expires_fromstring |
query | 可选 | 包含边界的开始时间,ISO 8601 且必须带时区。支持最多 6 位小数秒、四位年份 0001–9999 和不超过 ±14:00 的偏移;统一按 UTC 比较。 |
expires_tostring |
query | 可选 | 包含边界的结束时间,格式同开始时间,不能早于开始时间。 |
proxy_ids[]string[] |
query | 可选 | 最多 100 个 proxy_id,每项最长 80 个字符;重复值会合并,使用 proxy_ids[]=... 查询格式。 |
expiring_soonboolean |
query | 可选 | true 或 1:筛选尚未到期且将在 7 天内到期的 IP;false、0 或空值不筛选。翻页沿用第一页的时间基准。 |
- 新接入请显式分页。正式环境同时不传两项分页参数时保留最多 2000 条的旧行为,并非无限返回。筛选不改变账户及过期保留边界。
- 生产 IP 列表保留到期后 72 小时内的资源,expires_at 为 null 的资源也仍可列入。此列表规则不代表单个 IP 详情或沙盒列表存在相同时间限制。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
- 部分交付仅通过 GET 请求的可选请求头开启。不传时,原有交付行为与响应字段保持不变。
- 原样传回 next_page_token,并保持筛选条件与交付模式不变。标记有效期为 3 天。has_more 为 true 时即使本页为空也应继续;分页不代表冻结快照。
- 筛选条件同时生效,后续页必须重复等价条件与交付模式;更改或移除条件返回 invalid_page_token。日期区间包含两端。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/proxies?page_size=100
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.proxies | object[] | 可读取的连接记录;订单尚不允许当前交付模式读取时为空数组。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
data.page_size | integer | 本次响应采用的每页上限,不是本页实际记录数。 |
data.has_more | boolean | 是否可以继续请求下一页。 |
data.next_page_token | string|null | 下一页的不可解析标记,没有下一页时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"proxies": [
{
"proxy_id": "px_0123456789abcdef0123456789",
"status": "active",
"host": "198.51.100.10",
"port": 10080,
"protocol": "0",
"username": "example_user",
"password": "example_password",
"connection": "198.51.100.10:10080:example_user:example_password",
"socks5_uri": "socks5://example_user:[email protected]:10080",
"ip_type": "2",
"country_code": "US",
"region": "Los Angeles",
"line_name": "US Los Angeles 2",
"starts_at": "2026-11-09T00:00:00+00:00",
"expires_at": "2026-12-09T00:00:00+00:00"
}
],
"page_size": 100,
"has_more": false,
"next_page_token": null
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/proxies/{proxy_id}查询单个 IP
通过已保存的 IP 标识查询最新连接信息。
所需权限proxies:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
proxy_idstring |
path | 必填 | 原样使用订单或 IP 响应返回的 proxy_id,不要用连接地址或 IP 地址代替。 |
X-PuppyIP-Delivery-Modestring |
header | 可选 | 此 GET 请求可选传入 partial,允许在其他数量仍等待处理时读取已可用的连接。 |
- 以 proxy_id 关联资源,需要时查询最新连接信息。此接口在 data 中直接返回 IP 对象,没有额外 environment 包装。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
- 部分交付仅通过 GET 请求的可选请求头开启。不传时,原有交付行为与响应字段保持不变。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/proxies/px_0123456789abcdef0123456789
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.port | integer | 连接端口。 |
data.protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.password | string | 连接密码,属于敏感信息。 |
data.connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"proxy_id": "px_0123456789abcdef0123456789",
"status": "active",
"host": "198.51.100.10",
"port": 10080,
"protocol": "0",
"username": "example_user",
"password": "example_password",
"connection": "198.51.100.10:10080:example_user:example_password",
"socks5_uri": "socks5://example_user:[email protected]:10080",
"ip_type": "2",
"country_code": "US",
"region": "Los Angeles",
"line_name": "US Los Angeles 2",
"starts_at": "2026-11-09T00:00:00+00:00",
"expires_at": "2026-12-09T00:00:00+00:00"
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/proxies/renew续费 IP
使用钱包延长所选 IP 的有效期,并逐个查看续费结果。
所需权限proxies:write
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
Idempotency-Keystring |
header | 必填 | 写入操作必填。8–160 个字符,仅允许英文字母、数字、点、下划线、冒号和连字符。重试保留原键与完全相同的请求体。 |
X-PuppyIP-Sandbox-Scenariostring |
header | 可选 | 仅沙盒续费:completed、partial、failed、unresolved;unresolved 对应结果 manual_review。partial 至少需要 2 个 IP。不传保留默认全部成功模拟;正式续费忽略此请求头。 |
proxy_idsstring[] |
body | 必填 | 非空数组,最多 1000 个当前账户的 IP 标识,每个标识最长 80 个字符。 |
duration_daysinteger |
body | 必填 | 增加的天数,正数且为 30 的整数倍,不超过 1,095 天。 |
syncboolean |
body | 可选 | 默认 true,保留同步续费。开放异步能力时可传 JSON 布尔值 false;受理返回 HTTP 202、operation_id 和 status=processing。 |
external_order_nostring|null |
body | 可选 | 仅 sync=false:可选参考编号,最长 100 个字符,随任务保存。与购买订单号不同,它不单独用于去重,仍需保留原 Idempotency-Key。 |
remarkstring|null |
body | 可选 | 仅 sync=false:可选备注,最长 500 个字符,随任务保存,不返回到公开结果中。 |
- HTTP 200 不代表所有 IP 都续费成功,请检查三个结果标识列表与 line_items。全部失败时 HTTP 为 422,data 仍返回相同结构的结果。待确认项需要核对结果,不要重复创建续费。
- 金额计算使用整数最小货币单位或精确十进制运算,不要使用二进制浮点数;小数金额字段为保留两位小数的字符串。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
- 超时后保留原 Idempotency-Key 与完全相同的请求体字节,并使用新的时间戳和 nonce 重试。不要仅因结果尚未完成就换一个键重新购买或续费。
- 沙盒仅成功项延长有效期;确定失败项退还对应费用,未知项保留扣款且不自动重续。failed 返回 422 operation_failed,结果保留在 data;其他场景返回 200。相同幂等键重放原结果,未决 IP 不接受新的续费。
- 不传 sync 或传 true 时保留原同步结果。sync=false 时 proxy_ids 必须不重复;首次返回 HTTP 202,data={"operation_id":"rop_...","status":"processing"},不代表续费成功。请查询 GET /api/v1/proxies/renewal-operations/{operation_id}。重试原键和原请求体可能重放最初的受理响应,进度应查询任务。正式环境需开放异步续费,沙盒使用隔离模拟。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
POST /api/v1/proxies/renew
Content-Type: application/json
Idempotency-Key: example-renew-001
{
"proxy_ids": [
"px_0123456789abcdef0123456789"
],
"duration_days": 30
}
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.status | string | completed、partial、manual_review 或 failed。failed 时 HTTP 为 422、success 为 false,data 仍保留续费结果。 |
data.currency | string | 本响应金额所使用的币种。 |
data.total_cents | integer | 续费报价合计,整数最小货币单位,尚未扣除失败项退款。 |
data.total | string | 合计金额,保留两位小数的字符串。 |
data.charged_cents | integer | 扣除本次已记录退款后的净扣款,整数最小货币单位;结果待确认的项可能仍保留扣款。 |
data.refunded_cents | integer | 本次已确认失败项的退款金额,整数最小货币单位。 |
data.successful_proxy_ids | string[] | 已确认续费成功的 IP 标识。 |
data.failed_proxy_ids | string[] | 已确认续费失败的 IP 标识。 |
data.unresolved_proxy_ids | string[] | 结果待确认的 IP 标识,不要为这些 IP 重新发起一笔续费。 |
data.line_items | object[] | 每个所请求 IP 的结果与金额。 |
data.proxies | object[] | 所请求 IP 的当前记录;哪些续费成功请以结果标识列表判断。 |
data.wallet | object | 操作后的钱包余额。 |
data.line_items[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.line_items[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.line_items[].status | string | succeeded(成功)、failed(失败)或 unresolved(待确认)。 |
data.line_items[].duration_days | integer | 请求增加的天数。 |
data.line_items[].amount_cents | integer | 此 IP 的续费报价,整数最小货币单位。 |
data.line_items[].amount | string | 同一金额,保留两位小数的字符串。 |
data.line_items[].charged_cents | integer | 扣除本次已记录退款后的净扣款,整数最小货币单位;结果待确认的项可能仍保留扣款。 |
data.line_items[].refunded_cents | integer | 本次已确认失败项的退款金额,整数最小货币单位。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
data.wallet.environment | string | production(生产)或 sandbox(沙盒)。 |
data.wallet.currency | string | 本响应金额所使用的币种。 |
data.wallet.balance_cents | integer | 钱包余额,整数最小货币单位,100 分等于 1 元。 |
data.wallet.balance | string | 同一钱包余额,保留两位小数的字符串。 |
data.wallet.frozen_cents | integer | 钱包冻结金额,整数最小货币单位。 |
data.wallet.cash_value | boolean | 生产钱包为 true,沙盒模拟余额为 false。 |
data.operation_id按需返回 | string | 异步续费任务的公开标识,用于查询进度。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"status": "completed",
"currency": "CNY",
"total_cents": 2000,
"total": "20.00",
"charged_cents": 2000,
"refunded_cents": 0,
"successful_proxy_ids": [
"px_0123456789abcdef0123456789"
],
"failed_proxy_ids": [],
"unresolved_proxy_ids": [],
"line_items": [
{
"proxy_id": "px_0123456789abcdef0123456789",
"host": "198.51.100.10",
"status": "succeeded",
"duration_days": 30,
"amount_cents": 2000,
"amount": "20.00",
"charged_cents": 2000,
"refunded_cents": 0
}
],
"proxies": [
{
"proxy_id": "px_0123456789abcdef0123456789",
"status": "active",
"host": "198.51.100.10",
"port": 10080,
"protocol": "0",
"username": "example_user",
"password": "example_password",
"connection": "198.51.100.10:10080:example_user:example_password",
"socks5_uri": "socks5://example_user:[email protected]:10080",
"ip_type": "2",
"country_code": "US",
"region": "Los Angeles",
"line_name": "US Los Angeles 2",
"starts_at": "2026-11-09T00:00:00+00:00",
"expires_at": "2027-01-08T00:00:00+00:00"
}
],
"wallet": {
"environment": "production",
"currency": "CNY",
"balance_cents": 8000,
"balance": "80.00",
"frozen_cents": 0,
"cash_value": true
}
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
POST 请求使用 application/json。续费 proxy_ids 必须为非空公共 ID 数组,每次最多 1000 个,每个 ID 最长 80 个字符;超出限制返回 HTTP 422 / validation_error。duration_days 必须为 30 的正整数倍且不超过 1095。分页 token 为不透明字符串;接口响应有可选字段,接入端应容忍新增字段,并对未知业务状态停止自动重试、保留 trace_id 联系支持。
续费 IP
POST /api/v1/proxies/renew
Idempotency-Key: renew-demo-001
{
"proxy_ids": ["SANDBOX_PROXY_20260629080000_001"],
"duration_days": 30
}
{
"status": "completed",
"total_cents": 12000,
"charged_cents": 12000,
"refunded_cents": 0,
"successful_proxy_ids": ["SANDBOX_PROXY_20260629080000_001"],
"failed_proxy_ids": [],
"unresolved_proxy_ids": [],
"line_items": [{
"proxy_id": "SANDBOX_PROXY_20260629080000_001",
"host": "198.51.100.10",
"status": "succeeded",
"duration_days": 30,
"amount_cents": 12000,
"amount": "120.00",
"charged_cents": 12000,
"refunded_cents": 0
}]
}
原有续费响应字段保持不变,上例为 data 内的业务数据,IP 为示例值。status 为 completed 表示全部成功,partial 表示部分成功、明确失败的项目已退回钱包,manual_review 表示仍有结果待核对,不应重复提交。HTTP 200 或外层 success=true 不代表每条 IP 都续费成功。三组 proxy_ids 和 line_items 必须逐项核对。
line_items 每条包含 proxy_id、host、status、duration_days、amount_cents、amount、charged_cents、refunded_cents。逐项状态为 succeeded、failed 或 unresolved;proxies 中可读取各 IP 当前 expires_at。金额整数单位为分,amount/total 是主货币单位的十进制字符串。charged_cents 是钱包扣款减退款,不等于已经确认成功的续费金额:unresolved 项仍待核对,不自动退款。仅确认失败项会退款,结果待确认的项目不按失败处理。
全部明确失败仍返回 HTTP 422、success=false、code=operation_failed;此时 data.status=failed,并保留 failed_proxy_ids、line_items、charged_cents=0、refunded_cents 和 wallet。参数校验失败或扣款前余额不足不属于这个已退款结果,不保证包含续费明细。重放返回首次操作的结果快照;需要最新余额或到期时间时,单独读取 wallet 或 proxies。
GET /api/v1/offers 获取正式 offer_code,并用 GET /api/v1/proxies 获取正式环境的 proxy_id,不要混用沙盒标识。沙盒续费支持 completed、partial、failed、unresolved;unresolved 返回 manual_review。失败项退款,未知项不自动退款或重续;partial 至少需要 2 个 IP。
/api/v1/locations查询地区
查看当前商品目录中的地区标识。
所需权限offers:read
请求参数
无需额外业务参数。请求仍需携带签名请求头。
- 目录用于筛选导航,不保证指定数量或时长可购买。请用商品接口查询报价与库存,并用 offer_code 下单。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/locations
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.locations | object[] | 当前目录中的地区列表。 |
data.locations[].location_id | string | PuppyIP 公开地区标识。用于商品筛选,不能代替 offer_code 下单。 |
data.locations[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.locations[].country_name | string | 国家显示名称。 |
data.locations[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"locations": [
{
"location_id": "loc_0123456789abcdef0123456789ab",
"country_code": "US",
"country_name": "United States",
"region": "Los Angeles"
}
]
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/locations/tree查询地区树
按国家分组查看地区目录。
所需权限offers:read
请求参数
无需额外业务参数。请求仍需携带签名请求头。
- 目录用于筛选导航,不保证指定数量或时长可购买。请用商品接口查询报价与库存,并用 offer_code 下单。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/locations/tree
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.countries | object[] | 按国家分组的地区目录。 |
data.countries[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.countries[].country_name | string | 国家显示名称。 |
data.countries[].locations | object[] | 当前目录中的地区列表。 |
data.countries[].locations[].location_id | string | PuppyIP 公开地区标识。用于商品筛选,不能代替 offer_code 下单。 |
data.countries[].locations[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.countries[].locations[].country_name | string | 国家显示名称。 |
data.countries[].locations[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"countries": [
{
"country_code": "US",
"country_name": "United States",
"locations": [
{
"location_id": "loc_0123456789abcdef0123456789ab",
"country_code": "US",
"country_name": "United States",
"region": "Los Angeles"
}
]
}
]
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/offers/available查询可售商品
获取可售商品及对应的价格、库存和可选号段。
所需权限offers:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
page_sizeinteger |
query | 可选 | 每页条数:1–200;使用分页时默认 200。 |
page_tokenstring |
query | 可选 | 原样传入上一页的 next_page_token。第一页不传,最长 4,096 个字符。 |
country_codestring |
query | 可选 | 匹配商品返回的 country_code,例如 US。country_code 与 country 选用一个即可。 |
countrystring |
query | 可选 | country_code 的别名;同时传入时,两项条件都必须匹配。 |
regionstring |
query | 可选 | 匹配商品返回的 region 原值。region 与 city 选用一个即可。 |
citystring |
query | 可选 | region 的别名;同时传入时,两项条件都必须匹配。 |
linestring |
query | 可选 | 按商品显示名称搜索包含此文字的商品。 |
ip_typestring |
query | 可选 | 匹配返回的 IP 类型。当前代码:1 为广播,2 为原生住宅。 |
quantityinteger |
query | 可选 | 用于计算单价的数量,默认 1,限制在 1–2000;不是按库存充足数量筛选。 |
duration_daysinteger |
query | 可选 | 用于报价的时长,默认 30 天,限制在 1–1,095 天;仅返回支持该时长的商品。 |
line_groupstring |
query | 可选 | ordinary(普通)或 recommended(推荐)。分页前筛选,仅筛选不会自动增加响应字段。 |
selectionstring |
query | 可选 | 可选值 highest_stock,返回与 /offers/available 相同的可售商品列表;不传时使用 /offers 的默认列表。 |
location_idstring |
query | 可选 | 地区目录返回的 location_id,格式为 loc_ 加 26 位小写十六进制字符。有效格式但不存在的标识返回空列表。 |
tagstring |
query | 可选 | ordinary(普通)或 recommended(推荐),与其他筛选同时匹配。 |
includestring |
query | 可选 | 按需扩展响应:cidr_stock、line_group、catalog,可用逗号组合。不传保留默认响应。 |
- 报价按账户价格、查询数量与时长计算,不预留库存;购买前价格和库存可能变化。请勿将不同商品的库存相加。库存值 null 表示未知,不代表 0 或无限。
- 金额计算使用整数最小货币单位或精确十进制运算,不要使用二进制浮点数;小数金额字段为保留两位小数的字符串。
- 原样传回 next_page_token,并保持筛选条件与交付模式不变。标记有效期为 3 天。has_more 为 true 时即使本页为空也应继续;分页不代表冻结快照。
- 沙盒提供普通、推荐和 CIDR 测试商品,号段库存包含 3、0、null;null 表示未知,不能当作 0 或总库存。数量限制按每次请求校验,不模拟全局库存耗尽;返回连接仅用于测试,不代表所选号段的真实出口。
- 可直接调用此接口,或使用 /offers?selection=highest_stock。支持按数量和时长筛选,每个商品的报价和可选号段以该条返回记录为准。收到 invalid_page_token 时,请从第一页重新查询。查询不预留库存。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/offers/available?page_size=200
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.currency | string | 本响应金额所使用的币种。 |
data.payment_mode | string | wallet,API 购买使用账户钱包。 |
data.offers | object[] | 本页商品。即使为空数组,has_more 为 true 时仍需继续翻页。 |
data.offers[].offer_code | string | 下单使用的公开商品标识,不要解析。 |
data.offers[].name | string | 面向客户的商品名称。 |
data.offers[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.offers[].country_name | string | 国家显示名称。 |
data.offers[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.offers[].line_name | string | 面向客户的地区或线路名称,不是稳定标识。 |
data.offers[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.offers[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.offers[].unit_price_cents | integer | 按所查询数量与时长计算的单个 IP 价格,整数最小货币单位。 |
data.offers[].unit_price | string | 同一单价,保留两位小数的字符串。 |
data.offers[].base_duration_days | integer | 当前单价对应的天数。 |
data.offers[].duration_days | integer[] | 商品支持的购买天数数组。 |
data.offers[].max_quantity | integer | 当前商品在可售限制下的总购买上限,不代表每个号段都有此数量。 |
data.offers[].cidrs | string[] | 可选号段数组;空数组表示不提供指定号段选项。 |
data.offers[].cidr_stock按需返回 | object<string, integer|null> | 仅 include=cidr_stock 时返回。每个号段对应可售数量,未知为 null;空映射为 {},0 表示无可售库存。 |
data.offers[].stock_updated_at按需返回 | string|null | 仅 include=cidr_stock 时返回。库存快照时间,ISO 8601 UTC 或 null;沙盒为 null。 |
data.offers[].line_group按需返回 | string | 仅 include=line_group 时返回。ordinary(普通)或 recommended(推荐)。 |
data.offers[].location_id按需返回 | string|null | 仅 include=catalog 时返回公开地区标识,未知为 null。 |
data.offers[].tag按需返回 | string | 仅 include=catalog 时返回 ordinary 或 recommended。 |
data.page_size | integer | 本次响应采用的每页上限,不是本页实际记录数。 |
data.has_more | boolean | 是否可以继续请求下一页。 |
data.next_page_token | string|null | 下一页的不可解析标记,没有下一页时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"currency": "CNY",
"payment_mode": "wallet",
"offers": [
{
"offer_code": "off_0123456789abcdef0123456789",
"name": "United States - Los Angeles",
"country_code": "US",
"country_name": "United States",
"region": "Los Angeles",
"line_name": "United States - Los Angeles",
"ip_type": "2",
"protocol": "0",
"unit_price_cents": 2000,
"unit_price": "20.00",
"base_duration_days": 30,
"duration_days": [
30,
60,
90
],
"max_quantity": 12,
"cidrs": [
"198.51.100.0/24"
]
}
],
"page_size": 200,
"has_more": false,
"next_page_token": null
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/announcements查询公告
读取网站当前已发布的公告。
所需权限account:read
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
page_sizeinteger |
query | 可选 | 每页条数:1–100;使用分页时默认 20。 |
page_tokenstring |
query | 可选 | 不传此参数。当前公告无下一页;非空值返回 422 invalid_page_token。 |
- 返回当前网站公告,共 0 或 1 条;has_more 为 false,next_page_token 为 null。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/announcements
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.environment | string | production(生产)或 sandbox(沙盒)。 |
data.announcements | object[] | 当前已发布公告;没有启用的公告时为空数组。 |
data.announcements[].announcement_id | string | 本条已发布公告的公开标识。 |
data.announcements[].title | object | 包含 zh_CN 与 en 的显示名称对象。 |
data.announcements[].content | string | 与网站一致的公告原文。 |
data.announcements[].published_at | string|null | 公告发布更新时间,ISO 8601 UTC 或 null。 |
data.announcements[].title.zh_CN | string | 中文显示名称。 |
data.announcements[].title.en | string | 英文显示名称。 |
data.page_size | integer | 本次响应采用的每页上限,不是本页实际记录数。 |
data.has_more | boolean | 是否可以继续请求下一页。 |
data.next_page_token | string|null | 下一页的不可解析标记,没有下一页时为 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"environment": "production",
"announcements": [
{
"announcement_id": "ann_0123456789abcdef0123456789ab",
"title": {
"zh_CN": "公告",
"en": "Announcement"
},
"content": "PuppyIP service announcement.",
"published_at": "2026-11-09T00:00:00+00:00"
}
],
"page_size": 20,
"has_more": false,
"next_page_token": null
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/proxies/credentials修改连接凭据
提交凭据修改任务,再查询各个 IP 的执行结果。
所需权限proxies:credentials
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
Idempotency-Keystring |
header | 必填 | 写入操作必填。8–160 个字符,仅允许英文字母、数字、点、下划线、冒号和连字符。重试保留原键与完全相同的请求体。 |
proxy_idsstring[] |
body | 必填 | 1–100 个不重复的当前账户 proxy_id。 |
randomboolean |
body | 可选 | true 时随机生成用户名与密码,不能再传 username 或 password。 |
usernamestring |
body | 可选 | 非随机模式至少填写用户名或密码之一;8–32 位英文字母和数字。未填写的字段保留原值;原值不符合此格式时需同时提供两项新值。 |
passwordstring |
body | 可选 | 规则同 username。不要将密码写入公开页面或日志。 |
- 此操作需要单独的 proxies:credentials 权限,旧密钥不会自动获得。正式环境只有开放此功能时可用;沙盒使用隔离的模拟资源。
- 受理后返回 202 processing。重复原键与原请求体返回同一任务的当前状态;修改请求体返回 409 idempotency_key_conflict,新的请求与未决任务重叠返回 409 operation_in_progress。查询每个 IP 的结果;未决状态不自动再次修改,等待确认。成功项快照不代表此后未发生其他修改。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
- 超时后保留原 Idempotency-Key 与完全相同的请求体字节,并使用新的时间戳和 nonce 重试。不要仅因结果尚未完成就换一个键重新购买或续费。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
POST /api/v1/proxies/credentials
Content-Type: application/json
Idempotency-Key: example-credentials-001
{
"proxy_ids": [
"px_0123456789abcdef0123456789"
],
"random": true
}
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.operation_id | string | 凭据修改任务的公开标识。 |
data.status | string | processing、completed、partial、failed 或 manual_review。 |
data.successful_proxy_ids | string[] | 已确认修改成功的 IP 标识。 |
data.failed_proxy_ids | string[] | 已确认修改失败的 IP 标识。 |
data.unresolved_proxy_ids | string[] | 修改结果未确认的 IP 标识,不应重新提交修改。 |
data.proxies | object[] | 仅包含成功项在本次任务确认时的连接快照;最新连接请查询单个 IP。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
响应示例 HTTP 202
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"operation_id": "cop_documentation_example",
"status": "processing",
"successful_proxy_ids": [],
"failed_proxy_ids": [],
"unresolved_proxy_ids": [
"px_0123456789abcdef0123456789"
],
"proxies": []
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/proxies/credential-operations/{operation_id}查询凭据修改结果
查看指定任务已确认与待确认的结果。
所需权限proxies:credentials
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
operation_idstring |
path | 必填 | 提交修改返回的 operation_id,仅可查询当前账户的任务。 |
- 受理后返回 202 processing。重复原键与原请求体返回同一任务的当前状态;修改请求体返回 409 idempotency_key_conflict,新的请求与未决任务重叠返回 409 operation_in_progress。查询每个 IP 的结果;未决状态不自动再次修改,等待确认。成功项快照不代表此后未发生其他修改。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/proxies/credential-operations/cop_documentation_example
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.operation_id | string | 凭据修改任务的公开标识。 |
data.status | string | processing、completed、partial、failed 或 manual_review。 |
data.successful_proxy_ids | string[] | 已确认修改成功的 IP 标识。 |
data.failed_proxy_ids | string[] | 已确认修改失败的 IP 标识。 |
data.unresolved_proxy_ids | string[] | 修改结果未确认的 IP 标识,不应重新提交修改。 |
data.proxies | object[] | 仅包含成功项在本次任务确认时的连接快照;最新连接请查询单个 IP。 |
data.proxies[].proxy_id | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port | integer | 连接端口。 |
data.proxies[].protocol | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password | string | 连接密码,属于敏感信息。 |
data.proxies[].connection | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at | string|null | 到期时间,ISO 8601 UTC 或 null。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"operation_id": "cop_documentation_example",
"status": "processing",
"successful_proxy_ids": [],
"failed_proxy_ids": [],
"unresolved_proxy_ids": [
"px_0123456789abcdef0123456789"
],
"proxies": []
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/proxies/renewal-operations/{operation_id}查询异步续费
查询已提交的续费任务,不会重新发起续费。
所需权限proxies:write
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
operation_idstring |
path | 必填 | 异步续费返回的 operation_id,仅能查询当前账户在当前环境的任务。 |
- 不传 sync 或传 true 时保留原同步结果。sync=false 时 proxy_ids 必须不重复;首次返回 HTTP 202,data={"operation_id":"rop_...","status":"processing"},不代表续费成功。请查询 GET /api/v1/proxies/renewal-operations/{operation_id}。重试原键和原请求体可能重放最初的受理响应,进度应查询任务。正式环境需开放异步续费,沙盒使用隔离模拟。
- 处理中 data 仅含 operation_id 与 status;核实结果后增加现有续费金额、逐项结果、钱包和连接快照。无法核实结果时,manual_review 可能不含金额和钱包字段;缺失不能解释为金额为 0 或已经退款。扣款前失败可带 error,单项 host 可为 null;报价变化会明确失败且不扣款。仅确认失败项可退款,未决项不得另起任务重续。完成结果是快照,最新连接请查询单个 IP。external_order_no 与 remark 仅在异步请求中保存,不增加到本响应。
- 连接密码和完整连接字符串仅保存在可信服务端,不要写入公开页面或日志。时间使用包含 UTC 偏移的 ISO 8601 格式;null 表示未知,不等于 0。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/proxies/renewal-operations/rop_documentation_example
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.operation_id | string | 异步续费任务的公开标识,用于查询进度。 |
data.environment按需返回 | string | production(生产)或 sandbox(沙盒)。 |
data.status | string | 处理中为 processing;有结果时为 completed、partial、failed 或 manual_review。此 GET 即使业务失败也返回 HTTP 200。 |
data.currency按需返回 | string | 本响应金额所使用的币种。 |
data.total_cents按需返回 | integer | 续费报价合计,整数最小货币单位,尚未扣除失败项退款。 |
data.total按需返回 | string | 合计金额,保留两位小数的字符串。 |
data.charged_cents按需返回 | integer | 扣除本次已记录退款后的净扣款,整数最小货币单位;结果待确认的项可能仍保留扣款。 |
data.refunded_cents按需返回 | integer | 本次已确认失败项的退款金额,整数最小货币单位。 |
data.successful_proxy_ids按需返回 | string[] | 已确认续费成功的 IP 标识。 |
data.failed_proxy_ids按需返回 | string[] | 已确认续费失败的 IP 标识。 |
data.unresolved_proxy_ids按需返回 | string[] | 结果待确认的 IP 标识,不要为这些 IP 重新发起一笔续费。 |
data.line_items按需返回 | object[] | 每个所请求 IP 的结果与金额。 |
data.proxies按需返回 | object[] | 所请求 IP 的当前记录;哪些续费成功请以结果标识列表判断。 |
data.wallet按需返回 | object | 操作后的钱包余额。 |
data.line_items[].proxy_id按需返回 | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.line_items[].host按需返回 | string|null | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.line_items[].status按需返回 | string | succeeded(成功)、failed(失败)或 unresolved(待确认)。 |
data.line_items[].duration_days按需返回 | integer | 请求增加的天数。 |
data.line_items[].amount_cents按需返回 | integer | 此 IP 的续费报价,整数最小货币单位。 |
data.line_items[].amount按需返回 | string | 同一金额,保留两位小数的字符串。 |
data.line_items[].charged_cents按需返回 | integer | 扣除本次已记录退款后的净扣款,整数最小货币单位;结果待确认的项可能仍保留扣款。 |
data.line_items[].refunded_cents按需返回 | integer | 本次已确认失败项的退款金额,整数最小货币单位。 |
data.proxies[].proxy_id按需返回 | string | IP 资源的公开标识。保存此值用于查询和续费,连接信息可能变化。 |
data.proxies[].status按需返回 | string | 当前资源状态,例如 active 或 expired,判断有效期时同时读取 expires_at。 |
data.proxies[].host按需返回 | string | 连接主机名或 IP 地址,请使用本次返回的最新值。 |
data.proxies[].port按需返回 | integer | 连接端口。 |
data.proxies[].protocol按需返回 | string|null | 兼容字段,当前商品通常返回 0;该值不用于选择 HTTP 或 SOCKS5,也可能为 null。 |
data.proxies[].username按需返回 | string|null | 连接用户名,未知时为 null;属于敏感信息。 |
data.proxies[].password按需返回 | string | 连接密码,属于敏感信息。 |
data.proxies[].connection按需返回 | string | host:port:username:password 格式的连接文本,属于敏感信息。 |
data.proxies[].socks5_uri按需返回 | string | 包含编码后凭据的 SOCKS5 连接地址,属于敏感信息。 |
data.proxies[].ip_type按需返回 | string|null | 当前代码:1 为广播,2 为原生住宅;历史记录也可能为文本或 null,请保留返回原值。 |
data.proxies[].country_code按需返回 | string|null | 国家代码,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].region按需返回 | string|null | 地区或城市,未知时为 null;筛选时使用返回的原值。 |
data.proxies[].line_name按需返回 | string|null | 面向客户的地区或线路名称,不是稳定标识。 |
data.proxies[].starts_at按需返回 | string|null | 开始时间,ISO 8601 UTC 或 null。 |
data.proxies[].expires_at按需返回 | string|null | 到期时间,ISO 8601 UTC 或 null。 |
data.wallet.environment按需返回 | string | production(生产)或 sandbox(沙盒)。 |
data.wallet.currency按需返回 | string | 本响应金额所使用的币种。 |
data.wallet.balance_cents按需返回 | integer | 钱包余额,整数最小货币单位,100 分等于 1 元。 |
data.wallet.balance按需返回 | string | 同一钱包余额,保留两位小数的字符串。 |
data.wallet.frozen_cents按需返回 | integer | 钱包冻结金额,整数最小货币单位。 |
data.wallet.cash_value按需返回 | boolean | 生产钱包为 true,沙盒模拟余额为 false。 |
data.error按需返回 | object | 扣款前失败时可出现;charged_cents 与 refunded_cents 均为 0。 |
data.error.code按需返回 | string | renewal_price_changed、insufficient_wallet_balance 或 operation_failed。 |
data.error.message按需返回 | string | 本次受理或执行失败的安全说明。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"operation_id": "rop_documentation_example",
"status": "processing"
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/webhook查询回调设置
查看当前 API 密钥配置的回调订阅。
所需权限webhooks:write
请求参数
无需额外业务参数。请求仍需携带签名请求头。
- 每个 API 密钥管理自己的订阅,需要单独的 webhooks:write 权限,旧密钥不会自动获得。正式环境关闭时,POST 配置返回 403 feature_disabled;已有配置仍可查询或停用。沙盒提供配置与验签文档用于测试,但实际向外发送仍需开启发送功能;配置成功不代表发送已开启。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
GET /api/v1/webhook
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.configured | boolean | 当前密钥是否有启用的订阅;单凭此值不能确认外发通知已开启。 |
data.url | string|null | 已配置的回调地址,停用时为 null。 |
data.events | string[] | 订阅的事件名称,停用时为空数组。 |
data.version | integer | 订阅配置版本,配置发生变化时递增。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"configured": true,
"url": "https://example.com/puppyip-webhook",
"events": [
"order.updated",
"renewal.updated",
"proxy.updated"
],
"version": 1
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/webhook设置回调
设置 HTTPS 接收地址和需要订阅的事件。
所需权限webhooks:write
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
Idempotency-Keystring |
header | 必填 | 写入操作必填。8–160 个字符,仅允许英文字母、数字、点、下划线、冒号和连字符。重试保留原键与完全相同的请求体。 |
urlstring |
body | 必填 | HTTPS 地址,最长 2,048 个字符,域名必须可公开解析;仅允许 443 端口,不得包含用户名、密码或 fragment,所有解析地址均需为公网地址。 |
eventsstring[] |
body | 必填 | 从 order.updated、renewal.updated、proxy.updated 中选择 1–3 个不重复值。 |
- 每个 API 密钥管理自己的订阅,需要单独的 webhooks:write 权限,旧密钥不会自动获得。正式环境关闭时,POST 配置返回 403 feature_disabled;已有配置仍可查询或停用。沙盒提供配置与验签文档用于测试,但实际向外发送仍需开启发送功能;配置成功不代表发送已开启。
- 使用配置此回调的 API 密钥对应的 API Secret 验签。X-PuppyIP-Timestamp 为 Unix 秒;对 PuppyIP-Webhook-v1\n<timestamp>\n<原始 JSON 字节> 计算 HMAC-SHA256 小写十六进制,其中每个 \n 为一个 LF 换行字节。用恒定时间比较 X-PuppyIP-Signature,并验证时间戳处于接受窗口、X-PuppyIP-Event-ID 与 body.event_id 相同。先验原始字节,再解析 JSON,不要重新编码后验签。
- 仅 HTTP 200 表示接收成功;已经处理过的 event_id 也应返回 200。最多尝试 10 次,重试保留相同 event_id 与正文,但签名时间戳更新。按 event_id 去重,不假定严格顺序或只发送一次。通知用于提示查询当前 API 状态,不能独立作为付款凭据或再次扣款依据。撤销密钥、停用订阅或更改配置可能取消待发送通知。
- 事件对象包含 event_id、event、environment、occurred_at、observed_at、reason、data,不使用 API 的 success 响应包裹。event 为 order.updated、renewal.updated 或 proxy.updated。同一对象的多次变化可能合并为一条通知;occurred_at 为该通知首次触发时间,observed_at 为通知中状态的观察时间。重试保留相同正文,通知不保证覆盖每一次中间状态。
- order.updated 的 data 包含 order_no、status、payment_status、currency、total_cents;proxy.updated 包含 proxy_id、status、expires_at;renewal.updated 包含 status、currency、total_cents、charged_cents、refunded_cents 及 successful_proxy_ids、failed_proxy_ids、unresolved_proxy_ids。无法确认的续费金额可为 null,不能当作 0。通知不含连接密码或连接串,最新连接需通过有权限的 API 查询。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
POST /api/v1/webhook
Content-Type: application/json
Idempotency-Key: example-configure-webhook-001
{
"url": "https://example.com/puppyip-webhook",
"events": [
"order.updated",
"renewal.updated",
"proxy.updated"
]
}
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.configured | boolean | 当前密钥是否有启用的订阅;单凭此值不能确认外发通知已开启。 |
data.url | string|null | 已配置的回调地址,停用时为 null。 |
data.events | string[] | 订阅的事件名称,停用时为空数组。 |
data.version | integer | 订阅配置版本,配置发生变化时递增。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"configured": true,
"url": "https://example.com/puppyip-webhook",
"events": [
"order.updated",
"renewal.updated",
"proxy.updated"
],
"version": 1
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
/api/v1/webhook停用回调
停用当前 API 密钥的回调订阅。
所需权限webhooks:write
请求参数
| 字段 | 位置 | 要求 | 说明 |
|---|---|---|---|
Idempotency-Keystring |
header | 必填 | 写入操作必填。8–160 个字符,仅允许英文字母、数字、点、下划线、冒号和连字符。重试保留原键与完全相同的请求体。 |
- DELETE 需要正常请求签名、webhooks:write 和 Idempotency-Key。在已完成响应的 24 小时重放期内,使用同一 API 密钥、原幂等键和原请求体会重放这次停用结果,不会停用此后重新配置的订阅;新的停用操作请使用新幂等键。正常的 API 轮询查询不受影响。
请求示例
以下为路径与业务参数;完整请求还需携带签名请求头。 查看签名示例
DELETE /api/v1/webhook
Idempotency-Key: example-disable-webhook-001
返回字段
下表列出完整响应字段;data 内为本接口的业务数据。 查看通用响应
| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 此次 API 操作是否成功。每个数量的业务结果仍需查看订单或续费结果。 |
code | string | 稳定的结果代码,成功时为 ok。 |
message | string | 可读的结果说明,成功时为 OK。 |
data | object | 当前接口的响应对象。 |
timestamp | string | 响应时间,ISO 8601 格式,UTC 时区。 |
trace_id | string | 请求参考编号,联系客服时可提供此值。 |
data.configured | boolean | 当前密钥是否有启用的订阅;单凭此值不能确认外发通知已开启。 |
data.url | string|null | 已配置的回调地址,停用时为 null。 |
data.events | string[] | 订阅的事件名称,停用时为空数组。 |
data.version | integer | 订阅配置版本,配置发生变化时递增。 |
响应示例 HTTP 200
{
"success": true,
"code": "ok",
"message": "OK",
"data": {
"configured": false,
"url": null,
"events": [],
"version": 2
},
"timestamp": "2026-11-09T00:00:00+00:00",
"trace_id": "trc_documentation_example"
}
稳定分页
GET /api/v1/offers?page_size=200
{
"offers": [],
"page_size": 200,
"has_more": true,
"next_page_token": "opaque-token-from-response"
}
请求下一页时,把返回的 next_page_token 原样放入 page_token,并保持相同的 page_size 和筛选条件,例如 GET /api/v1/offers?page_size=200&page_token=...。线路、订单和 IP 列表均使用相同响应字段;线路每页最多 200 条,订单和 IP 的每页上限以接口校验为准。token 绑定当前账号、环境、资源类型和线路筛选条件,篡改、过期、跨账号或更换筛选条件使用都会被拒绝。
以 has_more=false 判断翻页结束;即使 offers 为空或少于 page_size,只要 has_more=true 就继续使用 next_page_token。各页读取当前数据,并非冻结的全目录快照;定期从第一页重新同步,按 offer_code 去重和更新。
订单状态与沙盒场景
创建订单返回 201 后,请使用 PuppyIP 返回的 order_no 查询 GET /api/v1/orders/{order_no}。paid 或 procuring 表示处理中;当 poll_after_seconds 为整数时,等待返回的秒数后再查询,为 null 或订单进入终态时停止轮询。收到 429 时优先遵循 Retry-After。只有 delivery.available=true 时,详情中的 proxies 才可交付客户。
{
"order_no": "SANDBOX_ORDER_EXAMPLE",
"status": "procuring",
"payment_status": "paid",
"delivery": {
"expected_count": 3,
"delivered_count": 1,
"complete": false,
"available": false
},
"poll_after_seconds": 1,
"refund": null,
"proxies": []
}
不传场景头:即时成功并返回全部测试 IP,兼容既有沙盒客户端。
success:正常异步完成并返回全部测试 IP。
delayed:延迟数秒后完成,用于验证轮询界面。
partial:先出现部分进度,再完成全部交付;默认订单响应在未完成前不返回凭据,沙盒代理列表与详情仍可读取已到的测试 IP。开启下方读取模式后,订单响应也会返回已到 IP,并显示剩余待交付数量。
rejected:不交付 IP,测试扣款自动退回测试钱包,refund 会明确返回去向和状态。
部分交付读取(可选)
GET /api/v1/orders/{order_no}
X-PuppyIP-Delivery-Mode: partial
正式环境与沙盒均支持该可选请求头,仅作用于 GET /api/v1/orders、GET /api/v1/orders/{order_no}、GET /api/v1/proxies 和 GET /api/v1/proxies/{proxy_id}。未传该请求头或值不为 partial 时,各环境保留原有响应字段、状态和读取行为:正式环境保留整单交付门禁;沙盒订单响应保留整单交付门禁,沙盒代理列表与详情保留原有按账号和沙盒标识读取测试资源的行为。旧客户端无需修改。不影响 POST 请求、原始 JSON 签名、Idempotency-Key 或历史响应重放。
开启后,已付款且仍属于当前账号的已到 IP 可先行读取;未付款、全额退款、缺失或已转移的资源仍不可取得。delivery.available=true 表示当前有可用资源,delivery.complete=true 才表示整单交付完成。订单可显示 partially_delivered;procurement_blocked 仍表示结果未结清,不应当作整单成功,继续遵循 poll_after_seconds。
仅开启此模式时,订单详情及列表的 delivery 增加 delivery.remaining_count:尚未结清时为原始预期数量减去当前已到数量和已确认单项退款数量,下限为 0;整单完成、全额退款或取消后为 0。历史已完成订单的资源缺失不计为待到货。expected_count 保留原购买数量,部分退款另看 refund。订单列表仍不返回代理凭据。
分页 token 绑定交付读取模式;翻页时保持相同请求头,切换模式须从第一页重新获取,否则返回 422 invalid_page_token。删除已停用的 API Key 不会清除有效期内的已完成响应或尚未结清的幂等记录;换用新 Key 重试仍须保持原 Idempotency-Key 和原始 body,不能借更换凭证重新执行同一笔购买。
常见错误
- invalid_signature
- 签名缺失、App ID 错误、canonical string 拼接不一致,或使用了错误的 API Secret。
- stale_timestamp
- 时间戳没有时区、格式不是 ISO 8601,或客户端时间与服务器相差超过 5 分钟。
- replayed_nonce
- 同一个 API Key 下重复使用了相同 nonce。每次请求都应该生成新的随机 nonce。
- idempotency_key_required
- 要求幂等键的请求缺少 Idempotency-Key,或其长度、字符不符合要求。
- idempotency_key_conflict
- 同一个 Idempotency-Key 已经用于不同请求体。
- idempotency_processing / idempotency_replay_failed
- 相同操作仍在处理中,或首次响应暂时无法安全重放。保持相同 key 和原始 body,按响应建议稍后重试。
- idempotency_unresolved
- HTTP 409。先前请求可能已经产生扣款或其他结果,需要核对;不能把异常或超时当作未执行,也不要换 key 自动重试。
- insufficient_scope
- API Key 没有所需权限,或请求来源 IP 不在允许范围内。
- api_key_revoked
- API Key 已停用。请换用仍有效且具有所需权限的 Key。
- validation_error
- 请求字段、数量、时长或组合不符合接口约束;按 data.errors 修正后再提交。
- offer_not_found / order_not_found / proxy_not_found
- 公共标识不存在、已不可用,或不属于当前账号和环境。请重新读取对应列表。
- insufficient_wallet_balance
- 钱包余额不足,创建订单或续费没有扣款成功。
- invalid_page_token
- 分页 token 已过期、被修改,或用于错误的账号、环境或资源。请从第一页重新获取。
- rate_limited
- 请求过快。读取 Retry-After 响应头后重试;不要用无间隔循环轮询。
- unsupported_operation
- 该操作不属于 Customer API v1,例如更换 IP 或试用 IP。
- proxy_endpoint_unavailable / operation_failed
- 连接端点暂不可用或操作未完成。请保存 trace_id,并按响应状态重试或联系支持。