Bỏ qua

Kéo báo cáo vào Google Sheets

Dùng Apps Script — không cần cài gì, chạy theo lịch ngay trong Google.

Đừng dùng IMPORTDATA hay IMPORTJSON

Những hàm đó không gửi được header, nên không mang được X-API-Key. Ngoài ra chúng chạy lại mỗi khi sheet tính lại — đốt hạn mức rất nhanh.

1. Mở Apps Script

Trong Google Sheet: Tiện ích mở rộng → Apps Script.

2. Lưu API key an toàn

Dán vào editor, sửa giá trị, chạy một lần, rồi xoá hàm này đi:

function luuBiMat() {
  PropertiesService.getScriptProperties().setProperties({
    LOGIDATA_API_KEY: 'ld_live_...',
    LOGIDATA_ACCOUNT_ID: 'ldc_9fK2xQ...',
    ADVERTISER_ID: '7001234567890',
  });
}

Cách này giữ key ngoài nội dung sheet, nên người được chia sẻ sheet không đọc được key.

3. Script kéo dữ liệu

const BASE = 'https://api.logidata.vn/v1/tiktok-ads';

function goi(path, params) {
  const p = PropertiesService.getScriptProperties();
  const qs = Object.keys(params)
      .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
      .join('&');

  const res = UrlFetchApp.fetch(BASE + path + '?' + qs, {
    headers: {
      'X-API-Key': p.getProperty('LOGIDATA_API_KEY'),
      'X-Account-Id': p.getProperty('LOGIDATA_ACCOUNT_ID'),
    },
    muteHttpExceptions: true,   // để tự đọc body lỗi thay vì bị ném ngoại lệ
  });

  const code = res.getResponseCode();
  const body = JSON.parse(res.getContentText());

  if (code >= 400) {
    throw new Error(body.error.code + ': ' + body.error.message +
                    ' (request_id=' + body.error.request_id + ')');
  }
  if (body.code !== 0) {        // TikTok trả 200 kèm lỗi nghiệp vụ
    throw new Error('TikTok ' + body.code + ': ' + body.message);
  }
  return body.data;
}

function ngay(luiBaoNhieu) {
  const d = new Date();
  d.setDate(d.getDate() - luiBaoNhieu);
  return Utilities.formatDate(d, 'Asia/Ho_Chi_Minh', 'yyyy-MM-dd');
}

function capNhatBaoCao() {
  const p = PropertiesService.getScriptProperties();

  const data = goi('/v1.3/report/integrated/get/', {
    advertiser_id: p.getProperty('ADVERTISER_ID'),
    report_type: 'BASIC',
    data_level: 'AUCTION_CAMPAIGN',
    dimensions: '["campaign_id","stat_time_day"]',
    metrics: '["spend","impressions","clicks"]',
    // Kéo lại 7 ngày: TikTok còn điều chỉnh số liệu tới 48 giờ sau
    start_date: ngay(7),
    end_date: ngay(1),
    page_size: 1000,
  });

  const rows = data.list.map(it => [
    it.dimensions.stat_time_day.slice(0, 10),
    it.dimensions.campaign_id,
    parseFloat(it.metrics.spend),
    parseInt(it.metrics.impressions, 10),
    parseInt(it.metrics.clicks, 10),
  ]);

  const sheet = SpreadsheetApp.getActive().getSheetByName('TikTok Ads')
             || SpreadsheetApp.getActive().insertSheet('TikTok Ads');
  sheet.clear();
  sheet.getRange(1, 1, 1, 5)
       .setValues([['Ngày', 'Campaign ID', 'Chi phí', 'Hiển thị', 'Click']])
       .setFontWeight('bold');
  if (rows.length) {
    sheet.getRange(2, 1, rows.length, 5).setValues(rows);
  }
  sheet.getRange('C2:C').setNumberFormat('#,##0');
  SpreadsheetApp.getActive().toast(rows.length + ' dòng đã cập nhật');
}

4. Chạy tự động hằng ngày

Trong Apps Script: Trigger (biểu tượng đồng hồ) → Add Trigger

Function capNhatBaoCao
Event source Time-driven
Type Day timer
Time of day 6am – 7am

Lần chạy đầu Google sẽ hỏi cấp quyền cho script — đó là quyền của script với sheet của bạn, không liên quan tới Logidata hay TikTok.

5. Thêm tên campaign

Báo cáo chỉ trả về ID. Lấy tên rồi ghép bằng map:

function layTenCampaign(advertiserId) {
  const ten = {};
  let page = 1, tongTrang = 1;
  do {
    const d = goi('/v1.3/campaign/get/',
                  {advertiser_id: advertiserId, page: page, page_size: 1000});
    d.list.forEach(c => { ten[c.campaign_id] = c.campaign_name; });
    tongTrang = d.page_info.total_page;
    page++;
  } while (page <= tongTrang);
  return ten;
}

Gọi một lần đầu capNhatBaoCao() rồi tra ten[it.dimensions.campaign_id] khi dựng từng dòng.

Giới hạn 6 phút của Apps Script

Mỗi lần chạy tối đa 6 phút. Nếu bạn có nhiều advertiser, tách thành nhiều hàm với trigger ở các khung giờ khác nhau, thay vì nhồi hết vào một hàm.

```