教學

LINE API 串接教學:Messaging API、Login 與 LIFF

給開發者的 2026 年 LINE API 全貌,以及不用 SDK 直接呼叫的方法:各 API 能做什麼、Channel 設定、用 fetch 回覆與推播、Webhook 簽章驗證、搭配 PKCE 的 LINE Login、LIFF 的 ID Token、速率限制與錯誤處理。

LineBot.pro Team· 更新 22 分鐘閱讀
LINE API 串接教學:Messaging API、Login 與 LIFF

#LINE API 能做什麼(2026)

LINE 開放給開發者的主要元件有四個:以 LINE 官方帳號收發訊息的 Messaging API、讓使用者用 LINE 帳號登入你的網站或 App 的 LINE Login、在 LINE 裡執行網頁應用程式的 LIFF(LINE Front-end Framework),以及把 LIFF 應用程式上架成 LINE 內服務的 LINE MINI App。這篇教學不使用 SDK,而是在 Node.js 22 用 fetch 直接以 HTTPS 呼叫這些 API,並附上 Python 版的 Webhook。內容依據 2026 年 10 月的 LINE Developers 官方文件查證。

API能做什麼常見用途
Messaging API回覆訊息;傳送推播、Multicast、Broadcast、Narrowcast 訊息;圖文選單、快速回覆、Flex Message;取得使用者個人檔案、受眾、統計資料、優惠券聊天機器人、訂單與預約通知、客服
LINE Login以 OAuth 2.0/OpenID Connect 登入;取得使用者 ID、顯示名稱與大頭貼;經 LINE 核准後可取得電子郵件;登入時邀請使用者將官方帳號加入好友網站或 App 的「使用 LINE 登入」、會員帳號綁定
LIFF在 LINE 內開啟、能辨識使用者的網頁應用程式(ID Token、個人檔案),可傳訊息到目前的聊天室、開啟分享對象選擇器、掃描 QR Code表單、會員卡、LINE 內的預約頁面
LINE MINI App由 LINE 上架成服務的 LIFF 應用程式;自 2026 年 3 月 11 日起,任何人都能在台灣與泰國發布未認證的 MINI App不必開發原生 App 的門市應用、預約、會員服務
LINE 社群外掛(Social Plugins)網站用的分享按鈕與加入好友按鈕分享文章、從網站增加好友

部分 API 只開放給通過申請的企業用戶(例如 LINE 通知型訊息)。另外有兩項服務在舊教學中仍常出現,但已經結束:LINE Notify 已於 2025 年 3 月 31 日終止服務(改用 LINE 官方帳號透過 Messaging API 傳送推播訊息,可參考 LINE Notify 替代方案);LINE Pay 在日本結束服務後,LINE 也於 2025 年 4 月停止提供 LINE Pay 的開發文件。

該選哪一個?

  • 回應客人或傳送通知的機器人 → Messaging API
  • 自家網站或 App 的登入功能 → LINE Login
  • 在 LINE 內開啟、知道使用者是誰的網頁 → LIFF(LINE 目前建議新的 LIFF 應用程式直接建立成 LINE MINI App)
  • 同時需要聊天與網頁介面 → 在同一個 Provider 下搭配 Messaging API 與 LIFF

想用官方 SDK 的話,@line/bot-sdk Express 中介軟體範例與 LINE Bot 開發教學會以 @line/bot-sdk v11 實作同樣的 Webhook。

#開發前準備:Provider、Channel 與憑證

1. 開發者帳號與 Provider:登入 LINE Developers Console,建立代表提供服務的公司或個人的 Provider。請謹慎選擇:Channel 之後無法移到別的 Provider,而且同一位 LINE 使用者在不同 Provider 下的使用者 ID 也不同。需要共用使用者 ID 的 Messaging API Channel 與 LINE Login Channel,必須放在同一個 Provider 下。

2. Messaging API Channel:自 2024 年 9 月 4 日起,無法再從 Console 直接建立 Messaging API Channel。請先建立 LINE 官方帳號,再到 LINE 官方帳號管理後台的設定 → Messaging API 啟用並選擇 Provider,Channel 就會出現在 Console 中。

3. LINE Login Channel:在同一個 Provider 下從 Console 建立,選擇 App 類型(Web app 或 Mobile app),並在 LINE Login 分頁登錄 Callback URL。

4. LIFF 應用程式:LIFF 應用程式要加在 LINE Login Channel 或 LINE MINI App Channel 中(每個 Channel 最多 30 個),不能加在 Messaging API Channel。新增後會取得 LIFF ID 與 LIFF URL(https://liff.line.me/{liffId})。

圖表
Provider "My Shop"
├── Messaging API channel   (與 LINE 官方帳號連動)
├── LINE Login channel      (網站登入+LIFF app)
└── LINE MINI App channel   (視需要)
憑證位置用途
Channel IDBasic settings 分頁LINE Login 的 client_id、驗證 ID Token、發行 Token
Channel secretBasic settings 分頁驗證 Webhook 簽章、LINE Login 換取 Token
Channel access tokenMessaging API 分頁(長效型)或透過 API 發行呼叫 Messaging API 時的 Authorization: Bearer 標頭
LIFF IDLINE Login 或 MINI App Channel 的 LIFF 分頁liff.init({ liffId })

Channel access token 共有四種:可自訂效期的 Token(v2.1,最長 30 天,LINE 建議使用)、無狀態 Token(15 分鐘)、短效型 Token(30 天),以及每個 Channel 一組的長效型 Token(不會過期,僅限 Messaging API)。所有密鑰都只放在伺服器端的環境變數或密鑰管理服務中:

bash
# .env
LINE_CHANNEL_ACCESS_TOKEN=...
LINE_CHANNEL_SECRET=...
LINE_LOGIN_CHANNEL_ID=...
LINE_LOGIN_CHANNEL_SECRET=...

#Messaging API:用 fetch 實作回覆、推播與 Multicast

呼叫 Messaging API 就是對 https://api.line.me 發出 HTTPS 請求,並在 Authorization 標頭帶上 Channel access token。下面這個小工具函式就能涵蓋大部分機器人的傳訊需求:

ts
// line-api.ts:用 fetch 寫的精簡 Messaging API 用戶端(Node.js 22 以上)
const API = "https://api.line.me/v2/bot"
const token = process.env.LINE_CHANNEL_ACCESS_TOKEN
if (!token) throw new Error("Set LINE_CHANNEL_ACCESS_TOKEN")

export type LineMessage = { type: string; [key: string]: unknown }

export class LineApiError extends Error {
  constructor(public status: number, public requestId: string | null, public body: string) {
    super(`LINE API ${status}: ${body}`)
  }
}

async function post(path: string, body: unknown, headers: Record<string, string> = {}) {
  const res = await fetch(`${API}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, ...headers },
    body: JSON.stringify(body),
  })
  if (!res.ok) throw new LineApiError(res.status, res.headers.get("x-line-request-id"), await res.text())
  return res.json() as Promise<{ sentMessages?: { id: string; quoteToken?: string }[] }>
}

// 回覆:需要 Webhook 事件的 replyToken,最多 5 則,不計入訊息則數
export function reply(replyToken: string, messages: LineMessage[]) {
  return post("/message/reply", { replyToken, messages })
}

// 推播:傳給 1 位使用者、1 個群組或聊天室;重試同一次推播時沿用同一個 retryKey(UUID)
export function push(to: string, messages: LineMessage[], retryKey: string) {
  return post("/message/push", { to, messages }, { "X-Line-Retry-Key": retryKey })
}

// Multicast:每次請求最多 500 個 user ID
export function multicast(to: string[], messages: LineMessage[]) {
  if (to.length > 500) throw new Error("multicast accepts at most 500 user IDs")
  return post("/message/multicast", { to, messages })
}

回覆還是推播? 回覆訊息要使用 Webhook 事件中的 replyToken。每個 Token 只能用一次,LINE 要求在收到 Webhook 後約 1 分鐘內使用(超過時間不保證能成功)。回覆訊息不計入每月訊息則數;推播、Multicast、Broadcast、Narrowcast 則會計入,而且是依收件人數計算,與一次請求中的訊息物件數量(最多 5 個)無關。已封鎖帳號的使用者不計入。

傳送方式Endpoint收件對象是否計入則數
回覆POST /v2/bot/message/reply該事件的使用者、群組或聊天室不計入
推播POST /v2/bot/message/push1 位使用者、1 個群組或聊天室計入
MulticastPOST /v2/bot/message/multicast最多 500 個使用者 ID計入
BroadcastPOST /v2/bot/message/broadcast所有好友計入
NarrowcastPOST /v2/bot/message/narrowcast受眾或依屬性篩選的對象計入

推播訊息可以傳給好友、帳號所在的群組與聊天室,以及過去 7 天內傳訊給帳號的使用者(即使不是好友)。推播因網路錯誤或 5xx 失敗而重試時,請帶上同一個 X-Line-Retry-Key:如果第一次請求已被受理,LINE 會回傳 409,而不會重複傳送。

ts
import { randomUUID } from "node:crypto"
import { push, type LineMessage } from "./line-api.js"

const userId = "U0123456789abcdef0123456789abcdef"
const welcome: LineMessage[] = [
  {
    type: "text",
    text: "您的訂單 #1042 已出貨。",
    quickReply: {
      items: [
        { type: "action", action: { type: "uri", label: "查詢物流", uri: "https://example.com/track/1042" } },
        { type: "action", action: { type: "message", label: "聯絡客服", text: "我想詢問訂單 #1042" } },
      ],
    },
  },
]
const retryKey = randomUUID()
const result = await push(userId, welcome, retryKey)
console.log(result.sentMessages?.map((m) => m.id))

訊息都是 JSON 物件,最常用的三種如下:

json
{ "type": "text", "text": "您好!請問需要什麼協助?" }
json
{
  "type": "image",
  "originalContentUrl": "https://example.com/photo.jpg",
  "previewImageUrl": "https://example.com/photo-preview.jpg"
}
json
{
  "type": "flex",
  "altText": "週末限定:飲品 8 折",
  "contents": {
    "type": "bubble",
    "hero": {
      "type": "image",
      "url": "https://example.com/promo.jpg",
      "size": "full",
      "aspectRatio": "20:13",
      "aspectMode": "cover"
    },
    "body": {
      "type": "box",
      "layout": "vertical",
      "contents": [
        { "type": "text", "text": "週末限定優惠", "weight": "bold", "size": "xl" },
        { "type": "text", "text": "全品項飲料 8 折,到週日為止", "wrap": true }
      ]
    },
    "footer": {
      "type": "box",
      "layout": "vertical",
      "contents": [
        {
          "type": "button",
          "style": "primary",
          "action": { "type": "uri", "label": "查看菜單", "uri": "https://example.com/menu" }
        }
      ]
    }
  }
}

文字訊息最多 5,000 個字元,快速回覆最多 13 個按鈕,所有圖片與連結都必須是 HTTPS。每月可免費傳送的則數依 LINE 官方帳號方案與國家而定,請參考 LINE 官方帳號費用指南;用量可透過 GET /v2/bot/message/quota 與 GET /v2/bot/message/quota/consumption 查詢。

#LINE Login:搭配 PKCE 的授權碼流程

LINE Login 遵循 OAuth 2.0 與 OpenID Connect,流程如下:

  1. 伺服器將使用者導向 https://access.line.me/oauth2/v2.1/authorize,並帶上 state(防止跨站請求偽造)、nonce(防止重送攻擊)與 PKCE 的 code_challenge。
  2. 使用者在 LINE 登入,並在同意畫面按下允許。
  3. LINE 帶著 code 與 state 導回你的 Callback URL;使用者拒絕時則會帶 error=ACCESS_DENIED。
  4. 伺服器以 POST https://api.line.me/oauth2/v2.1/token 用授權碼換取 Token,並附上 code_verifier。
  5. 伺服器以 POST https://api.line.me/oauth2/v2.1/verify 請 LINE 驗證 ID Token,其中的 sub 就是使用者 ID。
ts
// login.ts:用 Express 5 實作「使用 LINE 登入」(授權碼流程+PKCE)
import express from "express"
import { createHash, randomBytes } from "node:crypto"

const CHANNEL_ID = process.env.LINE_LOGIN_CHANNEL_ID!
const CHANNEL_SECRET = process.env.LINE_LOGIN_CHANNEL_SECRET!
const REDIRECT_URI = "https://example.com/auth/line/callback" // 必須與 Console 設定的 Callback URL 相同

const app = express()
// 示範用的暫存;正式環境請改用 session store 或資料庫
const pending = new Map<string, { nonce: string; codeVerifier: string }>()

function readCookie(header: string | undefined, name: string) {
  return header?.split(/;\s*/).find((c) => c.startsWith(`${name}=`))?.slice(name.length + 1)
}

app.get("/auth/line", (_req, res) => {
  const state = randomBytes(16).toString("hex")
  const nonce = randomBytes(16).toString("hex")
  const codeVerifier = randomBytes(32).toString("base64url") // 43 個字元
  const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url")
  pending.set(state, { nonce, codeVerifier })
  res.cookie("line_state", state, { httpOnly: true, secure: true, sameSite: "lax", maxAge: 10 * 60 * 1000 })

  const params = new URLSearchParams({
    response_type: "code",
    client_id: CHANNEL_ID,
    redirect_uri: REDIRECT_URI,
    state,
    scope: "profile openid",
    nonce,
    code_challenge: codeChallenge,
    code_challenge_method: "S256",
  })
  // URLSearchParams 會把空格編成 "+",LINE 文件範例使用 %20
  res.redirect(`https://access.line.me/oauth2/v2.1/authorize?${params.toString().replaceAll("+", "%20")}`)
})

app.get("/auth/line/callback", async (req, res) => {
  const { code, state, error } = req.query
  const saved = typeof state === "string" ? pending.get(state) : undefined
  if (typeof state === "string") pending.delete(state)
  if (error || typeof code !== "string" || !saved || state !== readCookie(req.headers.cookie, "line_state")) {
    res.status(400).send("Login cancelled or invalid state") // 使用者拒絕授權時會帶 error=ACCESS_DENIED
    return
  }

  // 1. 用授權碼換取 Token
  const tokenRes = await fetch("https://api.line.me/oauth2/v2.1/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "authorization_code",
      code,
      redirect_uri: REDIRECT_URI,
      client_id: CHANNEL_ID,
      client_secret: CHANNEL_SECRET,
      code_verifier: saved.codeVerifier,
    }),
  })
  if (!tokenRes.ok) {
    res.status(502).send("Token exchange failed")
    return
  }
  const { access_token, id_token } = (await tokenRes.json()) as { access_token: string; id_token: string }

  // 2. 交給 LINE 驗證 ID Token(簽章、Channel ID、效期、nonce)
  const verifyRes = await fetch("https://api.line.me/oauth2/v2.1/verify", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({ id_token, client_id: CHANNEL_ID, nonce: saved.nonce }),
  })
  if (!verifyRes.ok) {
    res.status(401).send("Invalid ID token")
    return
  }
  const claims = (await verifyRes.json()) as { sub: string; name?: string; picture?: string }

  // 3. 選用:用 Access Token 取得最新個人檔案
  const profile = await fetch("https://api.line.me/v2/profile", {
    headers: { Authorization: `Bearer ${access_token}` },
  }).then((r) => r.json() as Promise<{ userId: string; displayName: string; pictureUrl?: string }>)

  // claims.sub 是此 Provider 下的使用者 ID,在這裡建立登入 session
  console.log(claims.sub, profile.displayName)
  res.redirect("/")
})

app.listen(3000)

補充說明:

  • state 必須是每次登入都重新產生的英數字隨機值,並與瀏覽器綁定(這個範例使用 HttpOnly Cookie);不一致的 Callback 一律拒絕。
  • email scope 要等 LINE 核准 Channel 的電子郵件存取權限申請後才能使用。
  • 在授權 URL 加上 bot_prompt=normal 或 bot_prompt=aggressive,就能在登入時邀請使用者將 LINE 官方帳號加入好友,Callback 也會多帶 friendship_status_changed。

#LIFF:把 ID Token 交給伺服器驗證

LIFF 應用程式就是一般的網頁應用程式,部署在 HTTPS Endpoint 上,由 LINE 以 LIFF 瀏覽器開啟。從 npm 安裝 @line/liff 時請使用 2.31.1 以上版本(LINE 於 2026 年 9 月 30 日因安全性問題將 v2.20.0 到 v2.31.0 列為不建議使用);若從 CDN 載入 https://static.line-scdn.net/liff/edge/2/sdk.js,則一律會取得最新的 2.x 版。

ts
// LIFF app(瀏覽器端):取得 ID Token,交給伺服器驗證
import liff from "@line/liff"

await liff.init({ liffId: "1234567890-AbcdEfgh" })
if (!liff.isLoggedIn()) {
  liff.login() // 外部瀏覽器才需要;在 LIFF 瀏覽器中使用者已登入
} else {
  const idToken = liff.getIDToken() // 需要 openid scope
  await fetch("/api/me", { method: "POST", headers: { Authorization: `Bearer ${idToken}` } })
}

不要把 liff.getProfile() 的結果當成身分證明傳給伺服器,因為請求內容很容易偽造。請傳送 ID Token(或 Access Token),在伺服器端用與 LINE Login 一節相同的 POST /oauth2/v2.1/verify 驗證,client_id 填入 LIFF 應用程式所在 LINE Login Channel 的 Channel ID。畫面尺寸、liff.sendMessages()、分享對象選擇器、外部瀏覽器與部署方式,請看 LIFF 應用程式開發指南。

#Webhook:簽章驗證與事件處理

聊天室裡發生事件時(收到訊息、新增好友、點擊 Postback 按鈕等),LINE 會向 Messaging API Channel 唯一的 Webhook URL 發送 HTTPS POST。請在 Console 的 Messaging API 分頁設定 URL,按下 Verify,再開啟 Use webhook。URL 必須使用一般瀏覽器信任的憑證機構所簽發的憑證,自簽憑證無法使用。如果由你的伺服器回應使用者,請在 LINE 官方帳號管理後台關閉預設的加入好友歡迎訊息與自動回應訊息,避免使用者收到兩則回覆。

LINE 會為每個請求簽章:x-line-signature 標頭是以 Channel secret 為金鑰、對原始請求內容計算的 HMAC-SHA256,再以 Base64 編碼。請在解析 JSON 之前,用實際收到的位元組驗證。若先解析再重新序列化(例如在 express.json() 之後執行 JSON.stringify(req.body)),位元組就會改變,導致驗證失敗。

ts
// webhook.ts:不用 SDK 接收事件(Express 5)
import express from "express"
import { createHmac, timingSafeEqual } from "node:crypto"
import { reply } from "./line-api.js"

const CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET!
const app = express()

function isValidSignature(rawBody: Buffer, signature: string | undefined) {
  if (!signature) return false
  const expected = createHmac("sha256", CHANNEL_SECRET).update(rawBody).digest()
  const received = Buffer.from(signature, "base64")
  return received.length === expected.length && timingSafeEqual(received, expected)
}

type LineEvent = {
  type: string
  webhookEventId: string
  deliveryContext: { isRedelivery: boolean }
  replyToken?: string
  source?: { type: string; userId?: string; groupId?: string; roomId?: string }
  message?: { id: string; type: string; text?: string }
  postback?: { data: string }
}

// express.raw 保留 LINE 簽章時的原始位元組;這個路由不要用 express.json()
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body
  if (!Buffer.isBuffer(rawBody) || !isValidSignature(rawBody, req.get("x-line-signature"))) {
    res.sendStatus(401)
    return
  }
  const { events } = JSON.parse(rawBody.toString("utf8")) as { events: LineEvent[] }
  res.sendStatus(200) // 盡快回應,events 為 [](連線檢查)時也一樣
  for (const event of events) handleEvent(event).catch((err) => console.error(err))
})

const seen = new Set<string>() // 正式環境請用設有 TTL 的 Redis 或資料庫

async function handleEvent(event: LineEvent) {
  if (seen.has(event.webhookEventId)) return // 重新傳送或重複的事件
  seen.add(event.webhookEventId)

  switch (event.type) {
    case "message":
      if (event.replyToken && event.message?.type === "text") {
        await reply(event.replyToken, [{ type: "text", text: `您說:${event.message.text}` }])
      }
      break
    case "follow":
      if (event.replyToken) await reply(event.replyToken, [{ type: "text", text: "感謝您加入好友!" }])
      break
    case "postback": {
      const data = new URLSearchParams(event.postback?.data) // 例如 "action=book&slot=10:00"
      console.log("postback", data.get("action"), data.get("slot"))
      break
    }
    case "unfollow":
      // 沒有 replyToken:使用者已封鎖,停止傳訊給 event.source?.userId
      break
  }
}

app.listen(Number(process.env.PORT ?? 3000))

這段程式碼依循的 LINE 官方文件重點:

  • 所有合法請求都回傳 200,包括 LINE 為了確認連線而送來的 "events": [] 請求。
  • 以非同步方式處理事件,避免較慢的處理拖累後續的 Webhook。若使用回應送出後就停止執行的 Serverless 平台,請先把事件放進佇列再回應。
  • LINE 不公開發送 Webhook 的 IP 位址,因此簽章就是驗證身分的方法。
  • Webhook 重新傳送功能預設為關閉。在 Console 開啟後,LINE 會重新傳送沒有收到 2xx 回應的 Webhook,同一個事件也可能收到兩次;請略過已處理過的 webhookEventId,並以 deliveryContext.isRedelivery 判斷是否為重新傳送。
事件觸發時機Reply token
message使用者傳送文字、圖片、影片、語音、檔案、位置資訊或貼圖有
follow使用者加入好友或解除封鎖有
unfollow使用者封鎖帳號無
postback使用者點擊 Postback 動作(按鈕、圖文選單、快速回覆)有
unsend使用者收回訊息無
join / leave帳號加入群組或聊天室/離開或被移出join:有,leave:無
memberJoined / memberLeft有人加入/離開帳號所在的群組joined:有,left:無
messageEdited使用者在群組聊天中編輯文字訊息(2026 年 8 月 12 日新增)有
videoPlayComplete、beacon、accountLink、membership影片看完、LINE Beacon、帳號連結、會員方案有

用官方 SDK(line-bot-sdk v3,Python 3.10 以上)撰寫同樣的 Webhook 如下,WebhookHandler.handle() 會驗證簽章,不符時拋出 InvalidSignatureError:

python
# app.py:同樣的 Webhook 改用 Python(line-bot-sdk v3,Python 3.10 以上)
import os
from flask import Flask, request, abort
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
    ApiClient, Configuration, MessagingApi, ReplyMessageRequest, TextMessage,
)
from linebot.v3.webhooks import FollowEvent, MessageEvent, TextMessageContent

app = Flask(__name__)
configuration = Configuration(access_token=os.environ["LINE_CHANNEL_ACCESS_TOKEN"])
handler = WebhookHandler(os.environ["LINE_CHANNEL_SECRET"])


@app.post("/webhook")
def webhook():
    signature = request.headers.get("X-Line-Signature", "")
    body = request.get_data(as_text=True)  # 解析 JSON 之前的原始 body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(401)
    return "OK"


@handler.add(MessageEvent, message=TextMessageContent)
def on_text(event):
    with ApiClient(configuration) as api_client:
        MessagingApi(api_client).reply_message(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=f"您說:{event.message.text}")],
            )
        )


@handler.add(FollowEvent)
def on_follow(event):
    with ApiClient(configuration) as api_client:
        MessagingApi(api_client).reply_message(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text="感謝您加入好友!")],
            )
        )

#安全性、速率限制與錯誤處理

安全檢查清單

  1. 每個 Webhook 都以原始內容驗證簽章,並使用固定時間比對。
  2. Channel secret 與 Token 只放在伺服器端的環境變數或密鑰管理服務中,懷疑外洩的 Token 立即撤銷。
  3. 優先使用 Channel access token v2.1 或短效型 Token;若仍使用長效型 Token,請在 Console 的 Security 分頁限制可呼叫 API 的 IP 位址。
  4. LINE Login 與 LIFF 的 ID Token 一律在伺服器端驗證,不要相信瀏覽器傳來的使用者 ID。
  5. 全程使用 HTTPS,使用者輸入在儲存或顯示前都要先驗證與跳脫。
  6. Endpoint 路徑要與官方文件完全一致:LINE 於 2026 年 8 月 17 日提醒,結尾多一個斜線之類的差異可能導致無法預期的行為。

速率限制(以 Channel 為單位,2026 年 10 月)

Endpoint上限
回覆、推播及大部分 Endpoint每秒 2,000 次請求
Multicast(2025 年 4 月 23 日起)每秒 200 次請求
取得圖文選單清單(2026 年 5 月 26 日起)每秒 10 次請求
顯示載入動畫每秒 100 次請求
Broadcast、Narrowcast、統計資料、測試 Webhook Endpoint每小時 60 次請求
透過 API 建立或刪除圖文選單每小時 100 次請求

錯誤會以 JSON 回傳,包含 message,請求格式不正確時另有 details 陣列:

狀態碼意義處理方式
400請求不正確,或 Invalid reply token(Token 已使用或過期)依 details 修正內容,不要原封不動重試
401Channel access token 無效重新發行 Token
403帳號或方案無法使用此 Endpoint檢查方案與權限
409同一個 X-Line-Retry-Key 已被受理視為成功
413請求超過 2 MB減少單次傳送的內容
429超過速率限制,或已達每月訊息上限("You have reached your monthly limit.")間隔一段時間再重試,並檢查則數與方案
500LINE 端錯誤間隔遞增後重試,推播請沿用同一個 retry key
ts
import { randomUUID } from "node:crypto"
import { LineApiError, push, type LineMessage } from "./line-api.js"

export async function pushWithRetry(to: string, messages: LineMessage[], attempts = 3) {
  const retryKey = randomUUID() // 每次重試都用同一個 key
  for (let attempt = 1; ; attempt++) {
    try {
      return await push(to, messages, retryKey)
    } catch (err) {
      if (err instanceof LineApiError && err.status === 409) return // 先前的請求已被受理
      const retryable = !(err instanceof LineApiError) || err.status === 429 || err.status >= 500
      if (!retryable || attempt >= attempts) throw err
      await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000))
    }
  }
}

也請把回應標頭中的 x-line-request-id 記錄下來(上面的範例存在 LineApiError.requestId),向 LINE 回報問題時會需要提供。

#LineBot.pro 適合用在哪裡

LineBot.pro 不是 SDK,也不是 API 服務供應商,沒有公開 API;這篇教學的所有內容都在你自己的程式碼與 Channel 上執行。不過有兩個地方可以幫你省時間:

  • 訊息與選單設計:AI 產生器可以製作圖文訊息(含主圖、標題、內文與按鈕的 Flex 泡泡)或有 1 到 6 個點擊區的圖文選單圖片。把 Flex JSON 複製到上面的 reply 或 push 即可傳送,或下載選單圖片與 JSON 自行上傳。圖文訊息每次 8 點、圖文選單 10 點,新註冊送 50 點,點數採一次性購買、永不過期(價格)。立即產生 Flex Message
  • 不寫程式的機器人:如果關鍵字回覆就夠用,我們的 LINE 聊天機器人工具會在一對一聊天中,用你核准過的圖文訊息卡片回覆符合關鍵字的訊息;不符合的訊息可選擇開啟 AI 意圖判斷(每檢查一則訊息 1 點,AI 只負責挑選卡片,不會自行撰寫回答)。這個工具使用自己的 Webhook URL,而每個 Channel 只能設定一個 Webhook URL,所以它會取代你在該 Channel 上的 Webhook 伺服器,無法同時並行。

若要把 Messaging API 串接到自家系統,或開發 LIFF 應用程式、LINE MINI App,我們提供 LINE 客製化開發與 LINE 應用程式開發服務,歡迎附上需求與我們聯絡。

#常見問題

Q: LINE API 可以做什麼? 透過 Messaging API,LINE 官方帳號可以回覆使用者,傳送推播、Multicast、Broadcast、Narrowcast 訊息,管理圖文選單,並取得個人檔案與統計資料。LINE Login 為網站和 App 加上「使用 LINE 登入」,LIFF 在 LINE 內執行網頁應用程式,LINE MINI App 則把這些網頁應用程式上架成 LINE 內的服務。

Q: LINE Messaging API 要收費嗎? 呼叫 API 本身不收費。回覆訊息完全不計入則數;推播、Multicast、Broadcast、Narrowcast 則依收件人數計入方案的每月訊息則數。截至 2026 年 10 月,免費方案每月可傳送的則數為台灣 200 則、日本 200 則、泰國 300 則。付費方案請看費用指南。

Q: 為什麼 Webhook 簽章驗證一直失敗? 最常見的原因是驗證前內容已被解析成 JSON(例如經過 express.json()),導致計算雜湊的位元組與 LINE 簽章時不同。其他原因包括:用了其他 Channel 或已重新發行前的 Channel secret、代理伺服器改寫了內容,或演算法錯誤。請用原始內容計算 HMAC-SHA256,再以 Base64 比對。

Q: 現在還能用 LINE Notify 嗎? 不行。LINE Notify 已於 2025 年 3 月 31 日終止服務。請改用 LINE 官方帳號透過 Messaging API 傳送推播訊息,收件的使用者必須是該帳號的好友。

Q: 使用 LINE API 一定要用 SDK 嗎? 不一定。如本文所示,所有 API 都是一般的 HTTPS 與 JSON。官方 SDK(Node.js、Python、PHP、Java、Go、Ruby)提供型別、簽章驗證函式與正確的 Endpoint 路徑;其他程式語言則可使用 LINE 公開的 OpenAPI 規格。

Q: LIFF 和 LINE Login 有什麼不同? LINE Login 讓使用者登入 LINE 之外的自家網站或 App;LIFF 則是在 LINE 內執行你的網頁應用程式,底層使用 LINE Login,因此 LIFF 應用程式要加在 LINE Login Channel(或 LINE MINI App Channel)中。


下一步:

關於 LineBot.pro

LineBot.pro 是專為 LINE 官方帳號打造的 AI 工具,可製作圖文選單、圖文訊息與橫幅,群發行銷活動,並依關鍵字自動回覆。這些指南由我們的團隊根據 LINE Messaging API 的實務經驗撰寫。

聯絡我們
LineBot.pro

準備好讓您的 LINE 官方帳號自動運作了嗎?

立即使用 LineBot.pro,開始自動化您的 LINE 訊息溝通。