Danh mục tài liệu

SERP API

Tài liệu SERP API

SERP API cho phép bạn lấy dữ liệu kết quả tìm kiếm Google dưới dạng JSON thô, dùng chung hạ tầng cào và cùng đơn giá với SERP Checker. Tài liệu này mô tả đầy đủ cách xác thực, các endpoint, định dạng dữ liệu trả về, webhook và bảng mã lỗi.

Thông tin cơ bản

  • Base URL: https://app.serpupdate.com/api/v1
  • Xác thực: header X-API-KEY
  • Công cụ tìm kiếm: Google (giai đoạn hiện tại)
  • Định dạng: JSON thô, dùng mã trạng thái HTTP thật

1. Quy trình tích hợp

  1. Lấy API key trong app, mục SERP API, tab API Keys.
  2. Khai báo Pingback URLPingback Token ở cùng tab đó.
  3. Gọi POST /serp-api/trigger kèm từ khóa, nhận về snapshot_id.
  4. Chờ SERPUPDATE gọi webhook về máy chủ của bạn khi snapshot xong.
  5. Gọi GET /serp-api/{snapshot_id} để lấy dữ liệu.

Nên dùng webhook thay vì hỏi liên tục

Hỏi liên tục tốn lượt gọi và chậm hơn. Pingback webhook sẽ tự gọi về máy chủ của bạn ngay khi có kết quả.

2. Xác thực

Mọi lượt gọi đều gửi kèm header X-API-KEY. Key là chuỗi 32 ký tự hex thường, không dấu gạch.

curl -X GET https://app.serpupdate.com/api/v1/serp-api/languages 
  -H "X-API-KEY: 3f9a1c7e4b2d48a6915e0c83d7f6b214"

Tạo key mới là vô hiệu key cũ ngay lập tức

Bấm “Lấy key mới” sẽ khóa key hiện tại tức thì và không hoàn tác được. Mọi tích hợp còn dùng key cũ sẽ lỗi cho tới khi bạn cập nhật.

3. Danh sách endpoint

3.1. Tạo snapshot

POST /serp-api/trigger

Đây là lệnh gọi tính phí. Mỗi lượt gọi nhận đúng một từ khóa. Trả về snapshot_id ngay lập tức, dữ liệu lấy sau.

Tham số Kiểu Bắt buộc Mô tả
keyword string Từ khóa cần crawl, 1 đến 500 ký tự.
language string Không Mã ngôn ngữ, ví dụ vi, en. Lấy từ endpoint languages.
country string Không Mã quốc gia ISO-2, ví dụ VN, US.
device string Không desktop hoặc mobile. Mặc định desktop.
start_page number Không Trang SERP bắt đầu, từ 1. Mặc định 1.
end_page number Không Trang SERP kết thúc, tối đa 10. Mặc định 10, tức trọn Top 100.
location string Không Tên vị trí lấy từ endpoint locations. Bỏ trống nếu đã gửi uule.
uule string Không Chuỗi UULE đã mã hóa sẵn. Bỏ trống để hệ thống tự suy ra từ location.
include_aio boolean Không true để lấy thêm AI Overview và danh sách nguồn trích dẫn.
custom_id string Không ID riêng của bạn, dùng để đối chiếu.

Giá tính phẳng theo lệnh gọi

Lấy 1 trang hay đủ 10 trang đều cùng một mức giá. Không nên giảm end_page để tiết kiệm, vì không tiết kiệm được gì. Chỉ include_aio mới làm đổi đơn giá, và là giá thay thế chứ không cộng thêm.

Ví dụ lệnh gọi

curl -X POST https://app.serpupdate.com/api/v1/serp-api/trigger 
  -H "X-API-KEY: 3f9a1c7e4b2d48a6915e0c83d7f6b214" 
  -H "Content-Type: application/json" 
  -H "Idempotency-Key: order-8841" 
  -d '{
  "input": [
    {
      "keyword": "sàn nhựa giả gỗ",
      "language": "vi",
      "country": "VN",
      "device": "desktop",
      "start_page": 1,
      "end_page": 10,
      "include_aio": true
    }
  ]
}'

Phản hồi

{ "snapshot_id": "sd_abc123xyz" }

Chỉ 1 từ khóa mỗi lượt

Trường input là mảng nhưng giai đoạn này chỉ nhận đúng một phần tử. Gửi nhiều hơn sẽ nhận mã lỗi TOO_MANY_INPUTS.

3.2. Chống tính phí trùng

Gửi kèm header Idempotency-Key (tùy chọn, tối đa 64 ký tự) khi gọi trigger. Nếu mạng lỗi và bạn phải gọi lại cùng một key, hệ thống nhận ra và không chạy thêm một lượt tính phí nữa. Nên dùng một giá trị duy nhất cho mỗi lượt nghiệp vụ, ví dụ mã đơn hàng hoặc UUID.

3.3. Lấy kết quả

GET /serp-api/{snapshot_id}

Mã HTTP Ý nghĩa
200 Đã xong, thân phản hồi là mảng JSON kết quả.
202 Đang chạy, chưa có dữ liệu. Chờ webhook hoặc hỏi lại sau.
404 Không tìm thấy snapshot.
410 Snapshot đã quá hạn lưu trữ, dữ liệu đã bị xóa.
422 Lượt crawl thất bại, xem error_code trong thân phản hồi.
curl https://app.serpupdate.com/api/v1/serp-api/sd_abc123xyz 
  -H "X-API-KEY: 3f9a1c7e4b2d48a6915e0c83d7f6b214"

3.4. Tra cứu địa điểm và ngôn ngữ

GET /serp-api/locations nhận các tham số truy vấn: name (khớp gần đúng), country (ISO-2), page (đánh số từ 0), size.

GET /serp-api/languages không có tham số, trả về toàn bộ danh sách ngôn ngữ Google hỗ trợ.

Dùng hai endpoint này để lấy giá trị hợp lệ cho locationlanguage trước khi gọi trigger.

4. Cấu trúc dữ liệu trả về

Thân phản hồi là một mảng, mỗi phần tử ứng với một từ khóa. Hiện tại luôn có đúng một phần tử.

[
  {
    "keyword": "best protein powder for muscle gain",
    "organic": [
      {
        "rank": 1,
        "title": "Protein Powders For Muscle Growth Support",
        "url": "https://www.gnc.com/buy/protein-powders-for-muscle-growth",
        "domain": "gnc.com",
        "description": "Whey protein is often chosen for its rapid absorption..."
      }
    ],
    "aio_text": "Nội dung AI Overview dạng markdown, kèm chỉ dẫn nguồn [[1]](https://...)",
    "aio_reference": [
      {
        "rank": 1,
        "url": "https://www.healthline.com/nutrition/best-protein-powder-to-build-muscle",
        "domain": "healthline.com",
        "title": "The 10 Best Protein Powders to Build Muscle",
        "description": "Protein powders, especially when combined with resistance training..."
      }
    ]
  }
]
Trường Kiểu Ý nghĩa
keyword string Từ khóa đã crawl, trả lại đúng như bạn gửi.
organic array Kết quả tự nhiên, sắp theo thứ hạng.
organic[].rank number Vị trí trên SERP, đánh số từ 1 và chạy xuyên suốt các trang.
organic[].title string Tiêu đề hiển thị trên Google.
organic[].url string URL đích thật, không phải link chuyển hướng của Google.
organic[].domain string Tên miền rút gọn của URL.
organic[].description string Đoạn mô tả hiển thị dưới tiêu đề.
aio_text string Nội dung AI Overview dạng markdown, có chỉ dẫn nguồn dạng [[n]].
aio_reference array Danh sách nguồn mà AI Overview trích dẫn, cùng cấu trúc với organic.

Hai trường AI Overview có thể không xuất hiện

aio_textaio_reference chỉ có khi bạn gửi include_aio: true Google thật sự hiển thị AI Overview cho truy vấn đó. Nếu không, hai khóa này vắng mặt hoàn toàn chứ không phải bằng rỗng. Bộ phân tích phía bạn cần chấp nhận trường hợp thiếu khóa.

5. Pingback webhook

Khi snapshot xong hoặc thất bại, SERPUPDATE gửi một lệnh POST tới URL bạn khai báo. Đây là chiều SERPUPDATE gọi vào máy chủ của bạn, không phải endpoint bạn gọi.

POST https://your-server.com/webhook/serpupdate
User-Agent: SerpUpdate-Webhook/1.0
X-SerpUpdate-Delivery: dlv_a1b2c3d4e5f6
X-SerpUpdate-Token: token bạn đặt ở tab API Keys
Content-Type: application/json

{
  "snapshot_id": "sd_abc123xyz",
  "status": "ready",
  "keyword": "sàn nhựa giả gỗ",
  "collect_time_ms": 18450
}

status nhận giá trị ready khi thành công và failed khi thất bại. Trường collect_time_ms là thời gian thu thập tính bằng mili giây.

Luôn kiểm tra token trước khi tin nội dung

So sánh header X-SerpUpdate-Token với token bạn đã đặt. Nếu không khớp, trả về 401 và bỏ qua lượt gọi đó. Không có bước này thì bất kỳ ai biết URL cũng có thể giả mạo dữ liệu gửi vào hệ thống của bạn.

Máy chủ của bạn phải trả về mã 2xx. Nếu không, SERPUPDATE thử lại tối đa 3 lần, cách nhau 30 giây, 5 phút và 30 phút.

Ví dụ xử lý webhook bằng Node.js

import express from "express";
const app = express();
app.use(express.json());

app.post("/webhook/serpupdate", (req, res) => {
  if (req.get("X-SerpUpdate-Token") !== process.env.SERPUPDATE_PINGBACK_TOKEN) {
    return res.sendStatus(401);
  }
  const { snapshot_id: id, status } = req.body;
  if (status === "ready") {
    console.log("snapshot ready:", id);
  }
  res.sendStatus(200);
});

Yêu cầu cấu hình

  • Pingback URL bắt buộc dùng https://. Hệ thống chặn các địa chỉ nội bộ như localhost, 127.0.0.1, dải 10.x, 192.168.x, 172.16.x đến 172.31.x.
  • Pingback Token dài từ 16 đến 128 ký tự.
  • Trong app có nút Test pingback để bắn thử một lượt gọi và xem máy chủ của bạn trả về mã gì.
  • Nếu một lần gửi thất bại, bảng Request logs có nút gửi lại thủ công.

6. Bảng mã lỗi

Mã lỗi Ý nghĩa
INVALID_REQUEST Yêu cầu không hợp lệ, thiếu dữ liệu hoặc sai định dạng JSON.
INVALID_INPUT Một trường dữ liệu không hợp lệ.
TOO_MANY_INPUTS Giai đoạn này chỉ hỗ trợ 1 từ khóa mỗi lượt chạy.
INVALID_IDEMPOTENCY_KEY Khóa Idempotency-Key quá dài, tối đa 64 ký tự.
UNAUTHORIZED Xác thực không hợp lệ, kiểm tra lại API key.
INSUFFICIENT_BALANCE Số dư ví không đủ, vui lòng nạp thêm.
FEATURE_DISABLED Tính năng SERP API đang bị tạm khóa cho tài khoản này.
SNAPSHOT_EXPIRED Snapshot đã hết hạn lưu trữ, dữ liệu đã bị xóa.
SNAPSHOT_FAILED Snapshot thất bại.
COLLECTION_ERROR Yêu cầu không được chấp nhận.
COLLECTION_TIMEOUT Hệ thống không phản hồi kịp thời.
COLLECTION_UNAVAILABLE Hệ thống crawl hiện không khả dụng, thử lại sau.
TRIGGER_TIMEOUT Yêu cầu không khởi tạo được.
INTERNAL_ERROR Lỗi hệ thống, thử lại hoặc liên hệ hỗ trợ.

Vượt hạn mức gọi

Khi vượt hạn mức, hệ thống trả về HTTP 429 kèm header Retry-After tính bằng giây. Hãy chờ đúng khoảng đó rồi gọi lại thay vì thử liên tục.

Lượt chạy lỗi không tự động hoàn tiền

Phần lớn mã lỗi ở trên không kèm hoàn phí tự động. Nếu bạn cần đối soát, hãy liên hệ hỗ trợ kèm snapshot_id.

7. Vòng đời và giới hạn

  • Thời gian lưu trữ: 30 ngày kể từ lúc snapshot hoàn tất. Sau đó gọi lại sẽ nhận mã 410.
  • Cảnh báo sắp hết hạn: bảng Request logs gắn nhãn khi còn dưới 3 ngày.
  • Trạng thái snapshot: running, ready, failed, expired.
  • Không xóa được snapshot thủ công. Hệ thống tự dọn theo chính sách lưu trữ.
  • Số trang: start_pageend_page nằm trong khoảng 1 đến 10.

Giữ nguyên chuỗi JSON gốc nếu cần đối chiếu

Nếu bạn lưu lại phản hồi để kiểm toán hoặc băm chữ ký, hãy giữ đúng chuỗi thô nhận được. Việc phân tích rồi ghi lại thành JSON mới sẽ đổi thứ tự khóa và khoảng trắng, làm sai kết quả đối chiếu.