11 KiB
NotebookLM API — Tài liệu đầy đủ
Server Node.js điều khiển Google NotebookLM qua Puppeteer, giả lập thao tác người dùng thật.
Base URL: http://localhost:3456
Mục lục
- Cài đặt & Khởi động
- Biến môi trường
- Xác thực
- Notebooks
- Sources
- Chat
- Trạng thái server
- Mã lỗi
- Tích hợp
Cài đặt & Khởi động
Yêu cầu: Node.js 18+, Google Chrome
bash setup.sh # cài đặt + khởi động
# Hoặc thủ công:
npm install
cp .env.example .env
npm start
- Server khởi động tại
http://localhost:3456 - Swagger UI tại
http://localhost:3456/docs - Chrome tự mở với profile riêng tại
./chrome-profile/
Dừng server: Ctrl+C — KHÔNG dùng kill -9 (sẽ mất cookies Google)
Biến môi trường
File .env (copy từ .env.example):
| Biến | Mặc định | Mô tả |
|---|---|---|
PORT |
3456 |
Cổng HTTP/WebSocket |
HEADLESS |
false |
true = ẩn cửa sổ Chrome |
CHROME_PATH |
tự phát hiện | Đường dẫn Chrome tuỳ chỉnh |
API_KEY |
(trống) | Nếu đặt, mọi request phải có header x-api-key: <key> |
Xác thực
GET /api/auth/status
Kiểm tra trạng thái đăng nhập Google.
curl http://localhost:3456/api/auth/status
{ "ok": true, "authenticated": true }
{ "ok": true, "authenticated": false }
POST /api/auth/login
Mở browser để đăng nhập Google. Chờ tối đa 5 phút.
curl -X POST http://localhost:3456/api/auth/login
{ "ok": true, "authenticated": true, "message": "Đăng nhập thành công, session đã lưu" }
Session lưu vào ./chrome-profile/ — các lần khởi động lại server không cần đăng nhập lại.
Notebooks
GET /api/notebooks
Lấy danh sách tất cả notebooks.
curl http://localhost:3456/api/notebooks
{
"ok": true,
"total": 3,
"notebooks": [
{
"id": "2c4f0f26-0797-4cc0-a350-48c4b70d14cc",
"title": "Dự án Q3 2026",
"url": "https://notebooklm.google.com/notebook/2c4f0f26-..."
}
]
}
POST /api/notebooks
Tạo notebook mới.
Body:
{ "title": "Tên notebook" }
curl -X POST http://localhost:3456/api/notebooks \
-H "Content-Type: application/json" \
-d '{"title": "Dự án Q3 2026"}'
{
"ok": true,
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"title": "Dự án Q3 2026",
"url": "https://notebooklm.google.com/notebook/..."
}
DELETE /api/notebooks/:id
Xoá notebook.
curl -X DELETE http://localhost:3456/api/notebooks/2c4f0f26-0797-4cc0-a350-48c4b70d14cc
{ "ok": true, "deleted": true, "id": "2c4f0f26-..." }
Sources
GET /api/notebooks/:id/sources
Lấy danh sách sources trong notebook. Tự chờ đến khi tất cả sources convert xong.
Query params:
| Param | Mặc định | Mô tả |
|---|---|---|
timeout |
300 |
Giây tối đa chờ converting xong (max 600) |
# Mặc định chờ 5 phút
curl "http://localhost:3456/api/notebooks/<id>/sources" --max-time 310
# Notebook nặng — chờ tối đa 10 phút
curl "http://localhost:3456/api/notebooks/<id>/sources?timeout=600" --max-time 610
{
"ok": true,
"total": 3,
"stillLoading": 0,
"sources": [
{ "index": 0, "title": "Báo cáo Q2.pdf", "type": "pdf", "loading": false },
{ "index": 1, "title": "https://example.com/article", "type": "url", "loading": false },
{ "index": 2, "title": "Ghi chú cuộc họp", "type": "text", "loading": false }
]
}
stillLoading > 0 — gọi lại sau vài phút để lấy kết quả đầy đủ.
POST /api/notebooks/:id/sources
Thêm source vào notebook. Hỗ trợ 3 loại:
Loại 1: URL website
{ "type": "url", "content": "https://...", "title": "Tuỳ chọn" }
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
-H "Content-Type: application/json" \
-d '{"type":"url","content":"https://vnexpress.net/bai-viet-abc","title":"VnExpress"}'
{ "ok": true, "added": true, "type": "url", "url": "https://..." }
Loại 2: Văn bản paste
{ "type": "text", "content": "Nội dung văn bản...", "title": "Tên tài liệu" }
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
-H "Content-Type: application/json" \
-d '{"type":"text","content":"Báo cáo doanh thu tháng 6: ...","title":"Báo cáo T6/2026"}'
{ "ok": true, "added": true, "type": "text", "length": 1234 }
Loại 3: File local
Upload file từ máy đang chạy server. Đường dẫn phải là đường dẫn tuyệt đối (absolute path). Hỗ trợ cả URL-encoded path (có %20 thay dấu cách).
{ "type": "file", "content": "/đường/dẫn/tuyệt/đối/file.pdf" }
Định dạng hỗ trợ: .pdf .txt .md .docx .doc .pptx .ppt .xlsx .xls .mp3 .mp4 .jpg .jpeg .png
# File bình thường
curl -X POST http://localhost:3456/api/notebooks/<id>/sources \
-H "Content-Type: application/json" \
-d '{"type":"file","content":"/Users/me/Documents/report.pdf"}' \
--max-time 90
# Đường dẫn có dấu cách (cả 2 cách đều dùng được)
-d '{"type":"file","content":"/Users/me/Downloads/Telegram Desktop/file.md"}'
-d '{"type":"file","content":"/Users/me/Downloads/Telegram%20Desktop/file.md"}'
{
"ok": true,
"added": true,
"type": "file",
"filename": "report.pdf",
"path": "/Users/me/Documents/report.pdf"
}
Lưu ý: Upload có thể mất 30–60 giây. Đặt
--max-time 90khi dùng curl. Notebook phải do bạn sở hữu (không phải notebook public/shared).
Chat
POST /api/notebooks/:id/chat
Gửi câu hỏi và chờ câu trả lời đầy đủ (đồng bộ).
Body:
{ "message": "Câu hỏi của bạn" }
curl -X POST http://localhost:3456/api/notebooks/<id>/chat \
-H "Content-Type: application/json" \
-d '{"message":"Tóm tắt các điểm chính trong tài liệu này"}' \
--max-time 120
{
"ok": true,
"question": "Tóm tắt các điểm chính trong tài liệu này",
"answer": "Dựa trên các tài liệu, các điểm chính bao gồm: ..."
}
Timeout mặc định 90 giây. Câu hỏi phức tạp với nhiều sources có thể cần lâu hơn.
GET /api/notebooks/:id/chat/history
Lấy toàn bộ lịch sử hội thoại trong notebook.
curl http://localhost:3456/api/notebooks/<id>/chat/history
{
"ok": true,
"total": 4,
"history": [
{ "role": "user", "content": "Tóm tắt tài liệu" },
{ "role": "assistant", "content": "Tài liệu đề cập đến..." }
]
}
WS /api/notebooks/:id/chat/stream
Chat streaming qua WebSocket. Nhận câu trả lời theo từng chunk realtime.
Kết nối: ws://localhost:3456/api/notebooks/<id>/chat/stream
Gửi: { "message": "câu hỏi" }
Nhận:
{ "type": "connected", "notebookId": "<id>" }
{ "type": "chunk", "data": "Dựa trên" }
{ "type": "chunk", "data": " các tài liệu..." }
{ "type": "done", "data": { "answer": "Dựa trên các tài liệu..." } }
{ "type": "error", "data": "Thông báo lỗi" }
Ví dụ JavaScript:
const ws = new WebSocket('ws://localhost:3456/api/notebooks/<id>/chat/stream');
ws.onopen = () => ws.send(JSON.stringify({ message: 'Tóm tắt nội dung chính' }));
ws.onmessage = ({ data }) => {
const msg = JSON.parse(data);
if (msg.type === 'chunk') process.stdout.write(msg.data);
if (msg.type === 'done') console.log('\n[Xong]');
if (msg.type === 'error') console.error('[Lỗi]', msg.data);
};
Test nhanh bằng wscat:
npx wscat -c "ws://localhost:3456/api/notebooks/<id>/chat/stream"
# Sau khi kết nối:
{"message":"Tóm tắt tài liệu này"}
Trạng thái server
GET /health
curl http://localhost:3456/health
{
"ok": true,
"status": "running",
"queue": { "busy": false, "pending": 0 }
}
queue.busy = true — đang có thao tác browser chạy; các request khác xếp hàng chờ tự động.
Mã lỗi
| HTTP | error |
Ý nghĩa |
|---|---|---|
| 400 | Thiếu type hoặc content |
Body thiếu field bắt buộc |
| 401 | NOT_AUTHENTICATED |
Chưa đăng nhập → gọi POST /api/auth/login |
| 401 | Thiếu hoặc sai API key |
Header x-api-key sai hoặc thiếu |
| 404 | NOTEBOOK_NOT_FOUND |
Notebook ID không tồn tại hoặc không có quyền |
| 500 | (message) | Lỗi nội bộ — xem log server |
Tích hợp
n8n / Make / Zapier
- Node: HTTP Request
- URL:
http://<ip-máy>:3456/api/notebooks/<id>/chat - Method:
POST - Body (JSON):
{"message": "{{$json.input}}"}
Python
import requests
BASE = "http://localhost:3456"
NB_ID = "<notebook-id>"
# Thêm file
requests.post(f"{BASE}/api/notebooks/{NB_ID}/sources",
json={"type": "file", "content": "/path/to/file.pdf"},
timeout=90)
# Thêm URL
requests.post(f"{BASE}/api/notebooks/{NB_ID}/sources",
json={"type": "url", "content": "https://example.com"},
timeout=30)
# Hỏi
resp = requests.post(f"{BASE}/api/notebooks/{NB_ID}/chat",
json={"message": "Tóm tắt tài liệu"}, timeout=120)
print(resp.json()["answer"])
# Danh sách notebooks
notebooks = requests.get(f"{BASE}/api/notebooks").json()["notebooks"]
Node.js / fetch
const BASE = 'http://localhost:3456';
const NB_ID = '<notebook-id>';
// Thêm file
await fetch(`${BASE}/api/notebooks/${NB_ID}/sources`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'file', content: '/path/to/file.pdf' }),
signal: AbortSignal.timeout(90_000),
});
// Hỏi
const res = await fetch(`${BASE}/api/notebooks/${NB_ID}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'Tóm tắt nội dung' }),
signal: AbortSignal.timeout(120_000),
});
const { answer } = await res.json();
Lưu ý quan trọng
- Chỉ 1 thao tác browser tại một thời điểm — các request song song xếp hàng tự động
- Upload file chỉ hoạt động với notebook do bạn sở hữu (không phải public/shared)
- Dừng server đúng cách:
Ctrl+C— KHÔNG dùngkill -9 - Chrome crash: xoá
chrome-profile/SingletonLockrồi restart server - Google giới hạn automation: nếu bị chặn, đợi vài phút