Vercel 429 Too Many Requests 解決方案|API 限流處理完整指南

Vercel 429 Too Many Requests 解決方案|API 限流處理完整指南

API 一直回傳 429 錯誤?

這代表你發送的請求太多、太快了。

Vercel 為了保護系統穩定,會對請求頻率進行限制。

這篇文章教你怎麼處理 429 錯誤,並避免再次發生。


什麼是 429 錯誤?

429 Too Many Requests 代表:你在短時間內發送了太多請求。

這是一種「限流」機制,英文叫 Rate Limiting。

為什麼需要限流?

  1. 保護伺服器:避免被大量請求壓垮
  2. 公平使用:確保每個用戶都能正常使用
  3. 防止濫用:阻止惡意攻擊或爬蟲

什麼情況會觸發 429?

情況 範例
短時間大量請求 1 秒內發送 100 個 API 請求
超過方案限制 超過免費方案的請求額度
循環呼叫 程式碼寫錯導致無限循環
爬蟲攻擊 被惡意爬蟲大量訪問

Vercel 的 Rate Limit 規則

Vercel 有多種限制,了解它們才能避開。

部署相關限制

項目 Hobby Pro
每天部署次數 100 6,000
並行 Build 1 12
Build 佇列 10 100

API 相關限制

項目 限制
Serverless Function 並行執行 依方案不同
Edge Function 請求 每秒數千次(依區域)
API 路由 無硬性限制,但有 fair use

Vercel API(管理用 API)

項目 限制
API 請求 每分鐘 500 次
認證請求 每分鐘 100 次

注意: 這些是 Vercel 管理 API(用於部署、設定等),不是你自己的 API。


解決方案一:優化請求模式

最根本的解法:減少不必要的請求。

合併請求

// ❌ 不好:每個項目單獨請求
for (const id of ids) {
  const item = await fetch(`/api/items/${id}`);
  items.push(item);
}

// ✅ 好:一次請求多個項目
const items = await fetch('/api/items', {
  method: 'POST',
  body: JSON.stringify({ ids }),
});

使用快取

// ✅ 前端快取
const cache = new Map();

async function fetchWithCache(url) {
  if (cache.has(url)) {
    return cache.get(url);
  }
  const data = await fetch(url).then(r => r.json());
  cache.set(url, data);
  return data;
}

減少輪詢頻率

// ❌ 不好:每秒輪詢
setInterval(() => fetch('/api/status'), 1000);

// ✅ 好:合理的輪詢間隔
setInterval(() => fetch('/api/status'), 10000);

// ✅ 更好:使用 WebSocket 或 SSE
const eventSource = new EventSource('/api/status-stream');
eventSource.onmessage = (e) => updateStatus(JSON.parse(e.data));

防抖和節流

// ✅ 搜尋框使用 debounce
import { debounce } from 'lodash';

const debouncedSearch = debounce(async (query) => {
  const results = await fetch(`/api/search?q=${query}`);
  setResults(results);
}, 300);

// 用戶每次輸入都會呼叫,但實際請求只在停止輸入 300ms 後發送

解決方案二:實作 Retry 機制

遇到 429 時,等一下再試。

基本 Retry

async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch(url);

    if (res.ok) {
      return res.json();
    }

    if (res.status === 429) {
      // 等待後重試
      const waitTime = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      console.log(`Rate limited, waiting ${waitTime}ms...`);
      await new Promise(r => setTimeout(r, waitTime));
      continue;
    }

    throw new Error(`HTTP ${res.status}`);
  }

  throw new Error('Max retries exceeded');
}

指數退避(Exponential Backoff)

async function fetchWithExponentialBackoff(url, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const res = await fetch(url);

      if (res.ok) {
        return res.json();
      }

      if (res.status === 429) {
        // 從 Retry-After header 取得建議等待時間
        const retryAfter = res.headers.get('Retry-After');
        const waitTime = retryAfter
          ? parseInt(retryAfter) * 1000
          : Math.min(1000 * Math.pow(2, attempt), 30000); // 最多等 30 秒

        console.log(`Rate limited, waiting ${waitTime}ms...`);
        await new Promise(r => setTimeout(r, waitTime));
        continue;
      }

      throw new Error(`HTTP ${res.status}`);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
    }
  }
}

使用現成的 Library

// 使用 axios-retry
import axios from 'axios';
import axiosRetry from 'axios-retry';

axiosRetry(axios, {
  retries: 3,
  retryDelay: axiosRetry.exponentialDelay,
  retryCondition: (error) => {
    return error.response?.status === 429;
  },
});

解決方案三:自訂 Rate Limiting

在你自己的 API 加入限流,保護後端。

使用 Upstash Rate Limit

// 使用 Upstash 的 Rate Limit 套件
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '10 s'), // 每 10 秒最多 10 次
});

export async function GET(request) {
  const ip = request.headers.get('x-forwarded-for') ?? 'anonymous';
  const { success, limit, remaining, reset } = await ratelimit.limit(ip);

  if (!success) {
    return Response.json(
      { error: 'Too many requests' },
      {
        status: 429,
        headers: {
          'X-RateLimit-Limit': limit.toString(),
          'X-RateLimit-Remaining': remaining.toString(),
          'X-RateLimit-Reset': reset.toString(),
        },
      }
    );
  }

  // 正常處理請求
  return Response.json({ data: 'ok' });
}

使用記憶體限流(簡單版)

// 注意:這個方法在 Serverless 環境效果有限
const requestCounts = new Map();

function rateLimit(ip, maxRequests = 10, windowMs = 60000) {
  const now = Date.now();
  const windowStart = now - windowMs;

  if (!requestCounts.has(ip)) {
    requestCounts.set(ip, []);
  }

  const requests = requestCounts.get(ip).filter(time => time > windowStart);
  requests.push(now);
  requestCounts.set(ip, requests);

  return requests.length <= maxRequests;
}

export async function GET(request) {
  const ip = request.headers.get('x-forwarded-for') ?? 'anonymous';

  if (!rateLimit(ip)) {
    return Response.json({ error: 'Too many requests' }, { status: 429 });
  }

  return Response.json({ data: 'ok' });
}

解決方案四:升級方案

如果合理使用還是超過限制,可能需要升級。

Hobby vs Pro 比較

項目 Hobby Pro
每日部署 100 6,000
Function 執行 100 GB-Hours 1000 GB-Hours
頻寬 100 GB 1 TB
價格 $0 $20/月

什麼時候該升級?

  • 每天部署超過 100 次
  • Function 執行時間經常超額
  • 流量超過免費額度
  • 需要更多並行 Build

不確定該不該升級?可以聯繫我們幫你評估。


防止 429 的最佳實踐

預防勝於治療。

1. 監控請求量

// 在 API 中記錄請求量
export async function GET(request) {
  console.log('[API] Request received:', new Date().toISOString());

  // 你的邏輯
}

2. 使用佇列處理大量任務

// 不要一次發送大量請求
// ❌
await Promise.all(users.map(u => sendEmail(u)));

// ✅ 使用佇列,控制並行數量
import pLimit from 'p-limit';
const limit = pLimit(5); // 最多 5 個並行

await Promise.all(users.map(u => limit(() => sendEmail(u))));

3. 快取 API 回應

// 在 Next.js 中使用快取
export async function GET() {
  return Response.json(data, {
    headers: {
      'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
    },
  });
}

4. 使用 CDN

把靜態資源和可快取的 API 放到 CDN,減少對 Vercel 的請求。


常見問題 FAQ

Q1:429 錯誤會持續多久?

通常幾秒到幾分鐘。

Vercel 會在 Retry-After header 中告訴你應該等多久。

Q2:怎麼知道我離限制還有多遠?

查看 Vercel Dashboard → Usage。

可以看到當前的使用量。

Q3:被限流會影響 SEO 嗎?

可能會。如果 Google 爬蟲遇到 429,可能會減慢爬取速度。

但偶爾的 429 不會造成大問題。

Q4:如何區分是 Vercel 的限制還是我自己的 API 限制?

  • Vercel 的限制:通常發生在部署、Function 執行等
  • 你的 API 限制:需要自己實作

查看錯誤發生的位置來判斷。


還是無法解決?

如果你的 429 問題持續發生:

1. 確認以下資訊

  • 錯誤發生的位置(前端/後端/部署)
  • 請求頻率
  • 使用的方案(Hobby/Pro)

2. 尋求協助

有些限流問題需要架構調整才能解決。


Vercel 部署失敗?

Build Error、環境變數、自訂網域,我們幫你快速排除問題。

解決 Vercel 問題


延伸閱讀

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們