v1 ổn định

Tài liệu API

Mọi thao tác trên web đều gọi được bằng API. Tất cả endpoint dùng chung một địa chỉ gốc và trả về JSON.

Địa chỉ gốc

Mọi endpoint bắt đầu bằng địa chỉ dưới đây. Chỉ nhận HTTPS.

https://api.ip2.vn/v1

Xác thực

Mỗi port có một key riêng, xem trong trang quản lý. Gửi key qua tham số key trên URL, hoặc qua header nếu bạn muốn giấu key khỏi log.

# cách 1 — tham số URL GET /v1/info?key=ip2_a1b2c3d4e5f6 # cách 2 — header X-IP2-Key: ip2_a1b2c3d4e5f6

Ngoài key của từng port, tài khoản của bạn còn cấp được một token dùng chung cho mọi port — xem mục Token tài khoản. Key hợp khi bạn chia lẻ từng port cho từng người; token hợp khi một chương trình của bạn quản cả dàn.

Mọi key và token trong tài liệu này đều là chuỗi ví dụ. ip2_a1b2c3d4e5f6, ip2a_xxxxxxxxxxxxxxxx và rsl_xxxxxxxxxxxx chỉ để cho thấy hình dạng — thay bằng chuỗi thật của bạn thì mới gọi được, gọi nguyên như trong tài liệu sẽ nhận invalid_key hoặc invalid_token.
Giữ key cẩn thận. Ai có key là đổi được IP của bạn và xem được lịch sử. Nếu lộ, bấm tạo key mới trong trang quản lý, key cũ mất hiệu lực ngay.

Cách kết nối proxy

Port hỗ trợ cả HTTP và SOCKS5 trên cùng một cổng. Đăng nhập bằng tài khoản mật khẩu, hoặc thêm IP máy bạn vào danh sách trắng rồi kết nối thẳng không cần mật khẩu.

Địa chỉ kết nối lấy từ trường host của /v1/info, hoặc xem trong trang quản lý. Ví dụ dưới dùng 113.161.94.28 cho dễ hình dung.

# có mật khẩu http://user:pass@113.161.94.28:20007 socks5://user:pass@113.161.94.28:20007 # danh sách trắng IP http://113.161.94.28:20007
Địa chỉ kết nối có thể đổi. Máy đang giữ port gặp sự cố (mất điện, đứt đường truyền) thì hệ thống tự chuyển port sang máy khác còn cổng trống: host đổi, port giữ nguyên nếu máy mới còn trống đúng số đó. Key, tài khoản và mật khẩu proxy giữ nguyên, không phải sửa gì. Nên tool đừng lưu cứng địa chỉ: gọi /v1/info lấy host và port trước mỗi phiên, và khi proxy không kết nối được thì gọi lại /v1/info trước khi báo lỗi. Trường migrated_at đổi giá trị nghĩa là port vừa được chuyển máy. Tài khoản của bạn cũng nhận một thông báo ghi địa chỉ mới, nhưng tool chạy bằng API không đọc được thông báo đó — đừng trông vào nó.

Thư viện SDK

Có SDK chính thức cho JavaScript và Python, bọc sẵn mọi endpoint bên dưới, không phụ thuộc thư viện ngoài. Cài một dòng là gọi được.

# JavaScript · Node 18+ npm install ip2vn # Python · 3.8+ pip install ip2vn
// JavaScript import { IP2 } from 'ip2vn'; const cong = new IP2('ip2_a1b2c3d4e5f6'); const tin = await cong.info(); await cong.rotate();

Bên làm tool nhúng mã giới thiệu công khai để ăn hoa hồng giới thiệu — cách làm có trong README của gói ip2vn. Đừng nhúng key hay token vào tool phát đi. Muốn tự bán proxy cho khách thì xem API đại lý.

Cài trên npm Cài trên PyPI

Xem trạng thái port

Trả về IP hiện tại, cổng, hạn dùng và tình trạng hẹn giờ. Gọi bao nhiêu lần cũng được.

GET/v1/infokhông giới hạn
GET /v1/info?key=ip2_a1b2c3d4e5f6 { "ok": true, "host": "113.161.94.28", "port": 20007, "line": "S1", "ip": "113.161.94.28", "protocol": ["http", "socks5"], "auto": { "enabled": true, "every": 300, "next_in": 142 }, "cooldown": 0, "expire_at": "2026-10-04T23:59:59+07:00", "rotations": 1482, "migrated_at": null }

line là tên máy đang chạy port của bạn, đúng như tên hiện trong bảng điều khiển: S1, S2… Mỗi máy là một đường truyền Viettel riêng.

Trường trả về

TrườngÝ nghĩa
hostĐịa chỉ kết nối tới port. Có thể đổi, nên đọc lại mỗi phiên.
ipIP đang dùng ngay lúc gọi
cooldownSố giây còn phải chờ mới xoay được. Bằng 0 là xoay được ngay.
auto.next_inCòn bao nhiêu giây nữa tới lần tự xoay kế tiếp
expire_atThời điểm port hết hạn
rotationsTổng số lần đã xoay từ khi thuê
migrated_atLần gần nhất port được tự chuyển sang máy khác vì sự cố, null là chưa bao giờ. Giá trị đổi nghĩa là host (có thể cả port) đã đổi, tool phải nối lại bằng địa chỉ mới.

Đổi IP

Xin IP mới từ nhà mạng. Lệnh chờ tới khi port ra IP mới rồi mới trả về — thường 20–45 giây, tối đa 90 giây, nên đặt hạn chờ của tool từ 100 giây. Quá 90 giây mà máy vẫn đang quay lại (tổng đài trả đúng IP cũ) thì lệnh trả về với ip bằng previous_ip; port vẫn quay tiếp, gọi /v1/info sau ít phút để thấy IP mới. Trong lúc đổi, kết nối cũ bị ngắt nên tool cần chờ rồi thử lại.

GET/v1/rotatecách nhau tối thiểu 180 giây
GET /v1/rotate?key=ip2_a1b2c3d4e5f6 { "ok": true, "ip": "113.161.201.7", "previous_ip": "113.161.94.28", "host": "s1.poolviettel.com", "port": 20007, "line": "S1", "took": 31.6, "next_allowed_in": 180 }

host, port và line là địa chỉ kết nối ngay lúc xoay. Tool chỉ gọi xoay mà không gọi /v1/info thì so hai trường này với địa chỉ đang dùng: khác nhau là port đã được chuyển sang máy khác.

Số lần xoay không giới hạn, nhưng hai lần phải cách nhau ít nhất 180 giây. Gọi sớm hơn sẽ nhận lỗi 429 cooldown kèm số giây còn phải chờ trong retry_after. Lượt xoay trước vẫn đang chạy (máy quay tiếp tới khi ra IP khác, có thể mất vài phút) thì nhận 429 rotate_dang_chay — đợi nó xong rồi gọi lại, đừng gửi chồng.

Tham số

TênBắt buộcMô tả
keycóKey của port
waitkhôngĐặt false để trả về ngay, không chờ IP mới. Mặc định là true.

Hẹn giờ tự xoay

Đặt chu kỳ để port tự đổi IP mà không cần gọi lệnh. Chu kỳ tính bằng giây, thấp nhất là 180.

POST/v1/autotối thiểu 180 giây
POST /v1/auto { "key": "ip2_a1b2c3d4e5f6", "every": 300 } { "ok": true, "auto": true, "every": 300, "next_at": "2026-09-04T14:22:05+07:00" } # tắt hẹn giờ POST /v1/auto { "key": "ip2_a1b2c3d4e5f6", "every": 0 }
TênBắt buộcMô tả
keycóKey của port
everycóChu kỳ tính bằng giây, từ 180 trở lên. Đặt 0 để tắt.

Lịch sử IP

Danh sách IP mà port đã đi qua, mới nhất trước. Dùng để đối chiếu tránh trùng khi nuôi tài khoản dài hạn.

GET/v1/historytối đa 500 dòng mỗi lần
GET /v1/history?key=ip2_a1b2c3d4e5f6&limit=3 { "ok": true, "total": 1482, "items": [ { "ip": "113.161.201.7", "at": "2026-09-04T14:17:11+07:00" }, { "ip": "113.161.94.28", "at": "2026-09-04T14:12:09+07:00" }, { "ip": "113.161.77.140", "at": "2026-09-04T14:07:06+07:00" } ] }
TênBắt buộcMô tả
limitkhôngSố dòng trả về, mặc định 50, cao nhất 500
sincekhôngChỉ lấy từ mốc thời gian này trở đi, dạng ISO 8601

Gia hạn

Trừ tiền trong ví để kéo dài hạn dùng của port, days nhận 1, 7 hoặc 30. Hạn mới cộng dồn từ hạn cũ. Nếu ví không đủ, lệnh trả lỗi 402 và không trừ gì cả.

Port ca ngày / ca đêm cũng gia hạn được: gia hạn tính theo ngày và chuyển port sang ca 24 giờ (chuyen_sang_24h: true), không gia hạn lại theo ca. Gia hạn bằng key, bằng token hay bấm trên web đều cùng một giá, cùng một luật, và máy chạy port nhận hạn mới ngay.

Gọi lại an toàn: gửi kèm idempotency_key (chuỗi 8–100 ký tự do bạn đặt, ví dụ mã đơn) — rớt mạng rồi gọi lại với cùng khoá thì chỉ bị trừ tiền một lần, lần sau trả lại đúng kết quả lần trước.

POST/v1/renew
POST /v1/renew { "key": "ip2_a1b2c3d4e5f6", "days": 30, "idempotency_key": "gia-han-0915-a" } { "ok": true, "days_added": 30, "charged": 189600, "wallet_balance": 810400, "shift": "ca_24h", "chuyen_sang_24h": false, "expire_at": "2026-11-03T23:59:59+07:00" }

Token tài khoản

Key ở trên chỉ mở đúng một port. Nếu bạn thuê nhiều port và muốn một chương trình quản hết, hãy dùng token cấp ở cấp tài khoản: một chuỗi duy nhất điều khiển mọi port bạn đang thuê, port mua thêm tự nằm trong tầm với mà không phải sửa lại mã nguồn.

Tạo token trong bảng điều khiển, mục API của tôi. Chuỗi thô chỉ hiện đúng lần tạo — máy chủ chỉ lưu bản băm, không đọc lại được từ bất kỳ đường nào.

Gọi được từ mọi tên miền. Nhóm endpoint dành cho khách mở CORS cho mọi nguồn, nên JavaScript chạy trong trình duyệt trên web của bạn gọi thẳng được, không cần dựng thêm một tầng trung gian phía máy chủ.
Nhưng đừng nhúng token vào trang gửi tới người dùng cuối. Ai bấm F12 cũng đọc được, và người đó điều khiển được mọi port bạn đang thuê — xoay IP, đổi hẹn giờ, gia hạn trừ ví. Nhúng vào trang nội bộ chỉ mình bạn mở thì không sao; trang cho khách của bạn xem thì token phải nằm ở máy chủ, trình duyệt gọi về máy chủ bạn rồi máy chủ bạn gọi sang đây.
Token không chạm được vào tiền. Nó xem và điều khiển port, nhưng không mở được ví, không rút được tiền, không đổi được mật khẩu và không mua thêm port. Lộ token thì bạn mất quyền điều khiển port chứ không mất tiền trong ví.
# cách 1 — header chuẩn Authorization: Bearer ip2a_xxxxxxxxxxxxxxxx # cách 2 — header riêng X-IP2-Token: ip2a_xxxxxxxxxxxxxxxx # cách 3 — tham số URL, tiện nhưng lọt vào nhật ký máy trung gian GET /v1/api/ports?token=ip2a_xxxxxxxxxxxxxxxx
GET/v1/api/portsmọi port đang thuê
GET /v1/api/ports { "ok": true, "data": { "tong": 2, "items": [ { "id": "3f2a…", "port": 20007, "host": "s1.poolviettel.com", "ip_goc": "113.161.94.28", "node": "S1", "proxy_user": "ip2user", "shift": "ca_24h", "ten_ca": "ca 24 giờ", "expires_at": "2026-10-04T23:59:59+07:00", "auto_every": 300, "tranh_ip_24h": true, "con_cho_giay": 0, "so_lan_xoay": 1482 } ] } }

Danh sách rỗng vẫn trả ok: true — chưa thuê port nào là trạng thái hợp lệ, không phải lỗi. Mọi câu trả lời của nhóm này đều bọc trong data.

POST/v1/api/ports/:id/rotatecách nhau tối thiểu 180 giây
POST /v1/api/ports/3f2a…/rotate { "ok": true, "data": { "command_id": "8c41…", "trang_thai": "pending" } } # cả trăm port bằng MỘT lời gọi, đừng gọi từng cái POST /v1/api/ports/rotate-nhieu { "rental_ids": ["3f2a…", "9b71…"] }

id lấy từ trường id của /v1/api/ports. Trần gọi nhân theo số port bạn thuê: mỗi port thêm 40 lượt ghi và 120 lượt gọi mỗi phút, nên khách trăm port vẫn xoay hàng loạt thoải mái miễn là gọi gộp.

Các endpoint khác

EndpointViệc
GET /v1/api/ports/:idMột port
GET /v1/api/ports/:id/credentialsTài khoản proxy của port: proxy_user và proxy_pass. Lấy một lần lúc dựng kết nối, đừng gọi mỗi vòng lặp
PATCH /v1/api/ports/:id/auto-rotateHẹn giờ tự xoay: giay từ 180 trở lên, 0 là tắt
PATCH /v1/api/ports/:id/kho-ipBật tắt kho IP riêng 24 giờ: bat true hoặc false
PATCH /v1/api/ports/:id/loc-khoLọc hai kho cho lượt xoay: kho1_giay (null tắt · 0 vĩnh viễn · >0 số giây nhìn lại) và kho2 (true/false — hỏi API ngoài chấm IP sạch). Mặc định tắt cả hai.
PATCH /v1/api/ports/:id/auto-renewBật tắt tự gia hạn: auto_renew và so_ngay
POST /v1/api/ports/:id/renewGia hạn: so_ngay nhận 1, 7 hoặc 30 — trừ ví chính
GET /v1/api/ip-historyLịch sử IP, lọc một port bằng rental_id
GET /v1/api/ports/:id/loc-ten-mienTên miền port đang lọc, kèm số IP đang bị tránh
POST /v1/api/ports/:id/loc-ten-mienThêm một tên miền vào bộ lọc: domain
DELETE /v1/api/ports/:id/loc-ten-mienBỏ một tên miền khỏi bộ lọc: domain
POST /v1/api/ports/:id/diem-an-danhChấm điểm ẩn danh phần IP: IP đi ra, đường DNS, nhãn proxy, danh sách đen, ba mốc độ trễ
POST /v1/api/ports/diem-an-danh-nhieuChấm nhiều cổng trong MỘT lời gọi: rental_ids là mảng id, tối đa 60
Kho IP riêng. Bật kho-ip thì lệnh xoay mang theo những IP port đó đã ra trong 24 giờ qua, node gặp lại một trong số đó sẽ quay tiếp thay vì nhận. Tổng đài cấp lại IP cũ là chuyện thường của mạng dân cư, đây là cách quay giùm bạn.
Bộ lọc traffic 24 giờ. Kho IP riêng chỉ nhớ IP của chính port bạn. Bộ lọc traffic nhìn rộng hơn: chọn x.com cho một port thì mỗi lượt xoay của port đó bỏ qua mọi IP mà bất kỳ port nào trong hệ thống đã dùng để vào x.com trong 24 giờ qua — bên kia nhận ra IP chứ không nhận ra ai đang thuê nó. Quá 24 giờ thì IP được dùng lại bình thường. Mỗi port lọc tối đa 3 tên miền.
Chấm điểm ẩn danh. Mở một kết nối thật đi qua chính cổng đó rồi đo bốn mặt — IP đi ra có đúng là IP riêng của cổng không, IP có bị gắn nhãn proxy hay máy chủ thuê không, DNS có rò ra ngoài không, và tên miền ngược có giống đường dân cư không — cộng bốn danh sách đen thư rác. Kèm ba mốc độ trễ: ping_cong_ms (máy chủ IP2 tới máy chạy port), ping_trong_nuoc_ms (qua cổng tới máy chủ trong nước khác nhà mạng) và ping_quoc_te_ms (qua cổng tới máy chủ thật ở châu Âu hoặc Mỹ, không phải CDN). Số thường thấy trên đường Viettel: 9 · 23 · 250 ms. Có cả trăm port thì gọi diem-an-danh-nhieu, đừng gọi từng cái.
# lọc x.com cho một port POST /v1/api/ports/3f2a…/loc-ten-mien { "domain": "x.com" } # xem port đang lọc gì và đang tránh bao nhiêu IP GET /v1/api/ports/3f2a…/loc-ten-mien { "ok": true, "data": { "items": [ { "domain": "x.com", "so_ip_tranh": 37 } ], "toi_da": 20 } }

Mã lỗi

Lỗi luôn trả về ok: false kèm error dạng chữ và message tiếng Việt để bạn hiện thẳng cho người dùng.

{ "ok": false, "error": "cooldown", "message": "Cần chờ thêm 132 giây mới xoay được.", "retry_after": 132 }
MãerrorNghĩa
401invalid_keyKey sai hoặc đã bị thay
402insufficient_fundsVí không đủ tiền để gia hạn
403ip_not_allowedIP gọi API không nằm trong danh sách trắng
404port_not_foundPort không tồn tại
409expiredPort đã hết hạn, cần gia hạn trước
429cooldownChưa đủ 180 giây kể từ lần xoay trước
400invalid_daysThời hạn chỉ nhận 1, 7 hoặc 30 ngày
400invalid_everyChu kỳ tự xoay phải là 0 hoặc từ 180 giây trở lên
400invalid_sinceTham số since phải theo dạng ISO 8601
400too_many_ipsDanh sách trắng tối đa 20 IP
503line_downMáy giữ port mất liên lạc, lệnh xoay không gửi xuống được. Riêng một đường rớt trong khi máy còn sống thì vẫn xoay được — xoay chính là cách đường đó lên lại
400invalid_ipĐịa chỉ IP trong danh sách trắng không hợp lệ
400ca_ngay_khong_gia_hanPort ca lẻ không bật tự gia hạn được (hết ca là hết) — gia hạn tay một lần là chuyển sang ca 24 giờ, rồi bật
429rotate_dang_chayCổng đang xoay dở — node quay tiếp tới khi ra IP khác, đừng gửi lệnh thứ hai
400ten_mien_khong_hop_leGõ dạng x.com, đừng gõ cả đường dẫn
400qua_nhieu_ten_mienMỗi port lọc tối đa 3 tên miền, bỏ bớt rồi thêm
503node_chua_san_sangMáy chứa port chưa báo địa chỉ về, thử lại sau ít phút
503khong_ra_duocPort chưa ra được Internet nên chưa chấm điểm ẩn danh được

Riêng token tài khoản

MãerrorNghĩa
401invalid_tokenToken sai hoặc đã bị thu hồi
403account_suspendedTài khoản đang bị khoá
400qua_nhieu_tokenĐã đạt trần token đang dùng, thu hồi bớt token cũ

Mã lỗi riêng của nhóm đại lý (not_reseller, out_of_stock, key_not_found…) nằm ở trang API đại lý.

Ví dụ hoàn chỉnh

Xoay IP rồi chờ tới khi kết nối được, mẫu này xử lý sẵn cả trường hợp gặp cooldown.

rotate.py
import requests, time KEY = "ip2_a1b2c3d4e5f6" API = "https://api.ip2.vn/v1" def rotate(): r = requests.get(f"{API}/rotate", params={"key": KEY}, timeout=30).json() if not r["ok"]: if r["error"] == "cooldown": time.sleep(r["retry_after"]) return rotate() raise Exception(r["message"]) return r["ip"] ip = rotate() proxies = {"http": f"http://user:pass@113.161.94.28:20007"} print(ip, requests.get("https://ifconfig.me", proxies=proxies).text)
rotate.js
const KEY = "ip2_a1b2c3d4e5f6"; const API = "https://api.ip2.vn/v1"; const sleep = ms => new Promise(r => setTimeout(r, ms)); async function rotate() { const res = await fetch(`${API}/rotate?key=${KEY}`); const d = await res.json(); if (!d.ok) { if (d.error === "cooldown") { await sleep(d.retry_after * 1000); return rotate(); } throw new Error(d.message); } return d.ip; } rotate().then(console.log);
bash
# đổi IP curl "https://api.ip2.vn/v1/rotate?key=ip2_a1b2c3d4e5f6" # hẹn giờ tự xoay mỗi 5 phút curl -X POST "https://api.ip2.vn/v1/auto" \ -H "Content-Type: application/json" \ -d '{"key":"ip2_a1b2c3d4e5f6","every":300}' # kiểm tra IP thật sự đi qua proxy curl -x http://user:pass@113.161.94.28:20007 https://ifconfig.me