Google Sheets Kết Nối API REST: Lấy Dữ Liệu Từ Bất Kỳ Nguồn Nào [2026]
![Ảnh minh họa bài viết: Google Sheets Kết Nối API REST: Lấy Dữ Liệu Từ Bất Kỳ Nguồn Nào [2026]](/og-image.jpg)
Giới Thiệu: Google Sheets Không Chỉ Là Bảng Tính
Bạn muốn tỷ giá USD/VND tự động cập nhật mỗi sáng trong spreadsheet? Muốn theo dõi giá Bitcoin, dữ liệu thời tiết, hay thậm chí đồng bộ đơn hàng từ CRM về Sheets mà không cần nhập tay? Tất cả điều đó đều khả thi — và bài viết này sẽ hướng dẫn bạn từng bước.
Google Sheets có thể kết nối trực tiếp với hầu hết các REST API trên thế giới thông qua ba cách chính: hàm IMPORTDATA có sẵn, hàm tùy chỉnh IMPORTJSON, và Apps Script với UrlFetchApp. Mỗi cách có điểm mạnh riêng — và bài này sẽ giúp bạn chọn đúng cách cho từng tình huống.
Kèm theo đó là 5 ví dụ code thực tế hoàn chỉnh: tỷ giá ngoại tệ, thời tiết, giá crypto, API có xác thực, và gửi dữ liệu ngược ra API bên ngoài.
3 Cách Kết Nối API Với Google Sheets
Trước khi đi vào code, hãy hiểu rõ ba lựa chọn và khi nào dùng cái nào:
| Phương pháp | Loại dữ liệu | Xác thực (Auth) | Độ phức tạp | Phù hợp khi |
|---|---|---|---|---|
| IMPORTDATA | CSV / TSV | Không hỗ trợ | Rất đơn giản | API trả về CSV công khai |
| IMPORTJSON (custom) | JSON | Giới hạn | Trung bình | API JSON công khai, không cần header phức tạp |
| Apps Script UrlFetchApp | Mọi định dạng | Đầy đủ (Bearer, API key, OAuth) | Cao hơn (cần JavaScript) | Mọi trường hợp — đặc biệt khi cần auth hoặc xử lý phức tạp |
Cách 1: IMPORTDATA (Đơn Giản Nhất)
Đây là hàm có sẵn trong Google Sheets, không cần cài thêm gì. Chỉ hoạt động với dữ liệu dạng CSV hoặc TSV:
=IMPORTDATA("https://api.example.com/data.csv")
Giới hạn cần biết:
- Chỉ nhận CSV/TSV — không xử lý được JSON.
- Không thể thêm headers hay authentication token.
- Tự động refresh mỗi giờ (không kiểm soát được thời điểm).
- Nếu API thay đổi format, cả bảng tính có thể bị lỗi.
Khi nào dùng: Khi API trả về CSV thuần túy và không cần xác thực. Ví dụ: một số API tài chính, data government.
Cách 2: IMPORTJSON Qua Apps Script
Bạn có thể tạo một hàm tùy chỉnh để gọi JSON API và trả về kết quả vào ô tính. Cách này linh hoạt hơn IMPORTDATA nhưng vẫn giới hạn trong việc truyền authentication phức tạp.
Để tạo custom function: Mở Extensions > Apps Script và dán đoạn code vào. Sau khi lưu, bạn có thể dùng hàm như hàm bình thường trong sheet.
Cách 3: Apps Script + UrlFetchApp (Mạnh Nhất)
Đây là phương pháp được dùng nhiều nhất vì hỗ trợ đầy đủ mọi loại API:
- Gọi GET, POST, PUT, DELETE tùy theo API yêu cầu.
- Thêm headers tùy ý: Authorization, Content-Type, API key...
- Xử lý JSON response phức tạp và ghi vào bất kỳ vị trí nào trong sheet.
- Kết hợp với Trigger để tự động chạy theo lịch (hàng giờ, hàng ngày...).
- Xử lý lỗi, retry logic, logging đầy đủ.
Tất cả 5 ví dụ thực tế phía dưới đều dùng phương pháp này.
Hướng Dẫn Mở Apps Script
Trước khi bắt đầu với các ví dụ, đây là cách mở trình soạn thảo Apps Script:
- Mở Google Sheets của bạn.
- Trên menu, chọn Extensions (Tiện ích mở rộng).
- Chọn Apps Script.
- Một tab mới mở ra với trình soạn thảo code.
- Xoá code mẫu mặc định, dán code của bạn vào, nhấn Save (Ctrl+S).
- Nhấn nút Run (hình tam giác) để chạy thử.
Ví Dụ 1: Lấy Tỷ Giá Ngoại Tệ (ExchangeRate-API Miễn Phí)
API open.er-api.com hoàn toàn miễn phí, không cần đăng ký, cho phép lấy tỷ giá của hơn 160 đồng tiền so với USD. Đây là điểm bắt đầu hoàn hảo để học cách dùng UrlFetchApp.
function layTyGia() {
const url = 'https://open.er-api.com/v6/latest/USD';
const response = UrlFetchApp.fetch(url);
const data = JSON.parse(response.getContentText());
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange('A1').setValue('Cặp tiền');
sheet.getRange('B1').setValue('Tỷ giá');
sheet.getRange('C1').setValue('Cập nhật lúc');
sheet.getRange('A2').setValue('USD/VND');
sheet.getRange('B2').setValue(data.rates.VND);
sheet.getRange('A3').setValue('USD/EUR');
sheet.getRange('B3').setValue(data.rates.EUR);
sheet.getRange('A4').setValue('USD/JPY');
sheet.getRange('B4').setValue(data.rates.JPY);
sheet.getRange('A5').setValue('USD/SGD');
sheet.getRange('B5').setValue(data.rates.SGD);
// Ghi timestamp để biết lần cuối cập nhật
sheet.getRange('C2').setValue(new Date());
Logger.log('Đã cập nhật tỷ giá thành công!');
}
Giải thích từng dòng:
UrlFetchApp.fetch(url)— gửi HTTP GET request đến URL.response.getContentText()— lấy nội dung response dưới dạng string.JSON.parse(...)— chuyển string JSON thành object JavaScript để dễ truy cập.data.rates.VND— lấy giá trị VND từ object rates trong JSON response.new Date()— ghi thời điểm cập nhật để theo dõi.
Kết quả mẫu: Ô B2 sẽ hiển thị khoảng 25.400 (tỷ giá USD/VND hiện tại), tự động cập nhật mỗi lần bạn chạy script.
Ví Dụ 2: Lấy Thông Tin Thời Tiết (OpenWeatherMap)
OpenWeatherMap cung cấp gói miễn phí 1.000 request/ngày — đủ cho hầu hết nhu cầu cá nhân và startup nhỏ. Ví dụ này cũng giới thiệu cách lưu API key an toàn thay vì hardcode vào code.
function layThoiTiet() {
// Đọc API key từ Properties (không hardcode vào code)
const API_KEY = PropertiesService.getScriptProperties().getProperty('OWM_KEY');
const city = 'Hanoi,VN';
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=vi`;
const response = UrlFetchApp.fetch(url);
const data = JSON.parse(response.getContentText());
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange('A1:C1').setValues([['Thành phố', 'Nhiệt độ', 'Mô tả']]);
sheet.getRange('A2').setValue(data.name);
sheet.getRange('B2').setValue(data.main.temp + '°C');
sheet.getRange('C2').setValue(data.weather[0].description);
sheet.getRange('D2').setValue('Độ ẩm: ' + data.main.humidity + '%');
sheet.getRange('E2').setValue('Tốc độ gió: ' + data.wind.speed + ' m/s');
Logger.log('Thời tiết tại ' + data.name + ': ' + data.main.temp + '°C');
}
Cách đăng ký và lưu API key:
- Đăng ký miễn phí tại openweathermap.org.
- Sau khi xác nhận email, vào mục API keys để lấy key.
- Trong Apps Script, mở Project Settings (biểu tượng bánh răng) > Script Properties.
- Thêm property: Key =
OWM_KEY, Value = key của bạn. - Nhấn Save. Từ giờ code đọc key qua
PropertiesServicethay vì viết trực tiếp.
Lưu ý: Tham số lang=vi trả về mô tả thời tiết bằng tiếng Việt. units=metric dùng Celsius thay vì Fahrenheit.
Ví Dụ 3: Lấy Giá Crypto — Bitcoin và Ethereum (CoinGecko Miễn Phí)
CoinGecko cung cấp API miễn phí không cần đăng ký với giới hạn 30 request/phút — hoàn toàn đủ dùng. Ví dụ này cũng minh họa cách ghi nhiều ô cùng lúc bằng setValues (hiệu quả hơn ghi từng ô).
function layGiaCrypto() {
const url = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,tether&vs_currencies=vnd,usd&include_24hr_change=true';
const response = UrlFetchApp.fetch(url);
const data = JSON.parse(response.getContentText());
const sheet = SpreadsheetApp.getActiveSheet();
// Ghi header
sheet.getRange('A1:D1').setValues([['Coin', 'Giá (USD)', 'Giá (VND)', 'Thay đổi 24h']]);
// Ghi dữ liệu 3 coin cùng lúc (setValues nhận mảng 2 chiều)
sheet.getRange('A2:D4').setValues([
['Bitcoin', data.bitcoin.usd, data.bitcoin.vnd, (data.bitcoin.usd_24h_change || 0).toFixed(2) + '%'],
['Ethereum', data.ethereum.usd, data.ethereum.vnd, (data.ethereum.usd_24h_change || 0).toFixed(2) + '%'],
['Tether', data.tether.usd, data.tether.vnd, (data.tether.usd_24h_change || 0).toFixed(2) + '%']
]);
// Format cột giá USD với dấu phẩy ngàn
sheet.getRange('B2:B4').setNumberFormat('#,##0.00');
sheet.getRange('C2:C4').setNumberFormat('#,##0');
sheet.getRange('E1').setValue('Cập nhật: ' + new Date().toLocaleString('vi-VN'));
Logger.log('Đã cập nhật giá crypto.');
}
Mẹo tối ưu với setValues: Thay vì gọi setValue cho từng ô (mỗi lần là một lần ghi vào Sheets), dùng setValues với mảng 2 chiều để ghi nhiều ô trong một lần. Điều này giảm đáng kể thời gian thực thi khi có nhiều dữ liệu.
Ví Dụ 4: Gọi API Có Xác Thực — Bearer Token (CRM hoặc bất kỳ API nào)
Hầu hết API doanh nghiệp yêu cầu xác thực. Ví dụ này minh họa cách thêm header Authorization: Bearer, xử lý lỗi HTTP, và ghi dữ liệu có cấu trúc vào sheet có tên cụ thể.
function layDuLieuCRM() {
const token = PropertiesService.getScriptProperties().getProperty('CRM_TOKEN');
if (!token) {
SpreadsheetApp.getActive().toast('Chưa cấu hình CRM_TOKEN trong Script Properties!', '⚠️', 5);
return;
}
const url = 'https://api.yourcrm.com/v1/leads?status=active&limit=100';
const options = {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
muteHttpExceptions: true // Không throw exception khi HTTP 4xx/5xx, tự xử lý
};
const response = UrlFetchApp.fetch(url, options);
const statusCode = response.getResponseCode();
// Kiểm tra lỗi HTTP
if (statusCode === 401) {
Logger.log('Token hết hạn hoặc sai. Cần cập nhật CRM_TOKEN.');
return;
}
if (statusCode !== 200) {
Logger.log('Lỗi API: ' + statusCode + ' — ' + response.getContentText());
return;
}
const result = JSON.parse(response.getContentText());
const leads = result.data || result; // Tùy API trả về format khác nhau
// Ghi vào sheet tên 'Leads' (tạo nếu chưa có)
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spreadsheet.getSheetByName('Leads');
if (!sheet) sheet = spreadsheet.insertSheet('Leads');
sheet.clearContents();
sheet.appendRow(['Tên', 'Email', 'Số điện thoại', 'Trạng thái', 'Ngày tạo']);
const rows = leads.map(lead => [
lead.name || '',
lead.email || '',
lead.phone || '',
lead.status || '',
lead.created_at ? new Date(lead.created_at).toLocaleDateString('vi-VN') : ''
]);
if (rows.length > 0) {
sheet.getRange(2, 1, rows.length, 5).setValues(rows);
}
Logger.log('Đã đồng bộ ' + leads.length + ' leads từ CRM.');
SpreadsheetApp.getActive().toast('Đã đồng bộ ' + leads.length + ' leads!', '✅', 3);
}
Giải thích các điểm quan trọng:
muteHttpExceptions: true— bắt buộc phải có khi bạn muốn tự xử lý lỗi HTTP thay vì để script crash.response.getResponseCode()— trả về mã HTTP (200, 401, 404, 500...) để kiểm tra trước khi xử lý dữ liệu.spreadsheet.getSheetByName('Leads')— tìm sheet theo tên thay vì index, an toàn hơn khi user thêm/xóa sheet.sheet.clearContents()— xóa nội dung cũ trước khi ghi mới (không xóa format).
Ví Dụ 5: Gửi Dữ Liệu Từ Sheets Ra API Bên Ngoài (POST Request)
Không chỉ đọc dữ liệu từ API, bạn còn có thể gửi dữ liệu từ Sheets ra ngoài. Ví dụ này đọc dữ liệu từ hàng đang chọn và gửi lên API bằng POST request — hữu ích để đồng bộ đơn hàng, submit form, hay trigger workflow tự động.
function guiDonHangLenAPI() {
const sheet = SpreadsheetApp.getActiveSheet();
const row = sheet.getActiveRange().getRow();
// Đọc 5 cột từ hàng đang được chọn (A đến E)
const data = sheet.getRange(row, 1, 1, 5).getValues()[0];
if (!data[0]) {
SpreadsheetApp.getActive().toast('Hàng đang chọn không có dữ liệu!', '⚠️', 3);
return;
}
const payload = {
order_id: data[0],
customer: data[1],
amount: data[2],
product: data[3],
date: data[4] instanceof Date ? data[4].toISOString() : data[4]
};
const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
const options = {
method: 'POST',
contentType: 'application/json',
payload: JSON.stringify(payload),
headers: {
'X-API-Key': apiKey
},
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch('https://api.yourapp.com/orders', options);
const statusCode = response.getResponseCode();
if (statusCode === 200 || statusCode === 201) {
// Đánh dấu hàng đã gửi bằng cách tô màu xanh
sheet.getRange(row, 1, 1, 5).setBackground('#d9f7d9');
sheet.getRange(row, 6).setValue('Đã gửi — ' + new Date().toLocaleTimeString('vi-VN'));
SpreadsheetApp.getActive().toast('Đã gửi đơn: ' + data[0], '✅', 3);
} else {
Logger.log('Gửi thất bại: ' + statusCode + ' — ' + response.getContentText());
SpreadsheetApp.getActive().toast('Lỗi khi gửi đơn ' + data[0] + ': ' + statusCode, '❌', 5);
}
}
Mẹo thực tế: Thêm một nút button vào sheet và liên kết với function này để người dùng không kỹ thuật cũng có thể gửi đơn chỉ bằng một cú click. Vào Insert > Drawing, vẽ nút, nhấp phải và chọn Assign script, nhập tên function guiDonHangLenAPI.
Lưu API Key An Toàn — Không Bao Giờ Hardcode
Một trong những lỗi phổ biến nhất là ghi API key trực tiếp vào code:
// ❌ NGUY HIỂM — đừng làm thế này
const API_KEY = 'sk-abc123xyz...';
Khi bạn share spreadsheet với người khác, họ có thể xem code trong Apps Script và lấy key của bạn. Thay vào đó, dùng Script Properties — một key-value store riêng tư chỉ bạn (owner) mới đọc được:
// ✅ ĐÚNG — lưu key vào Properties một lần
function luuCacApiKey() {
const props = PropertiesService.getScriptProperties();
props.setProperty('OWM_KEY', 'your-openweathermap-key');
props.setProperty('CRM_TOKEN', 'your-crm-bearer-token');
props.setProperty('API_KEY', 'your-api-key');
Logger.log('Đã lưu API keys vào Properties!');
}
// ✅ Đọc lại trong các function khác
function vidu() {
const key = PropertiesService.getScriptProperties().getProperty('MY_API_KEY');
// dùng key ở đây
}
Quy trình lưu key lần đầu:
- Dán function
luuCacApiKeyvào Apps Script, điền giá trị thực của các key. - Chạy function một lần để lưu.
- Sau đó xóa function đó khỏi code (vì nó chứa key dạng plain text).
- Từ giờ, tất cả function khác đọc key qua
getProperty— không ai thấy giá trị thực.
Ngoài ra, bạn cũng có thể lưu qua UI: Project Settings (biểu tượng bánh răng) > Script Properties > nhấp Add property.
Xử Lý Lỗi và Retry Tự Động
API đôi khi bị lỗi tạm thời (network timeout, rate limit 429, server lỗi 500). Thay vì để script crash, hãy implement retry với exponential backoff:
/**
* Gọi API với retry tự động, backoff lũy thừa
* @param {string} url - URL cần gọi
* @param {Object} options - Options cho UrlFetchApp (headers, method, v.v.)
* @param {number} maxRetries - Số lần thử tối đa (mặc định 3)
* @returns {HTTPResponse} Response cuối cùng thành công
*/
function fetchVoiRetry(url, options, maxRetries) {
maxRetries = maxRetries || 3;
const fetchOptions = Object.assign({ muteHttpExceptions: true }, options);
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = UrlFetchApp.fetch(url, fetchOptions);
const statusCode = response.getResponseCode();
// Thành công
if (statusCode >= 200 && statusCode < 300) {
return response;
}
// Rate limit (429) hoặc lỗi server tạm thời (5xx) — retry
if (statusCode === 429 || statusCode >= 500) {
const waitMs = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
Logger.log('Lỗi ' + statusCode + ', thử lại sau ' + waitMs + 'ms...');
Utilities.sleep(waitMs);
continue;
}
// Lỗi client (4xx không phải 429) — không retry
Logger.log('Lỗi client ' + statusCode + ': ' + response.getContentText());
return response;
} catch (e) {
if (attempt === maxRetries - 1) throw e;
Utilities.sleep(1000 * (attempt + 1));
}
}
}
// Dùng trong thực tế:
function viDungRetry() {
const url = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd';
const response = fetchVoiRetry(url, {}, 3);
if (response && response.getResponseCode() === 200) {
const data = JSON.parse(response.getContentText());
SpreadsheetApp.getActiveSheet().getRange('A1').setValue(data.bitcoin.usd);
}
}
Thiết Lập Trigger Để Tự Động Cập Nhật
Viết được function rồi, nhưng làm sao chạy tự động mà không cần bạn ngồi bấm nút? Apps Script có Time-based Triggers — bộ lên lịch tích hợp sẵn:
Cách 1: Tạo Trigger Qua UI (Không Cần Code)
- Trong Apps Script, nhấp biểu tượng đồng hồ (Triggers) ở thanh bên trái.
- Nhấn + Add Trigger (góc dưới phải).
- Chọn function cần chạy (ví dụ:
layTyGia). - Chọn event source: Time-driven.
- Chọn loại: Day timer (hàng ngày), Hour timer (hàng giờ), v.v.
- Nhấn Save. Xong!
Cách 2: Tạo Trigger Bằng Code (Linh Hoạt Hơn)
// Chạy function layTyGia mỗi ngày lúc 8-9h sáng
function taoTriggerHangNgay() {
// Xóa trigger cũ trùng tên để tránh chạy trùng
ScriptApp.getProjectTriggers()
.filter(t => t.getHandlerFunction() === 'layTyGia')
.forEach(t => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger('layTyGia')
.timeBased()
.everyDays(1)
.atHour(8)
.create();
Logger.log('Đã tạo trigger chạy mỗi ngày lúc 8h!');
}
// Chạy function layGiaCrypto mỗi giờ
function taoTriggerMoiGio() {
ScriptApp.newTrigger('layGiaCrypto')
.timeBased()
.everyHours(1)
.create();
}
Lưu ý về Trigger: Mỗi Google account có giới hạn tổng số giờ chạy trigger là 6 giờ/ngày (với tài khoản miễn phí) và 90 phút/lần thực thi. Với Workspace, giới hạn cao hơn đáng kể.
Giới Hạn Quota Cần Biết
Apps Script không phải không có giới hạn. Đây là những con số quan trọng cho tài khoản Google miễn phí:
| Loại giới hạn | Tài khoản miễn phí | Google Workspace |
|---|---|---|
| UrlFetchApp calls/ngày | 20.000 | 100.000 |
| Thời gian thực thi/lần | 6 phút | 30 phút |
| Tổng thời gian trigger/ngày | 90 phút | 6 giờ |
| Email gửi/ngày (qua MailApp) | 100 | 1.500 |
| Script Properties storage | 9KB/property, 500KB tổng | Như nhau |
Với 20.000 request/ngày, nếu bạn refresh tỷ giá mỗi giờ (24 lần/ngày), bạn còn rất nhiều quota dư. Giới hạn này chỉ thành vấn đề khi xử lý hàng nghìn records hoặc dùng nhiều script cùng lúc.
Theo dõi quota: Vào script.google.com/home/userlimits để xem mức sử dụng hiện tại của tài khoản.
Kết Luận: Bắt Đầu Từ Đâu?
Google Sheets + Apps Script là một combo cực kỳ mạnh mẽ để kết nối dữ liệu từ bất kỳ API nào — và hoàn toàn miễn phí trong giới hạn cá nhân. Bạn không cần server riêng, không cần hạ tầng phức tạp, chỉ cần một tài khoản Google và vài dòng JavaScript.
Lộ trình đề xuất:
- Tuần 1: Thử Ví dụ 1 (tỷ giá) — không cần API key, chạy ngay.
- Tuần 2: Đăng ký OpenWeatherMap miễn phí, thử Ví dụ 2.
- Tuần 3: Áp dụng vào API thực tế trong công việc (CRM, ERP, v.v.) theo pattern Ví dụ 4.
- Tiếp theo: Thêm Trigger để tự động hóa hoàn toàn.
Quan trọng nhất: luôn dùng PropertiesService để lưu API key thay vì hardcode, và luôn thêm muteHttpExceptions: true cùng kiểm tra response code để script không bị crash bất ngờ.
Khi bạn đã quen với pattern cơ bản, gần như mọi REST API trên internet đều có thể tích hợp vào Google Sheets theo cách tương tự — chỉ thay đổi URL, headers, và cấu trúc JSON tương ứng.
Nguồn tham khảo: Apps Script UrlFetchApp Docs | ExchangeRate-API | OpenWeatherMap API | CoinGecko API Docs
Chia sẻ bài viết:
Tuân Hoang
Đội ngũ SheetStore
Google Workspace Certified, 5+ years experience
Bạn thấy bài viết hữu ích?
Đăng ký nhận thông báo khi có bài viết mới.

