INTERACTIVE
NVIDIA 免費模型清單怎麼做?從 API 串接到自動刷新機制的完整教學
AI 工具教學2026年4月30日

NVIDIA 免費模型清單怎麼做?從 API 串接到自動刷新機制的完整教學

Lucus

·

網站工程觀察

2026/04/30

如果你想在網站上顯示 NVIDIA 免費模型清單,第一件事不是做頁面,而是先把資料流設計好

很多人一開始會直覺地想:做一個頁面、打 NVIDIA API、把模型列出來就好。但只要這個功能準備上線,你很快就會發現問題沒有那麼簡單。哪些模型真的可用?哪些屬於免費端點?哪些只是 catalog 上看得到、但 API key 當下不一定可用?如果上游一時變慢、資料不完整,前台是不是就跟著壞掉?

所以這篇文章真正要教的,不是怎麼「列出模型」,而是怎麼把 NVIDIA 免費模型清單做成一個穩定的網站功能:有 API 串接、有本地快取、有手動刷新、有背景同步,而且前台永遠不直接依賴上游。

先搞懂來源:為什麼只打一個 API 還不夠?

如果你的目標是做「NVIDIA 免費模型清單」,你至少會碰到兩種資訊來源:

  • 可見模型: 也就是你的 NVIDIA API key 當下看得到、理論上能呼叫的模型

  • 免費端點標記: 也就是平台層面哪些模型屬於 Free Endpoint

真正的網站產品,不應該把這兩件事混成同一件事。因為「API 可見」不代表「一定在你的免費清單邏輯裡」,而「catalog 上看起來免費」也不代表「你這把 key 當下真的能打」。所以比較穩的方式是:先抓可見模型,再補上免費端點資訊,最後把結果存進自己的 catalog。

產品架構先決定:前台只讀本地,刷新交給背景任務

這種功能最穩定的設計原則只有一句話:Public read path 只查本地資料,不在頁面載入時直接打上游。

實務上你可以把它拆成這樣:

  1. 背景同步程式去抓 NVIDIA 上游資料

  2. 把原始資料保存成 snapshot

  3. 整理成網站用的 normalized catalog

  4. 前台 API 只查 catalog

  5. 管理端需要時再手動刷新

這樣做的好處是,網站速度、可用性與資料一致性都會穩很多。使用者永遠拿到的是「最近一次成功同步的結果」,而不是跟著上游 API 一起承受波動。

建議資料設計:至少分成兩層

第一層:raw snapshot

這一層的目的不是讓前台直接讀,而是保留現場,方便審計與除錯。建議至少存:

  • provider

  • sourceUrl

  • fetchedAt

  • httpStatus

  • payloadJson

  • refreshRunId

只要你未來遇到「這次同步為什麼少了 5 個模型」這種問題,這層就會救你。

第二層:normalized catalog

這才是前台要讀的清單。建議至少存:

  • provider

  • modelId

  • displayName

  • description

  • freeTierAvailable

  • agentCapable

  • visible

  • deprecated

  • lastSeenAt

  • lastSyncedAt

  • missingSince

  • rawSnapshotId

其中 freeTierAvailable 是這篇主題最關鍵的欄位。因為你最後要顯示的,正是它。

教學開始:用 TypeScript 做最小可用同步服務

下面的示範用 Node.js + TypeScript,因為這是網站後端最常見的技術棧之一。你不一定要用 Next.js,但這種寫法很容易接進 Next.js、Express 或任何 Node API 專案。

第一步:定義資料型別

type RawSnapshot = {
  provider: 'nvidia'
  sourceUrl: string
  fetchedAt: string
  httpStatus: number
  payloadJson: unknown
  refreshRunId: string
}

type CatalogRow = {
  provider: 'nvidia'
  modelId: string
  displayName: string
  description?: string
  freeTierAvailable: boolean
  visible: boolean
  deprecated: boolean
  lastSeenAt: string
  lastSyncedAt: string
  missingSince: string | null
  rawSnapshotId?: string
}

第二步:先實作上游 API 抓取

如果你的網站要先從 NVIDIA API 取得模型列表,程式應該至少長這樣:

const NVIDIA_API_BASE = 'https://integrate.api.nvidia.com/v1'

async function fetchVisibleModels(apiKey: string) {
  const res = await fetch(NVIDIA_API_BASE + '/models', {
    headers: {
      Authorization: 'Bearer ' + apiKey
    }
  })

  if (!res.ok) {
    throw new Error('Failed to fetch NVIDIA models: ' + res.status)
  }

  return res.json()
}

這一步只解決「目前這把 key 能看到哪些模型」。它還沒告訴你哪些是免費端點,所以接下來你要把 free endpoint 資訊補進來。

第三步:把免費端點資訊交給 provider adapter 處理

這篇不展開爬站與 enrichment 細節,因為重點是「刷新機制設計」,不是「特定頁面的抓取技巧」。實務上你應該把 NVIDIA 的免費端點判斷封裝在 adapter 裡,不要散在業務邏輯裡。

type NormalizedModel = {
  modelId: string
  displayName: string
  description?: string
  freeTierAvailable: boolean
}

interface CatalogProviderAdapter {
  providerName(): string
  fetchRawCatalog(apiKey: string): Promise<unknown>
  normalizeModels(raw: unknown): Promise<NormalizedModel[]>
}

這樣做的好處是,你現在先做 NVIDIA,未來要加別的 provider 時,不必重寫整套同步邏輯。

第四步:保存 raw snapshot,不要只留處理後結果

這一步很多人會偷懶,但我非常建議做。因為只要同步錯一次,你就會慶幸自己有留原始資料。

async function saveRawSnapshot(snapshot: RawSnapshot) {
  await db.rawCatalogSnapshot.create({
    data: snapshot
  })
}

實務上你可以存到資料庫,也可以先存到 JSON 檔,只要能查回來就行。

第五步:normalize 之後做 upsert,不要暴力覆蓋

真正的同步核心在這裡。你不應該每次都 delete all 再 insert all,而應該比對現有資料後做 upsert。

async function upsertCatalogRows(rows: CatalogRow[]) {
  for (const row of rows) {
    await db.llmCatalog.upsert({
      where: {
        provider_modelId: {
          provider: row.provider,
          modelId: row.modelId
        }
      },
      create: row,
      update: {
        displayName: row.displayName,
        description: row.description,
        freeTierAvailable: row.freeTierAvailable,
        visible: row.visible,
        deprecated: row.deprecated,
        lastSeenAt: row.lastSeenAt,
        lastSyncedAt: row.lastSyncedAt,
        missingSince: null,
        rawSnapshotId: row.rawSnapshotId
      }
    })
  }
}

這段 code pan 的重點只有一個:同步是增量更新,不是重灌。

第六步:這次沒看到的模型,先標 missing,不要立刻刪

這是安全同步裡最關鍵的一條原則。因為模型消失不一定代表它真的下架,也可能只是這次同步異常。

async function markMissingModels(provider: string, seenModelIds: string[]) {
  const existing = await db.llmCatalog.findMany({
    where: { provider }
  })

  const now = new Date().toISOString()

  for (const row of existing) {
    if (!seenModelIds.includes(row.modelId)) {
      await db.llmCatalog.update({
        where: { id: row.id },
        data: {
          missingSince: row.missingSince ?? now
        }
      })
    }
  }
}

這樣就算某次上游資料不完整,你的網站也不會立刻少掉一大批模型。

第七步:組成完整 refresh service

現在把前面幾步串起來,就會得到一個真正能跑的同步服務:

export async function refreshNvidiaFreeCatalog() {
  const provider = 'nvidia'
  const apiKey = process.env.NVIDIA_API_KEY
  if (!apiKey) {
    throw new Error('Missing NVIDIA_API_KEY')
  }

  const refreshRunId = crypto.randomUUID()
  const fetchedAt = new Date().toISOString()

  const raw = await nvidiaAdapter.fetchRawCatalog(apiKey)

  await saveRawSnapshot({
    provider,
    sourceUrl: 'nvidia:/v1/models',
    fetchedAt,
    httpStatus: 200,
    payloadJson: raw,
    refreshRunId
  })

  const normalized = await nvidiaAdapter.normalizeModels(raw)

  const rows: CatalogRow[] = normalized.map((model) => ({
    provider: 'nvidia',
    modelId: model.modelId,
    displayName: model.displayName,
    description: model.description,
    freeTierAvailable: model.freeTierAvailable,
    visible: true,
    deprecated: false,
    lastSeenAt: fetchedAt,
    lastSyncedAt: fetchedAt,
    missingSince: null
  }))

  await upsertCatalogRows(rows)
  await markMissingModels(provider, rows.map((r) => r.modelId))

  return {
    provider,
    refreshedAt: fetchedAt,
    count: rows.length
  }
}

第八步:做 public read API

前台只查本地 catalog,這是整篇文章最重要的產品原則之一。

// GET /api/llm-catalog?provider=nvidia&free=true
export async function GET(req: Request) {
  const url = new URL(req.url)
  const provider = url.searchParams.get('provider') ?? 'nvidia'
  const free = url.searchParams.get('free') === 'true'

  const rows = await db.llmCatalog.findMany({
    where: {
      provider,
      ...(free ? { freeTierAvailable: true } : {}),
      visible: true,
      deprecated: false
    },
    orderBy: { displayName: 'asc' }
  })

  return Response.json({ rows })
}

這樣你的網站頁面、搜尋頁、模型篩選器,全都不需要直接連到 NVIDIA 上游。

第九步:做 admin refresh API

如果你想讓內部人員手動刷新資料,可以做一個受保護的 refresh endpoint。

// POST /api/admin/llm-catalog/refresh
export async function POST(req: Request) {
  const auth = req.headers.get('x-admin-key')
  if (auth !== process.env.ADMIN_SYNC_KEY) {
    return new Response('forbidden', { status: 403 })
  }

  const result = await refreshNvidiaFreeCatalog()
  return Response.json({ ok: true, result })
}

這通常很適合搭配內部 admin console 的「Refresh now」按鈕。

第十步:背景排程刷新

正式上線後,這件事不應該只靠人手按。你可以用 cron、queue worker、GitHub Actions、Vercel Cron 或其他 scheduler 每 6 到 24 小時跑一次。

最簡單的做法,是把 refreshNvidiaFreeCatalog() 做成一個可被 cron job 呼叫的腳本。像這樣:

async function main() {
  try {
    const result = await refreshNvidiaFreeCatalog()
    console.log('refresh ok', result)
  } catch (err) {
    console.error('refresh failed', err)
    process.exit(1)
  }
}

main()

這樣你就能在排程器裡安全執行它。

如果今天只想做最小可用版本,應該先做哪四件事?

  1. 先把 /v1/models 串起來

  2. 先存本地 catalog,不要做前台即時查詢

  3. 先做 manual refresh API

  4. 先做 upsert + missing tracking,不要用 destructive overwrite

只要這四件先成立,你的 NVIDIA 免費模型清單就已經比多數「抓到就直接顯示」的做法穩很多。

總結

要做好一個 NVIDIA 免費模型清單功能,重點不是頁面,而是同步架構。真正穩定的做法,是把這件事當成 catalog sync 系統來設計:上游抓取、raw snapshot、normalized catalog、upsert、missing tracking、public read API、admin refresh API、背景排程,缺一不可。

一句話收尾:你不是在寫一個模型列表頁,你是在寫一個可維護的 NVIDIA free model sync service。

參考資料

FAQ

常見問題

為什麼 NVIDIA 免費模型清單不應該做成前台即時查詢?+
因為這會把使用者體驗綁在上游 API 的穩定性與延遲上。更穩的做法是用背景同步寫入本地 catalog,前台永遠只讀本地資料。
設計自動刷新機制時,最重要的同步原則是什麼?+
最重要的是使用 upsert 與 missing tracking,而不是 truncate-and-reload 或 overwrite-only。這樣才能避免某次上游異常時把原本正確的資料整個洗掉。
如果我現在只想做最小可用版本,該先做哪些部分?+
先完成上游 API 串接、本地 catalog 儲存、manual refresh API,以及 upsert 加 missing tracking。這四項先成立,前台就能安全地顯示 NVIDIA 免費模型清單。

Next Step

如果這篇內容剛好對到你現在的問題,下一步就不要只停在閱讀。

你可以直接把目前的流程、卡點或想導入的方向告訴我們;如果你還在評估,也可以先去看〈 英特 Ai 〉或其他正式解決方案,確認哪一條路最適合現在的公司狀況。

Line
1