API là gì? Cách sử dụng API tối ưu: từ khái niệm đến xác thực, thử lại và kiểm soát chi phí

Giải thích API bằng ví dụ đời thường, rồi đi qua 8 nguyên tắc dùng API ở mức sản xuất: thời gian chờ, thử lại có lùi thời gian, khóa bất biến, phân trang, webhook và theo dõi, kèm mã mẫu chạy được.
Gần như mọi thứ bạn dùng hằng ngày đều chạy bằng API: ứng dụng thanh toán gọi ngân hàng, website bán hàng gọi đơn vị vận chuyển, chatbot gọi mô hình AI. Hiểu API là gì giúp bạn nói chuyện đúng với đội kỹ thuật, còn biết dùng API tối ưu giúp hệ thống nhanh hơn, rẻ hơn và ít sự cố hơn.
Bài này đi từ khái niệm đến những nguyên tắc dùng API ở mức sản xuất: xác thực an toàn, mã trạng thái, phân trang, giới hạn tốc độ, thử lại, khóa bất biến, webhook và theo dõi.
Các tiêu chuẩn được nhắc tới (RFC 9110, RFC 6585, RFC 9457, RFC 6749) là tài liệu của IETF, phần ví dụ về khóa bất biến dựa trên tài liệu của Stripe, còn cách sắp xếp thứ tự ưu tiên là kinh nghiệm của Click Star.
Điểm chính
- API (giao diện lập trình ứng dụng) là bản hợp đồng cho biết một phần mềm có thể yêu cầu phần mềm khác làm gì, bằng định dạng nào, và nhận lại gì. Phần lớn API web chạy trên HTTP và trao đổi dữ liệu JSON.
- Cặp thông tin quan trọng nhất của mỗi cuộc gọi là phương thức (GET, POST...) và mã trạng thái (2xx thành công, 4xx lỗi do người gọi, 5xx lỗi do máy chủ).
- Dùng API tối ưu nghĩa là: đặt thời gian chờ, thử lại đúng loại lỗi với thời gian lùi tăng dần, dùng khóa bất biến cho thao tác ghi, phân trang bằng con trỏ, giảm số cuộc gọi bằng bộ nhớ đệm, và nhận webhook thay vì hỏi liên tục.
- Khóa API là bí mật. Chỉ để ở phía máy chủ, cấp quyền tối thiểu, tách theo môi trường, và thu hồi ngay khi nghi ngờ lộ.
- Cần đo: độ trễ, tỷ lệ lỗi, chi phí và mã yêu cầu cho từng cuộc gọi, để biết hệ thống đang khỏe hay không.

API là gì
API là viết tắt của Application Programming Interface. Hình dung đơn giản: bạn ngồi ở nhà hàng, thực đơn là danh sách những gì bạn được gọi, người phục vụ nhận yêu cầu mang vào bếp rồi mang món ra. Bạn không cần biết bếp nấu thế nào. API là "thực đơn và người phục vụ" của một phần mềm: nó cho bạn biết được hỏi những gì, hỏi bằng cách nào, và cam kết trả về cái gì.
Có nhiều loại API (của hệ điều hành, của thư viện lập trình), nhưng khi người ta nói "dùng API" trong kinh doanh thì thường là API web: bạn gửi một yêu cầu HTTP tới một địa chỉ (endpoint) và nhận về phản hồi, thường là JSON. Một cuộc gọi gồm 4 phần: địa chỉ và phương thức (làm gì, với cái gì), tiêu đề (header: ai gọi, định dạng gì), nội dung (body: dữ liệu gửi đi, nếu có) và phản hồi (mã trạng thái cộng nội dung trả về).
# Lấy đơn hàng số 1042
curl https://api.example.com/v1/orders/1042 \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json"
# Phản hồi: mã trạng thái 200 và nội dung JSON
# { "id": 1042, "status": "paid", "total": 150000, "currency": "VND" }
Những khái niệm cần nắm
Phương thức HTTP và tính "an toàn", "bất biến"
RFC 9110 (ngữ nghĩa của HTTP) định nghĩa các phương thức. GET đọc dữ liệu và là phương thức an toàn, không làm thay đổi gì. PUT thay thế toàn bộ một tài nguyên và DELETE xóa nó; cả hai đều bất biến (idempotent), nghĩa là gọi 2 lần cho kết quả cuối giống gọi một lần.
POST thường dùng để tạo mới và không bất biến: gọi 2 lần có thể tạo 2 bản ghi. PATCH (định nghĩa ở RFC 5789) sửa một phần và cũng không được bảo đảm bất biến. Chính điều này quyết định bạn có được thử lại tự động hay không, như phần dưới sẽ nói.
Mã trạng thái
Mã trạng thái cho bạn biết cuộc gọi đi tới đâu. Nhóm 2xx là thành công (200 thành công, 201 đã tạo, 204 thành công không có nội dung). Nhóm 3xx là chuyển hướng hoặc "chưa đổi" (304).
Nhóm 4xx là lỗi do phía gọi: 400 yêu cầu sai, 401 chưa xác thực, 403 không đủ quyền, 404 không tìm thấy, 409 xung đột, 422 dữ liệu không hợp lệ, 429 gọi quá nhiều (RFC 6585). Nhóm 5xx là lỗi phía máy chủ: 500 lỗi chung, 502 và 503 dịch vụ trung gian hoặc quá tải.
Quy tắc dùng: lỗi 4xx (trừ 429) là lỗi của bạn, thử lại y nguyên sẽ vẫn lỗi, hãy sửa yêu cầu. Lỗi 429 và 5xx thường là tạm thời, có thể thử lại một cách có kiểm soát.

Webhook, GraphQL, gRPC
Webhook đảo chiều cuộc gọi: thay vì bạn hỏi liên tục "đơn đã thanh toán chưa", nhà cung cấp gọi vào địa chỉ của bạn khi có sự kiện. GraphQL cho phép bạn chọn đúng các trường cần lấy trong một cuộc gọi, còn gRPC dùng giao thức nhị phân hiệu quả cho giao tiếp giữa các dịch vụ nội bộ. REST qua HTTP và JSON vẫn là lựa chọn phổ biến nhất cho API công khai.
Xác thực và bảo mật
- Khóa API (API key): đơn giản, phù hợp gọi từ máy chủ của bạn. Đừng bao giờ nhúng khóa vào mã chạy trên trình duyệt hoặc ứng dụng di động, và đừng đưa vào kho mã. Dùng biến môi trường hoặc kho bí mật, tách khóa theo môi trường (thử nghiệm và thật), và chọn khóa có quyền hẹp nhất có thể.
- OAuth 2.0 (RFC 6749): dùng khi ứng dụng của bạn hành động thay mặt người dùng, ví dụ đọc lịch hoặc email của họ, mà không giữ mật khẩu của họ. Mã truy cập thường được gửi dưới dạng bearer token (RFC 6750) trong header Authorization và có thời hạn ngắn.
- Luôn dùng HTTPS, kiểm tra và làm sạch dữ liệu đầu vào, và không ghi khóa hay mã truy cập vào nhật ký.
- Hãy coi mọi khóa từng xuất hiện trong mã nguồn công khai là đã lộ: thu hồi và cấp khóa mới, đừng chỉ xóa dòng chứa khóa khỏi kho mã.
- Với API do chính bạn cung cấp: đặt giới hạn tốc độ theo khóa, xác thực quyền ở từng tài nguyên, và trả lỗi đủ thông tin để người gọi sửa được nhưng không lộ chi tiết nội bộ.
Dùng API tối ưu: 8 nguyên tắc
1. Đọc giới hạn trước khi viết mã
Mỗi API có giới hạn về số yêu cầu mỗi phút, kích thước dữ liệu, số bản ghi mỗi trang và phiên bản được hỗ trợ. Biết trước giới hạn giúp bạn thiết kế đúng ngay từ đầu thay vì vá sau khi sự cố xảy ra.
2. Đặt thời gian chờ cho mọi cuộc gọi
Cuộc gọi không có thời gian chờ có thể treo mãi và kéo sập cả hệ thống của bạn. Đặt thời gian chờ cụ thể (ví dụ 10 giây cho yêu cầu thông thường) và quyết định trước sẽ làm gì khi hết giờ.
3. Thử lại đúng cách
Chỉ thử lại lỗi tạm thời: lỗi mạng, quá thời gian chờ, 429 và 5xx. Đừng thử lại 400, 401, 403, 404. Giãn khoảng cách giữa các lần thử theo cấp số nhân (0,5 giây, 1 giây, 2 giây...), cộng một chút ngẫu nhiên để hàng nghìn máy khách không cùng gọi lại một lúc. Nếu phản hồi có header Retry-After (RFC 9110), hãy chờ đúng thời gian đó. Luôn giới hạn số lần thử. Đoạn mã dưới đây gói các quy tắc trên:
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const backoff = (n) => Math.min(30_000, 500 * 2 ** n) * (0.5 + Math.random() / 2)
async function callApi(url, options = {}, { retries = 4, timeoutMs = 10_000 } = {}) {
for (let attempt = 0; ; attempt++) {
let res
try {
res = await fetch(url, { ...options, signal: AbortSignal.timeout(timeoutMs) })
} catch (err) {
// lỗi mạng hoặc quá thời gian chờ
if (attempt >= retries) throw err
await sleep(backoff(attempt))
continue
}
if (res.ok) return res.json()
const retryable = res.status === 429 || res.status >= 500
if (!retryable || attempt >= retries) throw new Error(`HTTP ${res.status}`)
// Retry-After là số giây (cũng có thể là ngày giờ HTTP, khi đó Number cho NaN và ta dùng backoff)
const retryAfter = Number(res.headers.get('retry-after'))
await sleep(retryAfter > 0 ? retryAfter * 1000 : backoff(attempt))
}
}

4. Dùng khóa bất biến cho thao tác ghi
Thử lại một cuộc gọi POST tạo thanh toán có thể trừ tiền 2 lần nếu cuộc gọi đầu thật ra đã thành công nhưng phản hồi bị mất. Giải pháp là khóa bất biến (idempotency key): máy khách sinh một giá trị duy nhất cho mỗi thao tác logic và gửi kèm, máy chủ nhận ra lần thử lại và trả về kết quả của lần đầu thay vì thực hiện lại.
Theo tài liệu của Stripe, khóa gửi trong header Idempotency-Key, dài tối đa 255 ký tự, nên dùng UUID v4 hoặc chuỗi ngẫu nhiên đủ độ phức tạp, và không dùng dữ liệu nhạy cảm như email làm khóa.
Ở API v1 của Stripe, mọi yêu cầu POST đều nhận khóa, khóa có thể bị xóa sau ít nhất 24 giờ, và nếu bạn gửi cùng khóa với tham số khác thì hệ thống báo lỗi để tránh dùng nhầm. GET và DELETE vốn đã bất biến nên không cần khóa.
Header Idempotency-Key cũng đang được nhóm httpapi của IETF soạn thảo thành tiêu chuẩn; theo bản mới nhất Click Star tra được thì nó vẫn là bản thảo (Internet-Draft), chưa có số RFC, nên hãy đọc tài liệu của từng nhà cung cấp.
// Sinh khóa MỘT LẦN cho mỗi thao tác, và dùng lại đúng khóa đó ở mọi lần thử
await callApi('https://api.example.com/v1/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({ amount: 150000, currency: 'VND' }),
})
Hàm callApi ở trên dùng lại cùng đối tượng options cho mọi lần thử, nên khóa được giữ nguyên giữa các lần.
5. Phân trang bằng con trỏ
Danh sách dài được trả theo trang. Phân trang theo vị trí (offset) dễ bỏ sót hoặc lặp bản ghi khi dữ liệu thay đổi giữa 2 lần gọi. Phân trang bằng con trỏ (cursor) dùng một dấu mốc do máy chủ trả về nên ổn định hơn. Hãy lặp cho tới khi hết trang tiếp theo, và đặt trần số trang để tránh vòng lặp vô tận.
6. Giảm số cuộc gọi
- Lọc và chọn trường ngay trong yêu cầu thay vì kéo hết rồi lọc ở phía bạn.
- Gộp (batch) nhiều thao tác vào một cuộc gọi nếu API hỗ trợ.
- Dùng bộ nhớ đệm HTTP: với ETag và header If-None-Match, máy chủ có thể trả 304 (chưa đổi) mà không gửi lại nội dung (RFC 9110 và RFC 9111). Với dữ liệu ít thay đổi, lưu kết quả phía bạn với thời hạn rõ ràng.
- Tránh gọi trong vòng lặp từng bản ghi một khi có thể gọi một lần cho cả danh sách.
7. Dùng webhook thay vì hỏi liên tục
Hỏi liên tục (polling) tốn cuộc gọi và vẫn chậm. Khi nhà cung cấp hỗ trợ webhook, hãy nhận sự kiện: xác minh chữ ký để chắc yêu cầu đến từ họ, trả mã 2xx nhanh rồi xử lý ở nền, và thiết kế để xử lý một sự kiện giao 2 lần vẫn cho kết quả đúng, vì webhook thường được gửi lại khi bạn không phản hồi kịp.
8. Quan sát hệ thống
Ghi lại mã yêu cầu (request id) của từng cuộc gọi để đối chiếu với nhà cung cấp khi có sự cố. Theo dõi độ trễ, tỷ lệ lỗi theo mã trạng thái và chi phí theo tháng. Đặt cảnh báo khi tỷ lệ 429 hoặc 5xx tăng, và ghi phiên bản API bạn đang dùng để biết khi nào cần nâng cấp.
Khi bạn là bên cung cấp API
- Tên tài nguyên là danh từ số nhiều (
/orders), dùng phương thức HTTP đúng nghĩa, có phiên bản trong đường dẫn (/v1/). - Trả lỗi nhất quán theo định dạng Problem Details của RFC 9457 (kiểu nội dung
application/problem+json): có loại lỗi, tiêu đề, mã trạng thái và mô tả để người gọi xử lý được bằng máy. - Trả header giới hạn và Retry-After cùng mã 429 để máy khách biết khi nào gọi lại.
- Mô tả API bằng OpenAPI Specification để sinh tài liệu, mã máy khách và kiểm thử hợp đồng tự động, giữ tài liệu và hành vi thật luôn khớp nhau.
- Hỗ trợ khóa bất biến cho các thao tác tạo và thanh toán.
API trong thời đại AI: vài điểm riêng
API của các mô hình ngôn ngữ cũng tuân theo các nguyên tắc trên, kèm vài đặc thù. Giới hạn thường tính theo cả số yêu cầu lẫn số token, nên cần theo dõi cả hai. Phản hồi có thể dài và chậm, nên đặt thời gian chờ rộng hơn hoặc dùng truyền luồng (streaming).
Chi phí tỷ lệ với số token, nên bộ nhớ đệm prompt, chọn model nhỏ cho việc đơn giản và giới hạn độ dài đầu ra tiết kiệm hơn bất kỳ mẹo nào khác (xem thêm bài về cách dùng GPT tối ưu). Khóa API của dịch vụ AI nên có hạn mức chi tiêu hằng tháng, và đừng ghi nội dung prompt chứa dữ liệu cá nhân vào nhật ký.
Quy trình tích hợp một API mới
- Đọc tài liệu, giới hạn, điều khoản sử dụng và chính sách dữ liệu của nhà cung cấp.
- Lấy khóa cho môi trường thử nghiệm, cất vào biến môi trường hoặc kho bí mật.
- Thử từng cuộc gọi bằng curl hoặc công cụ như Postman trước khi viết mã.
- Viết một lớp bọc mỏng quanh API: thời gian chờ, thử lại, khóa bất biến, ghi nhật ký, chuyển lỗi sang kiểu lỗi của bạn.
- Viết kiểm thử với phản hồi giả cho cả trường hợp thành công, lỗi 4xx, 429 và 5xx.
- Thêm theo dõi: độ trễ, tỷ lệ lỗi, chi phí và cảnh báo.
- Chạy thử trên môi trường xem trước với dữ liệu thử nghiệm, rồi mới bật khóa thật.
- Đặt lịch xem lại: khóa luân phiên định kỳ, thông báo ngừng hỗ trợ phiên bản, hạn mức chi phí.
6 sai lầm thường gặp
- Đặt khóa API trong mã chạy ở trình duyệt hoặc đưa vào kho mã.
- Thử lại vô hạn, không lùi thời gian, làm quá tải máy chủ đúng lúc nó đang gặp sự cố.
- Thử lại thao tác ghi mà không có khóa bất biến, gây trùng đơn hàng hay trùng thanh toán.
- Bỏ qua mã 429 và header Retry-After, cứ gọi tiếp cho đến khi bị khóa.
- Không đặt thời gian chờ, để một dịch vụ chậm kéo cả hệ thống đứng theo.
- Không theo dõi chi phí và tỷ lệ lỗi, nên chỉ biết có vấn đề khi khách hàng báo.
Bài liên quan
Cần tư vấn cụ thể cho doanh nghiệp?
Click Star giúp anh/chị xây dựng giải pháp phù hợp ngành nghề và quy mô. Trả lời mọi câu hỏi trong giờ làm việc.



