跳至文章內容

部落格觀察 API

公開取用最新文章、部落格目錄、正文提及關係與話題排行;資料只涵蓋已收錄且驗證的部落格。

使用規則

  • 基底網址 https://blog.observe.tw。公開查詢使用 GET(也接受 HEAD),回傳 UTF-8 JSON。
  • API 狀態頁:/api/status。瀏覽器以 HTML 顯示;以 Accept: application/json 取得 JSON,只顯示 API 狀態與端點用途。狀態包含 ok 或 degraded,以有界可用性檢查確認公開查詢的資料依賴可連線,最多快取 5 秒;不是每個端點逐筆測試。JSON 欄位為 status、version、endpoints(每項 path、summary)與 updatedAt;這是狀態回應產生時間,不是文章資料更新時間。此回應為 no-store,不提供內部主機或爬蟲維運資訊。
  • 公開查詢不需要金鑰。每個 IP 每分鐘最多 60 次,超過回 429;回應帶 x-ratelimit-limit、x-ratelimit-remaining、x-ratelimit-reset。登入後可在 /my/ 建立個人 API 金鑰(每人最多 3 把),以 x-api-key: blog_… 傳送,每把每分鐘 1000 次;伺服器只存 SHA-256 摘要,完整金鑰只顯示一次。無效或撤銷的金鑰回 401。為防止隨機金鑰探測,首次出現或未快取的金鑰須先通過每 IP 每分鐘 60 次的匿名/驗證桶;若該桶已用完會先回 429,請等窗口重設後重試。已快取且有效的金鑰不受此 IP 桶限制,每把每分鐘 1000 次額度不變。金鑰不能用來登入、管理或讀取私人資料,Authorization 不是本 API 的金鑰認證方式。
  • 允許跨網域:Access-Control-Allow-Origin: *,不接受跨域 cookie 認證。瀏覽器可讀取限流標頭與 Retry-After;遇 429 請依 Retry-After 秒數退避。限流標頭表示產生回應時的額度;來源站對實際到站請求計額。
  • 時間一律使用 UTC ISO 8601(例如 2026-10-12T00:00:00.000Z);「一天」指台北時間(UTC+8)的日曆日,hours 則是連續經過的小時數。未知日期回 null,不以抓取日期冒充發文日期。
  • 部落格 slug 與話題 id 是穩定 API 識別碼,不因網頁網址改名而變動。已建立可讀網頁網址時,項目另附選用的 canonicalPath,文章附 blogCanonicalPath;以基底網址加上此相對路徑即可分享。欄位暫缺時仍可使用原識別碼網址,網頁會 301 到正式網址;不要拿可讀 slug 取代 API 參數。
  • 匿名成功回應帶 cache-control: no-store。文章依各站 RSS/抓取排程分批更新,查詢不保留跨請求結果快取,讓隱藏或刪除內容可即時反映;低頻部落格不因久未發文而失效。請至少間隔 120 秒輪詢,關係以 5 分鐘以上為宜。更新與歷史回補有延遲,不保證每輪都有新資料。
  • 帶 x-api-key 或 Authorization 的請求與錯誤回應為 private, no-store 或 no-store,不得進共享快取。快取標頭不代表 CDN 已快取該端點;目前 Cloudflare 的專用 HTML 規則只涵蓋首頁。
  • 錯誤格式為 {"error":"..."}:400 參數/游標錯誤、401 無效金鑰、404 找不到、410 話題證據已全部撤銷、405 不支援的方法、429 太頻繁、503 暫時無法提供服務。未知參數、重複參數與不合法的時間窗口會回 400。
  • 路徑參數請 URL 編碼(例如話題 rss:1 寫成 rss%3A1),路徑結尾不要加 /。文章與目錄使用 limit、cursor/nextCursor;游標是不透明字串,請原樣傳回並保持原查詢條件。nextCursor: null 表示這個結果已到尾頁。
  • 變更紀錄(2026-10-12,已核准的破壞性變更):GET /api/v1/blogs 的 items[].url 與 GET /api/v1/blogs/{slug} 的 blog.url 改為本站可讀網頁絕對網址;原站首頁移至同物件的 sourceUrl(可為 null)。原先使用 blog.url 前往原站的程式請改讀 sourceUrl。GET /api/v1/topics 的 items[].url 及 GET /api/v1/topics/{id} 的 topic.url 為新增的本站網頁網址。ID/slug/favicon 識別碼不變,文章 url 仍是原文;內部 /api/* 的原站 url 不改。缺少 registry 時,本站 url 暫用可轉址的舊識別碼路徑;純標點話題不提供網址。不提供 pageUrl 系列欄位。
  • 除上述明列的已核准變更外,v1 內只做向後相容的變更(新增欄位、新增端點);移除或改名會先公告。客戶端應忽略未知的新欄位。既有站內 /api/* 介面保留給網站使用,不是本文件的穩定公開契約;本站沒有舊版 PHP API。
  • 部落格文章、標題與圖片著作權屬原作者。API 只提供標題、原文連結、統計及最多 500 字元的短摘要,不提供全文;私人追蹤、個人 RSS、候選證據、管理及 AI 原始輸入不在公開 API。使用統計請註明「資料來源:部落格觀察 blog.observe.tw」,閱讀文章請連回原站。
  • 不寫程式也能追:訂閱各部落格原站提供的 RSS/Atom;登入 /my/ 可管理追蹤與匯出 /me/opml 給閱讀器,OPML 只包含已成功抓取的 RSS/Atom,沒有可用 feed 的站會略過,請到原站確認訂閱方式。/me/feed.xml 是需要瀏覽器登入 session 的私人摘要 RSS,不是可公開分享或以 API 金鑰讀取的 feed。
  • 變更紀錄(2026-10-13):話題詳情新增選用 history 累積歷史清單與 historyCursor 分頁。話題曾有有效比對證據就保留,不因退出每日詞庫窗口消失;只公開標題、原文連結及觀測狀態,絕不提供全文。全部證據明確撤銷時回 410,合併網頁一次 301 到最終話題。
  • 正文提及是交流訊號,不是抄襲或背書判斷。引用占比的分母為來源站在窗口內所有已解析、指向已收錄目標的「文章—目標部落格」提及數;一篇提及三站會計三筆,不是占全站文章的百分比。覆蓋與截斷欄位應與排行一起閱讀。
  • 話題詞條以新文易數維護的標籤池為唯一來源;目前 observed-article-window 僅代表觀測窗口內出現的詞,不代表完整詞庫。NFKC 與大小寫正規化後合併,顯示文字取同快照中上游文章出現次數最多的寫法。只比對標題與清理後正文;同篇取標題權重 2 或正文權重 1,依權重由高到低排列後,同站後續文章貢獻依序乘上 0.5。只命中正文時即 1、0.5、0.25…;2 篇標題加 1 篇正文合計 3.25。排除詞保留原始資料與原因,可透過話題詳情查詢;RSS 分類不另建排行詞庫。
  • 排行另使用新文易數 broad 門檻(至少三站、兩站各兩篇且一站三篇),配合既有文件頻率門檻。若詞在查詢窗口內超過 2% 公開文章命中,排行只計標題證據;原詞、正文命中與歷史話題頁保留。matchingPolicy.displayGate 記錄門檻與來源,matchingReason=high-window-document-frequency-title-only 可查核正文降權原因;此規則不改原 matcher/policy 版本,也不是另建通用詞黑名單。
  • 若跨站詞在長期基準中少於三分之一命中文章出現在標題,熱門排行另要求至少兩個公開部落格的文章原站標籤/分類明確使用該詞;否則僅退出排行和文字雲,保留詞庫、比對、話題頁與歷史證據。matchingPolicy.displayGate.background 記錄門檻。這是全詞統一的背景詞品質規則,不含硬編的排除詞清單。
  • coverage.matchedPosts 是依既有高頻詞標題/正文規則認定的有效比對文章數,包含因跨站不足或背景詞品質退出熱門排行的文章;不是所有原始 topic_matches 紀錄數。高頻詞僅正文命中不計入此有效比對統計,但原索引與歷史證據仍保留。

快速開始

最新文章與游標分頁(curl + jq)

BASE='https://blog.observe.tw'
page=$(curl --fail-with-body -sS "$BASE/api/v1/articles?hours=720&limit=10")
printf '%s\n' "$page" | jq '.items[] | {title, url, blogName, publishedAt}'
cursor=$(printf '%s\n' "$page" | jq -r '.nextCursor // empty')
if [ -n "$cursor" ]; then
  curl --fail-with-body -sS -G "$BASE/api/v1/articles" --data-urlencode "hours=720" --data-urlencode "limit=10" --data-urlencode "cursor=$cursor" | jq '.items'
fi

某部落格的文章與提及關係(curl + jq)

BASE='https://blog.observe.tw'
slug=$(curl --fail-with-body -sS "$BASE/api/v1/blogs?limit=1" | jq -r '.items[0].slug // empty')
if [ -n "$slug" ]; then
  encoded=$(printf '%s' "$slug" | jq -sRr @uri)
  curl --fail-with-body -sS "$BASE/api/v1/blogs/$encoded/articles?hours=2160&limit=10" | jq '.items'
  curl --fail-with-body -sS "$BASE/api/v1/blogs/$encoded/relations?hours=2160" | jq '.outgoing, .incoming, .coverage'
fi

誰提到誰、熱門話題與部落格目錄(curl + jq)

curl --fail-with-body -sS 'https://blog.observe.tw/api/v1/relations?hours=720' | jq '.nodes, .edges, .coverage'
curl --fail-with-body -sS 'https://blog.observe.tw/api/v1/topics?hours=168&limit=10' | jq '.items, .vocabulary'
curl --fail-with-body -sS -G 'https://blog.observe.tw/api/v1/blogs' --data-urlencode 'q=旅行' --data-urlencode 'limit=20' | jq '.items, .nextCursor'

JavaScript:有上限的文章分頁(瀏覽器或 Node 24)

const endpoint = 'https://blog.observe.tw/api/v1/articles';
let cursor = null;
for (let page = 0; page < 3; page++) {
  const query = new URLSearchParams({ hours: "720", limit: "50" });
  if (cursor) query.set("cursor", cursor);
  const response = await fetch(endpoint + "?" + query);
  if (!response.ok) throw new Error("HTTP " + response.status + ": " + await response.text());
  const result = await response.json();
  for (const article of result.items) console.log(article.blogName, article.title, article.url);
  cursor = result.nextCursor;
  if (!cursor) break;
  await new Promise(resolve => setTimeout(resolve, 1100));
}
// 這是同一輪分頁;開始下一輪輪詢前至少等 120 秒。

端點一覽

最新文章與搜尋

GETHEAD/api/v1/articles

最新文章與標題搜尋

依發文時間(未知時用首次收錄時間)再依 ID 由新到舊;publishedAt 仍保留 null。游標固定第一頁窗口與篩選條件,後續請保持相同參數。

參數型別說明
limit
integer每頁筆數,1–200,預設 50
cursor
string上一頁的 nextCursor,不可修改或跨篩選條件使用
hours
integer最近幾小時;不得與 since/until 同時使用,1–2160,預設 720
q
string名稱或標題的純文字包含搜尋;不是 SQL/正規表示式
since
string (ISO 時間)窗口起點(UTC ISO 8601,秒或三位毫秒且以 Z 結尾),必須與 until 成對,跨度最多 90 天
until
string (ISO 時間)窗口終點(UTC ISO 8601,秒或三位毫秒且以 Z 結尾),必須與 since 成對
blog
string限制部落格代碼
curl -s 'https://blog.observe.tw/api/v1/articles'
在瀏覽器開啟這個範例 ↗
回應欄位18 個
欄位型別說明
items
object[]
items[].id
integer文章 ID
items[].title
string原文標題
items[].url
string原文網址
items[].author
string | null原站署名,不代表跨站作者身分相同
items[].publishedAt
string (ISO 時間) | null原站發文時間
items[].updatedAt
string (ISO 時間) | nullRSS/原站提供的文章更新時間
items[].imageUrl
string | null圖片網址;沒有圖片時為 null
items[].blogSlug
string部落格代碼
items[].blogName
string部落格名稱
items[].summary
string純文字短摘要;不提供全文
items[].observedAt
string (ISO 時間) | null本站首次收錄時間
items[].iconUrl
string | null部落格圖示
items[].blogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
nextCursor
string | null下一頁游標;null 表示末頁
window
object此輪固定窗口;游標保留窗口與篩選條件
window.since
string (ISO 時間)
window.until
string (ISO 時間)

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

GETHEAD/api/v1/blogs/{slug}/articles

某部落格的文章

參數型別說明
slug路徑
string從目錄取得的部落格代碼
limit
integer每頁筆數,1–200,預設 50
cursor
string上一頁的 nextCursor,不可修改或跨篩選條件使用
hours
integer最近幾小時;不得與 since/until 同時使用,1–2160,預設 720
q
string名稱或標題的純文字包含搜尋;不是 SQL/正規表示式
since
string (ISO 時間)窗口起點(UTC ISO 8601,秒或三位毫秒且以 Z 結尾),必須與 until 成對,跨度最多 90 天
until
string (ISO 時間)窗口終點(UTC ISO 8601,秒或三位毫秒且以 Z 結尾),必須與 since 成對
curl -s 'https://blog.observe.tw/api/v1/blogs/skyhong-tw/articles'
在瀏覽器開啟這個範例 ↗
回應欄位18 個
欄位型別說明
items
object[]
items[].id
integer文章 ID
items[].title
string原文標題
items[].url
string原文網址
items[].author
string | null原站署名,不代表跨站作者身分相同
items[].publishedAt
string (ISO 時間) | null原站發文時間
items[].updatedAt
string (ISO 時間) | nullRSS/原站提供的文章更新時間
items[].imageUrl
string | null圖片網址;沒有圖片時為 null
items[].blogSlug
string部落格代碼
items[].blogName
string部落格名稱
items[].summary
string純文字短摘要;不提供全文
items[].observedAt
string (ISO 時間) | null本站首次收錄時間
items[].iconUrl
string | null部落格圖示
items[].blogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
nextCursor
string | null下一頁游標;null 表示末頁
window
object此輪固定窗口;游標保留窗口與篩選條件
window.since
string (ISO 時間)
window.until
string (ISO 時間)

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

部落格目錄

GETHEAD/api/v1/blogs

已驗證部落格目錄

依 ID 由小到大,使用游標續頁;不公開未核准的探索候選。

參數型別說明
limit
integer每頁筆數,1–200,預設 50
cursor
string上一頁的 nextCursor,不可修改或跨篩選條件使用
q
string名稱或標題的純文字包含搜尋;不是 SQL/正規表示式
curl -s 'https://blog.observe.tw/api/v1/blogs'
在瀏覽器開啟這個範例 ↗
回應欄位15 個
欄位型別說明
items
object[]
items[].id
integer部落格 ID
items[].slug
string部落格代碼
items[].name
string部落格名稱
items[].url
string本站部落格可讀網頁絕對網址;2026-10-12 起原站網址改存 sourceUrl
items[].sourceUrl
string | null原站部落格首頁;原 blog.url 的值
items[].category
string原始分類,不代表權威分類
items[].description
string公開短簡介
items[].iconUrl
string | null本站圖示網址
items[].discoveredAt
string (ISO 時間) | null收錄時間
items[].lastCrawlSuccessAt
string (ISO 時間) | null最近抓取成功時間
items[].lastPublishedAt
string (ISO 時間) | null最近發文時間
items[].expectedIntervalHours
integer預期更新間隔,單位小時
items[].canonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
nextCursor
string | null下一頁游標

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

GETHEAD/api/v1/blogs/{slug}

單一部落格資料

參數型別說明
slug路徑
string從目錄取得的部落格代碼
curl -s 'https://blog.observe.tw/api/v1/blogs/skyhong-tw'
在瀏覽器開啟這個範例 ↗
回應欄位17 個
欄位型別說明
blog
object
blog.id
integer部落格 ID
blog.slug
string部落格代碼
blog.name
string部落格名稱
blog.url
string本站部落格可讀網頁絕對網址;2026-10-12 起原站網址改存 sourceUrl
blog.sourceUrl
string | null原站部落格首頁;原 blog.url 的值
blog.category
string原始分類,不代表權威分類
blog.description
string公開短簡介
blog.iconUrl
string | null本站圖示網址
blog.discoveredAt
string (ISO 時間) | null收錄時間
blog.lastCrawlSuccessAt
string (ISO 時間) | null最近抓取成功時間
blog.lastPublishedAt
string (ISO 時間) | null最近發文時間
blog.expectedIntervalHours
integer預期更新間隔,單位小時
blog.canonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
blog.feeds
object[]
blog.feeds[].url
string原站 feed 位址
blog.feeds[].type
stringrss/atom/sitemap

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

部落格提及關係

GETHEAD/api/v1/relations

誰提到誰與互相提及排行

只計正文且雙方均為已驗證部落格。全站圖最多 500 條邊,排行有獨立上限;請檢查 coverage 截斷標記。

參數型別說明
hours
168 | 720 | 2160最近 7/30/90 天,預設 720
curl -s 'https://blog.observe.tw/api/v1/relations'
在瀏覽器開啟這個範例 ↗
回應欄位44 個
欄位型別說明
hours
integer
days
integer
generatedAt
string (ISO 時間)
nodes
object[]
nodes[].id
integer
nodes[].slug
string
nodes[].name
string
nodes[].iconUrl
string | null
nodes[].canonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
edges
object[]全站最多 500 條圖形邊
edges[].sourceBlogId
integer
edges[].targetBlogId
integer
edges[].count
integer不同來源文章提及此目標的篇數
edges[].share
numbercount ÷ 來源站已解析的文章—目標站提及數,不是文章總數占比
edges[].firstMentionAt
string (ISO 時間) | null
edges[].lastMentionAt
string (ISO 時間) | null
edges[].mutual
boolean窗口內是否也有反向正文提及
edges[].sourceBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
edges[].targetBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
mostMentioned
object[]
mostMentioned[].blogId
integer
mostMentioned[].mentions
integer
mostMentioned[].incomingBlogs
integer
mutual
object[]
mutual[].sourceBlogId
integer
mutual[].targetBlogId
integer
mutual[].forwardCount
integer
mutual[].reverseCount
integer
coverage
object解析覆蓋統計;並非所有文章皆已分析
coverage.observedPosts
integer
coverage.totalPosts
integer
coverage.failedPosts
integer
coverage.noBodyPosts
integer
coverage.unknownDatePosts
integer
coverage.unresolvedLinks
integer
coverage.truncatedPosts
integer
coverage.graphTruncated
boolean
coverage.aggregationTruncated
boolean
coverage.sampledAt
string (ISO 時間) | null
coverage.method
string
coverage.nonBodyLinks
object
coverage.nonBodyLinks.sidebar
integer
coverage.nonBodyLinks.blogroll
integer
coverage.nonBodyLinks.comment
integer

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

GETHEAD/api/v1/blogs/{slug}/relations

某部落格提到誰、被誰提到

單站關係不受全站圖形 500 邊上限影響,最多 500 邊及 20 筆公開連結證據;不提供正文片段。

參數型別說明
slug路徑
string從目錄取得的部落格代碼
hours
168 | 720 | 2160最近 7/30/90 天,預設 720
curl -s 'https://blog.observe.tw/api/v1/blogs/skyhong-tw/relations'
在瀏覽器開啟這個範例 ↗
回應欄位76 個
欄位型別說明
hours
integer
days
integer
generatedAt
string (ISO 時間)
nodes
object[]
nodes[].id
integer
nodes[].slug
string
nodes[].name
string
nodes[].iconUrl
string | null
nodes[].canonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
edges
object[]全站最多 500 條圖形邊
edges[].sourceBlogId
integer
edges[].targetBlogId
integer
edges[].count
integer不同來源文章提及此目標的篇數
edges[].share
numbercount ÷ 來源站已解析的文章—目標站提及數,不是文章總數占比
edges[].firstMentionAt
string (ISO 時間) | null
edges[].lastMentionAt
string (ISO 時間) | null
edges[].mutual
boolean窗口內是否也有反向正文提及
edges[].sourceBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
edges[].targetBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
mostMentioned
object[]
mostMentioned[].blogId
integer
mostMentioned[].mentions
integer
mostMentioned[].incomingBlogs
integer
mutual
object[]
mutual[].sourceBlogId
integer
mutual[].targetBlogId
integer
mutual[].forwardCount
integer
mutual[].reverseCount
integer
coverage
object解析覆蓋統計;並非所有文章皆已分析
coverage.observedPosts
integer
coverage.totalPosts
integer
coverage.failedPosts
integer
coverage.noBodyPosts
integer
coverage.unknownDatePosts
integer
coverage.unresolvedLinks
integer
coverage.truncatedPosts
integer
coverage.graphTruncated
boolean
coverage.aggregationTruncated
boolean
coverage.sampledAt
string (ISO 時間) | null
coverage.method
string
coverage.nonBodyLinks
object
coverage.nonBodyLinks.sidebar
integer
coverage.nonBodyLinks.blogroll
integer
coverage.nonBodyLinks.comment
integer
blogId
integer
outgoing
object[]
outgoing[].sourceBlogId
integer
outgoing[].targetBlogId
integer
outgoing[].count
integer不同來源文章提及此目標的篇數
outgoing[].share
numbercount ÷ 來源站已解析的文章—目標站提及數,不是文章總數占比
outgoing[].firstMentionAt
string (ISO 時間) | null
outgoing[].lastMentionAt
string (ISO 時間) | null
outgoing[].mutual
boolean窗口內是否也有反向正文提及
outgoing[].sourceBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
outgoing[].targetBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
incoming
object[]
incoming[].sourceBlogId
integer
incoming[].targetBlogId
integer
incoming[].count
integer不同來源文章提及此目標的篇數
incoming[].share
numbercount ÷ 來源站已解析的文章—目標站提及數,不是文章總數占比
incoming[].firstMentionAt
string (ISO 時間) | null
incoming[].lastMentionAt
string (ISO 時間) | null
incoming[].mutual
boolean窗口內是否也有反向正文提及
incoming[].sourceBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
incoming[].targetBlogCanonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
evidence
object[]
evidence[].postId
integer
evidence[].postTitle
string
evidence[].postUrl
string
evidence[].targetUrl
string
evidence[].anchor
string
evidence[].publishedAt
string (ISO 時間) | null
evidence[].sourceBlogId
integer
evidence[].targetBlogId
integer
evidence[].location
"body"
profileTruncated
boolean

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

話題與關鍵字

GETHEAD/api/v1/topics

熱門話題排行

只列至少兩個部落格寫過的話題。依 limit 最多回應 200 筆排行;truncated 表示仍有其他結果,此端點不提供 cursor。

參數型別說明
hours
168 | 720 | 2160最近 7/30/90 天,預設 720
limit
integer每頁筆數,1–200,預設 50
curl -s 'https://blog.observe.tw/api/v1/topics'
在瀏覽器開啟這個範例 ↗
回應欄位79 個
欄位型別說明
items
object[]
items[].id
stringtag:<NFKC/大小寫正規化詞條 SHA-256>;舊 tag/rss ID 可在詳情解析
items[].label
string
items[].source
"rss-category" | "tag-vocabulary"
items[].score
number同篇取標題 2 或正文 1;同站按權重排序後依序減半加總
items[].posts
integer
items[].blogs
integer
items[].titlePosts
integer
items[].bodyOnlyPosts
integer
items[].recentShare
number | null
items[].baselineShare
number | null
items[].lift
number | null近期文章占比 ÷ 長期基準占比;無基準時為 null,不代表零
items[].matchingRule
"title-only" | "title-and-body"
items[].matchingReason
string | null
items[].canonicalPath
string可讀網頁的相對 canonical 路徑;選用,不取代原 API 識別碼
items[].url
string本站話題網頁絕對網址;選用,純標點話題不提供
nextCursor
| null排行僅提供有界快照,不提供游標續頁
hours
integer
vocabulary
object
vocabulary.status
"ready" | "unavailable"
vocabulary.source
string
vocabulary.version
string | null
vocabulary.coverage
string | null
vocabulary.syncedAt
string (ISO 時間) | null
vocabulary.window
object | null
vocabulary.window.since
string (ISO 時間)
vocabulary.window.until
string (ISO 時間)
vocabulary.invalidTagCount
integer | null
vocabulary.normalizedTagCount
integer | null
vocabulary.tagNormalization
string | null
vocabulary.upstreamSnapshotConsistent
boolean
vocabulary.excludedTermCount
integer保留但不參與比對的 canonical 詞條數
vocabulary.exclusionReasons
{鍵: integer}排除原因與詞條數
vocabulary.frequencyBaseline
object | null
vocabulary.frequencyBaseline.since
string (ISO 時間)
vocabulary.frequencyBaseline.until
string (ISO 時間)
vocabulary.frequencyBaseline.totalPosts
integer
vocabulary.frequencyBaseline.coveredPosts
integer
vocabulary.frequencyBaseline.thresholdRatio
number
vocabulary.frequencyBaseline.version
string
vocabulary.totalSourceTerms
integer | null保留的上游原始詞數,包含未參與比對的詞
vocabulary.activeSet
object | null
vocabulary.activeSet.status
string
vocabulary.activeSet.version
string | null
vocabulary.activeSet.asOf
string (ISO 時間) | null
vocabulary.activeSet.eligibleTerms
integer
vocabulary.activeSet.activeTerms
integer
vocabulary.activeSet.termLimit
integer
vocabulary.activeSet.codePointLimit
integer
vocabulary.activeSet.memoryBudgetBytes
integer
vocabulary.activeSet.estimatedBytes
integer
vocabulary.activeSet.inactiveReasons
{鍵: integer}low-frequency 或 memory-budget;原詞仍保留
matchingPolicy
object
matchingPolicy.version
string
matchingPolicy.policyVersion
string
matchingPolicy.titleWeight
number
matchingPolicy.bodyWeight
number
matchingPolicy.sameBlogDecay
number
matchingPolicy.sources
"title" | "body"[]
matchingPolicy.recentBackfillHours
integer
matchingPolicy.frequency
{鍵: object}
matchingPolicy.publicationNoiseVersion
string公開呈現的純標點雜訊規則版本;不改原始詞條 ID
matchingPolicy.displayGate
object選用的排行展示品質門檻
matchingPolicy.displayGate.version
string展示門檻版本;不取代 matcher/policy 版本
matchingPolicy.displayGate.upstream
string移植的 tag 跨站 broad 門檻來源
matchingPolicy.displayGate.minimumSources
integer至少三個部落格
matchingPolicy.displayGate.minimumTwiceSources
integer至少兩站各兩篇
matchingPolicy.displayGate.minimumPeakPosts
integer至少一站三篇
matchingPolicy.displayGate.documentFrequencyRatio
number查詢窗口內高文件頻率門檻,目前 0.02
matchingPolicy.displayGate.reason
stringhigh-window-document-frequency-title-only
matchingPolicy.displayGate.effect
string只影響排行的正文貢獻;保留來源池及歷史證據
matchingPolicy.displayGate.background
object選用的背景詞原站分類佐證門檻
matchingPolicy.displayGate.background.maximumTitleShare
number長期基準標題命中占比門檻,目前三分之一
matchingPolicy.displayGate.background.minimumBaselinePosts
integer至少三篇基準命中文章
matchingPolicy.displayGate.background.minimumNativeBlogs
integer至少兩個公開部落格的原站分類/標籤
matchingPolicy.displayGate.background.reason
stringbody-dominant-without-native-label-consensus
minimumBlogs
integer
generatedAt
string (ISO 時間)
truncated
boolean

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable

GETHEAD/api/v1/topics/{id}

某話題有哪些部落格在寫

近期最多 100 個部落格與 100 篇文章;請檢查 truncated。history 是累積曾有比對證據的公開文章,獨立於 hours 與每日來源窗口;近期為空時仍可讀歷史。pending 僅表示過去觀測、待重新確認,不代表現在仍命中。全部證據明確撤銷回 410;不存在或暫不可公開回 404。

參數型別說明
id路徑
string從話題排行取得的 ID,請 URL 編碼
hours
168 | 720 | 2160最近 7/30/90 天,預設 720
historyCursor
stringhistory.nextCursor;累積歷史文章續頁,保持同一話題 ID
curl -s 'https://blog.observe.tw/api/v1/topics/rss%3A1'
在瀏覽器開啟這個範例 ↗
回應欄位108 個
欄位型別說明
hours
integer
generatedAt
string (ISO 時間)
topic
object
topic.id
string
topic.label
string
topic.source
string
topic.canonicalId
string | null
topic.excludedReason
string | nulltoo-short、pure-number、pure-date、tag-noise、no-equal、upstream-noise、upstream-no-equal、invalid-label、empty-label、punctuation-only 或 not-in-tag-vocabulary
topic.inactiveReason
string | nulllow-frequency 或 memory-budget;與雜訊排除分開
topic.matchingStatus
"active" | "inactive" | "excluded"
topic.articleCount
integer | null
topic.lastPublishedAt
string (ISO 時間) | null
topic.matchingRule
"title-only" | "title-and-body"
topic.matchingReason
string | null
topic.canonicalPath
string本站可讀網頁相對路徑;選用
topic.url
string本站話題網頁絕對網址;選用
vocabulary
object
vocabulary.status
"ready" | "unavailable"
vocabulary.source
string
vocabulary.version
string | null
vocabulary.coverage
string | null
vocabulary.syncedAt
string (ISO 時間) | null
vocabulary.window
object | null
vocabulary.window.since
string (ISO 時間)
vocabulary.window.until
string (ISO 時間)
vocabulary.invalidTagCount
integer | null
vocabulary.normalizedTagCount
integer | null
vocabulary.tagNormalization
string | null
vocabulary.upstreamSnapshotConsistent
boolean
vocabulary.excludedTermCount
integer保留但不參與比對的 canonical 詞條數
vocabulary.exclusionReasons
{鍵: integer}排除原因與詞條數
vocabulary.frequencyBaseline
object | null
vocabulary.frequencyBaseline.since
string (ISO 時間)
vocabulary.frequencyBaseline.until
string (ISO 時間)
vocabulary.frequencyBaseline.totalPosts
integer
vocabulary.frequencyBaseline.coveredPosts
integer
vocabulary.frequencyBaseline.thresholdRatio
number
vocabulary.frequencyBaseline.version
string
vocabulary.totalSourceTerms
integer | null保留的上游原始詞數,包含未參與比對的詞
vocabulary.activeSet
object | null
vocabulary.activeSet.status
string
vocabulary.activeSet.version
string | null
vocabulary.activeSet.asOf
string (ISO 時間) | null
vocabulary.activeSet.eligibleTerms
integer
vocabulary.activeSet.activeTerms
integer
vocabulary.activeSet.termLimit
integer
vocabulary.activeSet.codePointLimit
integer
vocabulary.activeSet.memoryBudgetBytes
integer
vocabulary.activeSet.estimatedBytes
integer
vocabulary.activeSet.inactiveReasons
{鍵: integer}low-frequency 或 memory-budget;原詞仍保留
matchingPolicy
object
matchingPolicy.version
string
matchingPolicy.policyVersion
string
matchingPolicy.titleWeight
number
matchingPolicy.bodyWeight
number
matchingPolicy.sameBlogDecay
number
matchingPolicy.sources
"title" | "body"[]
matchingPolicy.recentBackfillHours
integer
matchingPolicy.frequency
{鍵: object}
matchingPolicy.publicationNoiseVersion
string公開呈現的純標點雜訊規則版本;不改原始詞條 ID
matchingPolicy.displayGate
object選用的排行展示品質門檻
matchingPolicy.displayGate.version
string展示門檻版本;不取代 matcher/policy 版本
matchingPolicy.displayGate.upstream
string移植的 tag 跨站 broad 門檻來源
matchingPolicy.displayGate.minimumSources
integer至少三個部落格
matchingPolicy.displayGate.minimumTwiceSources
integer至少兩站各兩篇
matchingPolicy.displayGate.minimumPeakPosts
integer至少一站三篇
matchingPolicy.displayGate.documentFrequencyRatio
number查詢窗口內高文件頻率門檻,目前 0.02
matchingPolicy.displayGate.reason
stringhigh-window-document-frequency-title-only
matchingPolicy.displayGate.effect
string只影響排行的正文貢獻;保留來源池及歷史證據
matchingPolicy.displayGate.background
object選用的背景詞原站分類佐證門檻
matchingPolicy.displayGate.background.maximumTitleShare
number長期基準標題命中占比門檻,目前三分之一
matchingPolicy.displayGate.background.minimumBaselinePosts
integer至少三篇基準命中文章
matchingPolicy.displayGate.background.minimumNativeBlogs
integer至少兩個公開部落格的原站分類/標籤
matchingPolicy.displayGate.background.reason
stringbody-dominant-without-native-label-consensus
blogs
object[]
blogs[].id
integer
blogs[].slug
string
blogs[].name
string
blogs[].posts
integer
blogs[].score
number
blogs[].titlePosts
integer
blogs[].bodyOnlyPosts
integer
blogs[].canonicalPath
string本站可讀網頁相對路徑;選用
articles
object[]
articles[].id
integer
articles[].title
string
articles[].url
string
articles[].publishedAt
string (ISO 時間) | null
articles[].blogSlug
string
articles[].blogName
string
articles[].blogCanonicalPath
string本站可讀網頁相對路徑;選用
truncated
boolean
history
object
history.articles
object[]
history.articles[].id
integer
history.articles[].title
string
history.articles[].url
string原文連結;不提供全文
history.articles[].publishedAt
string (ISO 時間) | null
history.articles[].blogSlug
string
history.articles[].blogName
string
history.articles[].blogCanonicalPath
string可讀部落格路徑
history.articles[].evidenceState
"confirmed" | "pending"confirmed 為已確認;pending 為過去觀測、待重新確認
history.articles[].observedAt
string (ISO 時間)本站最後觀測到提及的時間
history.nextCursor
string | null歷史清單下一頁游標;傳入 historyCursor,綁定話題不可跨用
history.confirmed
integer累積已確認的公開文章數
history.pending
integer累積待重新確認的公開文章數
history.lastObservedAt
string (ISO 時間) | null
history.total
integer累積不同公開文章數

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;410 gone(累積證據已全部撤銷);503 unavailable

API 本身

GETHEAD/api/v1/openapi.json

機器可讀 OpenAPI 規格

curl -s 'https://blog.observe.tw/api/v1/openapi.json'
在瀏覽器開啟這個範例 ↗
回應欄位4 個
欄位型別說明
openapi
string
info
object
paths
object
components
object

錯誤:400 invalid_query/invalid_cursor;401 invalid_api_key;404 not_found;405 method_not_allowed;503 unavailable