Products
Products are identified by a numeric ID. This endpoint is backed by the same service as the web console, so pricing and limits match exactly what the console uses. The table below is fetched live — treat the API as the source of truth and do not hardcode this snapshot.
/api/open/v1/productsAvailable products, pricing, size limits and country requirementsThis response comes from the same backend service that powers the web console, so the IDs, pricing, size limits and country requirements you see here are exactly what the console uses.
Fields
| Field | Type | Description |
|---|---|---|
| id | integer | The public product ID, passed as `type` when creating a task. Once published an ID is never reused and never repointed |
| unit_price | number | USD per single number (not per thousand). Charge = effective count × this value |
| min_numbers | integer | Minimum per task, measured on the effective count after normalisation, de-duplication and country filtering; below it the task is rejected |
| max_numbers | integer | Maximum per task. Split larger inputs into several tasks yourself |
| visible | boolean | false means it is not available to your account and will be rejected. Filter on this field rather than hardcoding a list of usable IDs |
| country_required | boolean | true means `country` is required when creating a task; false (email or username products) means any value is ignored |
| allowed_countries | string[] | Country allowlist (uppercase ISO-2). Non-empty means only these countries are accepted; an empty array means unrestricted (see blocked_countries instead) |
| blocked_countries | string[] | Country blocklist (uppercase ISO-2). Non-empty means every country except these is accepted; an empty array means unrestricted. Mutually exclusive with allowed_countries — the two are never both non-empty |
| result_columns | object[] | Columns of this product's result file, in CSV column order; each entry is { name, facet } |
| result_columns[].name | string | Column name, i.e. the header in the result CSV (phone, activated, gender, …) |
| result_columns[].facet | boolean | Whether this column can be used to filter result downloads (maps to parameters such as active_day and sex) |
| price_display_unit | integer | The N used by the console to quote a price per N numbers. Display only, never part of any calculation (a top-level field, not inside products) |
Reading the country restrictions
The two lists are mutually exclusive and never both non-empty — the backend refuses such a configuration at startup. So the logic is simple:
allowed_countries 非空Only countries in the list are accepted; everything else is rejectedblocked_countries 非空Every country is accepted except those listedBoth are empty arraysNo country restrictionThe lists reflect what the product's current upstream channel supports; they change when the supplier changes, so read them from the API each time instead of caching or hardcoding. Products with country_required = false (email and username products) ignore both fields.
Response shape
{
"products": [
{
"id": 19,
"unit_price": 0.0025, // USD/条(注意:每一条的单价,不是每千条)
"min_numbers": 1000,
"max_numbers": 10000000,
"visible": true,
"country_required": true,
"allowed_countries": ["US", "CA"], // 空数组 = 不限
"blocked_countries": [],
"result_columns": [ { "name": "phone", "facet": false } ]
}
],
"price_display_unit": 1000 // 控制台按"每 N 条"展示价格,仅影响展示
}About product names
The endpoint returns IDs without names: a name is localized display text that changes with the interface language and is not part of the API contract. Use the ID as the identifier in your code, and keep your own ID → name mapping where you need something human-readable.
Products available to your account
Stored only in your browser. It is never sent to any server of ours.