SDK & Ví dụ
SDK chính thức của Tino cho Python, Node.js và PHP, mã nguồn mở ở tinovn/tino-sdk (giấy phép MIT). Mỗi route có một hàm client.<nhóm>.<thao tác>(); SDK mã hoá query kiểu filter[key]=value, đọc lỗi trong body (kể cả HTTP 200 kèm error) và ném lỗi có kiểu.
Cập nhật: 2026-10-27.
Cài đặt
| Ngôn ngữ | Lệnh cài | Phiên bản đang phát hành | Yêu cầu |
|---|---|---|---|
| Python | pip install tino-userapi | 1.2.0 (PyPI) | Python 3.8+, không phụ thuộc gói khác |
| Node.js | npm install @tinovn/tino-sdk | 1.1.0 (npm) | Node.js 18+, ESM, có định nghĩa TypeScript, không phụ thuộc gói khác |
| PHP | composer require tinovn/tino-sdk | 1.1.0 (Packagist) | PHP 7.4+, PSR-4, không phụ thuộc gói khác |
Khởi tạo client
Trỏ SDK vào gateway https://api.tino.vn/v1 và truyền token: token API cá nhân (tinopat_) hoặc access token của Đăng nhập bằng Tino (tinoat_). Token đọc từ biến môi trường TINO_TOKEN, không ghi vào mã nguồn.
import os
from tino_userapi import TinoClient
client = TinoClient(base_url="https://api.tino.vn/v1", token=os.environ["TINO_TOKEN"])
print(client.account.get_details())import { TinoClient } from "@tinovn/tino-sdk";
const client = new TinoClient({ baseUrl: "https://api.tino.vn/v1", token: process.env.TINO_TOKEN });
console.log(await client.account.getDetails());<?php
require "vendor/autoload.php";
$client = new Tino\UserApi\TinoClient(["baseUrl" => "https://api.tino.vn/v1", "token" => getenv("TINO_TOKEN")]);
print_r($client->account->getDetails());Không truyền refresh token OAuth cho SDK: SDK tự làm mới theo cách đăng nhập riêng của nó, không qua /oauth/token. Access token OAuth hết hạn (mặc định 24 giờ) thì ứng dụng làm mới token rồi tạo client với token mới.
Gọi một route
Mỗi trang route ở API Reference có đoạn gọi SDK của route đó, ở cột Ví dụ yêu cầu. Tham số đường dẫn là đối số theo thứ tự; body là đối số tên (Python), đối tượng (Node.js) hoặc mảng (PHP). Ví dụ thêm bản ghi DNS (quyền dns:write):
client.dns.create_record("100003", "500001", name="www", type="A", content="203.0.113.10", ttl=3600, priority=0)await client.dns.createRecord("100003", "500001", { name: "www", type: "A", content: "203.0.113.10", ttl: 3600, priority: 0 });$client->dns->createRecord("100003", "500001", ["name" => "www", "type" => "A", "content" => "203.0.113.10", "ttl" => 3600, "priority" => 0]);Route chưa có hàm riêng
Một số route của gateway chưa có hàm trong SDK (trang route ghi rõ). Gọi bằng request với đường dẫn không có tiền tố /v1 (base URL đã có):
records = client.request("GET", "/domain/200001/dns")const records = await client.request("GET", "/domain/200001/dns");$records = $client->request("GET", "/domain/200001/dns");Body truyền qua body= (Python), { body: … } (Node.js) hoặc ["body" => …] (PHP); query qua query.
Lỗi
| Tình huống | Python, Node.js | PHP |
|---|---|---|
HTTP 401 invalid_token (token sai, hết hạn, đã thu hồi) | TinoAuthError | TinoAuthException |
HTTP 403 insufficient_scope, ip_not_allowed | TinoApiError | TinoApiException |
| Không tìm thấy | TinoNotFoundError | TinoNotFoundException |
| Dữ liệu gửi lên sai | TinoValidationError | TinoValidationException |
| Bị giới hạn tần suất | TinoRateLimitError | TinoRateLimitException |
| Lỗi mạng, hết thời gian chờ | TinoTransportError | TinoTransportException |
Mọi lỗi kế thừa TinoError (TinoException ở PHP); lỗi từ API mang mã lỗi gốc (ví dụ insufficient_scope).
Ví dụ: dịch vụ sắp hết hạn
Liệt kê dịch vụ đang hoạt động hết hạn trong 30 ngày tới (quyền services:read):
import os
from datetime import date, timedelta
from tino_userapi import TinoClient
client = TinoClient(base_url="https://api.tino.vn/v1", token=os.environ["TINO_TOKEN"])
soon = (date.today() + timedelta(days=30)).isoformat()
for service in client.services.list()["services"]:
if service["status"] == "Active" and service["expires"] <= soon:
print(service["id"], service["domain"], service["name"], service["expires"])Ví dụ: hoá đơn chưa thanh toán
In các hoá đơn đến hạn (quyền billing:read):
import { TinoClient } from "@tinovn/tino-sdk";
const client = new TinoClient({ baseUrl: "https://api.tino.vn/v1", token: process.env.TINO_TOKEN });
const { invoices_due: due } = await client.billing.listInvoicesDue();
for (const invoice of due) console.log(invoice.number, invoice.total, invoice.currency, invoice.duedate);Ví dụ: cập nhật bản ghi A khi IP đổi
Trỏ home.example.com về IP mới, kiểu DNS động (quyền dns:read, dns:write). Vùng DNS lấy theo tên miền; zone_id là domain_id của vùng. Khi đọc, tên bản ghi đầy đủ (home.example.com); khi thêm, sửa, gửi tên tương đối (home):
import os
from tino_userapi import TinoClient
client = TinoClient(base_url="https://api.tino.vn/v1", token=os.environ["TINO_TOKEN"])
new_ip = "203.0.113.25"
zone = next(z for z in client.dns.list_zones_all()["zones"] if z["name"] == "example.com")
records = client.dns.get_zone(zone["service_id"], zone["domain_id"])["records"].values()
record = next(r for r in records if r["type"] == "A" and r["name"] == "home.example.com")
if record["content"] != new_ip:
client.dns.update_record(zone["service_id"], zone["domain_id"], record["id"],
name="home", type="A", content=new_ip, ttl=record["ttl"], priority=record["priority"])Các ví dụ đọc dữ liệu theo dạng mẫu response trên trang route; chạy thử với token chỉ đủ quyền cần dùng, xem Môi trường thử nghiệm.