DraftReviewPublishedArchived

免费大模型 API 怎么调,坑在哪

我把智谱 8 个免费模型逐个真调了一遍:glm-4-flashx 名字带 flash 但报余额不足,glm-4.7-flash 十次里六次被限流,glm-4.5-flash 调通了却返回空——是 max_tokens 被思考过程吃光了。还测出限流是按模型算不按账号,报错信息会骗你。附最小可跑代码和复现脚本。

By Joker2026/08/105 min

我自己的项目里一直挂着免费大模型 API 做兜底。前几天为了另一件事去调智谱的免费模型,随手多试了几个,结果发现好几处跟公开说法对不上——有的模型名字带 flash 但根本不免费,有的调通了却返回空,有的十次里有六次直接被限流。

干脆花了一下午,把它家的免费模型挨个真调了一遍,把额度、限流、返回格式、报错全记下来。

这篇是调用手册,不是评测。下面所有数字都是 2026 年 8 月 10 日实测出来的,脚本我贴在文末,你可以自己跑一遍复现。

先说清楚范围:我手上只有智谱一家的可用 key,所以只有它是真调的。另外两家的状态也顺手记了,在文末。别家我没测,不替它们打包票。

先看哪些是真的免费

一上来就有个坑:智谱的 /models 接口列不出免费模型。

curl https://open.bigmodel.cn/api/paas/v4/models \
  -H "Authorization: Bearer $KEY"

返回的是 glm-4.5、glm-4.6、glm-4.7、glm-5、glm-5-turbo、glm-5.1、glm-5.2 这一串,全是付费主力,一个 flash 都没有。你要是照着这个接口找免费模型,会一无所获。免费模型的 ID 只能从文档里翻。

我把公开资料里提到过的免费模型 ID 全试了一遍,实测结果:

模型 ID能不能调耗时说明
glm-4-flash0.7–1.7s最稳,日常主力
glm-4-flash-2504140.7s同上,带日期的版本号
glm-4-flashx报余额不足,不免费
glm-4.5-flash18–42s旧 ID 仍能调通
glm-4.7-flash7–10s能用,但十次六次被限
glm-z1-flash5.5–6s推理模型
glm-4v-flash1.1s图像理解
glm-4.1v-thinking-flash1.9s图像推理

glm-4-flashx 这个要单独拎出来说。名字长得跟 flash 一模一样,很多汇总文章把它列进免费清单,实际调它返回的是:

{"error":{"code":"1113","message":"余额不足或无可用资源包,请充值。"}}

多一个 x,就要钱。我账户里没余额,所以它一次都没跑通。

最坑的一个:调通了,但返回是空的

这个坑我们自己的项目里踩过,而且踩了之后得出了一个错误结论,一直挂到今天。

现象是这样:调 glm-4.5-flashglm-4.7-flash,HTTP 200,不报错,但 choices[0].message.content 是空字符串。当时的结论是"这个思考模型有问题,别用,换 glm-4-flash"。

这次挨个试才发现,模型没问题,是我们自己给的参数不对

看这组对照,同一个模型、同一个问题,只改 max_tokens

max_tokenscontent 长度reasoning 长度finish_reason补全 tokens
2000403length200
80031369stop215
300044355stop210

看懂了吗——max_tokens 是"思考 + 回答"的总预算,不是只给回答的。

这类思考模型会先在肚子里推理一遍,推理本身就吃掉两三百个 token。你给 200,它光思考就用光了,轮到写答案时预算已经归零,于是 content 返回空,finish_reasonlength(意思是"被长度截断了"),而不是 stop

判据很清楚:content 为空 + finish_reason 是 length,就是 max_tokens 给少了,不是模型坏了。

glm-4.7-flash 的思考更长,我实测它的 reasoning_content 能到 1300–1600 个字符,所以给它至少留 1500,才稳定能拿到答案。

顺带说,这三个思考模型的返回格式还不一样,写代码的得分开处理:

  • glm-4.5-flashglm-4.7-flash:思考过程放在 message.reasoning_content 字段,content 里是干净的答案。
  • glm-z1-flash:思考过程直接混在 content,用 <think> 标签包着。我实测拿到的 content 长这样:
<think>
嗯,用户让我用一句话说明什么是API。首先,我需要确认...
</think>
API 是不同软件之间约定好的接口...

你要是直接把 content 丢给用户看,就会把它的内心戏一起显示出去。得自己按 </think> 切一刀取后半段。

第二个坑:限流是按模型算的,报错信息会骗你

前几天我调生图接口,并发发了 6 个请求,4 个立刻返回 429,错误信息写着:

您的账户已达到速率限制,请您控制请求频率

"您的账户"——看到这句我当时的理解是,整个账号被限速了,那这会儿别的模型也别想调。

这次专门做了个对照实验,同一个账号、同一个时间段,两种模型各并发 6 个请求:

并发 6 个成功耗时
glm-4-flash(文本)6 / 62.4s
cogview-3-flash(生图)1 / 68.3s

文本全过,生图只过一个。同一个账号,同一时刻。

所以那句"您的账户已达到速率限制"是误导——限的是那个模型,不是你的账号。生图模型的并发额度紧得多,文本模型宽松得多。

这个区别很实际:如果你的程序里图文混着调,生图被限的时候,文本任务完全可以继续跑,不用整个流程停下来等。

还有个相关的发现,响应头里没有任何限流字段。我把返回的 header 全翻了一遍,没有 X-RateLimit-Remaining 这类东西。也就是说你无法提前知道自己还剩多少配额,只能撞上 429 才知道。程序里必须自己写重试。

glm-4.7-flash 单独说:能用,但你得接受它十次里有六次不理你

这个模型公开资料给的评价很高——30B、200K 上下文、免费。实际调用体验是另一回事。

我按 2 秒间隔连续调了 10 次,max_tokens 给足 1500:

#1  ✗ 1305 该模型当前访问量过大
#2  ✗ 1305
#3  ✓ 9158ms   content=33  reasoning=1329
#4  ✓ 9734ms   content=44  reasoning=1224
#5  ✗ 1305
#6  ✗ 1305
#7  ✗ 1305
#8  ✓ 7301ms   content=44  reasoning=988
#9  ✗ 1305
#10 ✓ 9695ms   content=69  reasoning=1346

成功 4 次,被限 6 次,成功时平均 8.9 秒。

错误码 1305 跟前面那个 1302 不是一回事:1302 是"你调太快了",1305 是"这个模型现在大家都在抢"。后者跟你的调用频率无关,是模型侧的公共资源不够——所以你降低频率也没用,只能重试。

我的判断是:它不适合放在任何需要即时响应的地方。九秒的响应时间加上四成的成功率,用户在前面等着的场景直接排除。它适合的是离线批处理——夜里跑个任务,失败就重试,反正不赶时间,正好白嫖它的 200K 上下文。

要是你想稳定拿到结果,重试逻辑得这么写(1305 要退避重试,不是直接失败):

async function callWithRetry(model, messages, maxTokens = 1500) {
  for (let i = 0; i < 6; i++) {
    const r = await fetch('https://open.bigmodel.cn/api/paas/v4/chat/completions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.ZHIPU_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ model, messages, max_tokens: maxTokens }),
    })
    const j = await r.json()

    // 1305 模型侧繁忙 / 1302 自己调太快,都退避重试
    if (j.error && (j.error.code == 1305 || j.error.code == 1302)) {
      await new Promise((s) => setTimeout(s, 2000 * (i + 1)))
      continue
    }
    if (j.error) throw new Error(`${j.error.code} ${j.error.message}`)

    const msg = j.choices[0].message
    // 空 content 一般是 max_tokens 不够,翻倍再来一次
    if (!msg.content?.trim() && j.choices[0].finish_reason === 'length') {
      maxTokens *= 2
      continue
    }
    return msg.content
  }
  throw new Error('重试用尽')
}

最小可跑的调用

免费模型和付费的是同一个接口,换个 model 就行。base URL 是 https://open.bigmodel.cn/api/paas/v4

curl 版本,复制就能跑:

curl https://open.bigmodel.cn/api/paas/v4/chat/completions \
  -H "Authorization: Bearer $ZHIPU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-4-flash",
    "messages": [{"role": "user", "content": "你好"}],
    "max_tokens": 800
  }'

它兼容 OpenAI 的接口格式,所以现成的 OpenAI SDK 改个 base_url 就能直接用,不用换库:

from openai import OpenAI

client = OpenAI(
    api_key="你的key",
    base_url="https://open.bigmodel.cn/api/paas/v4",
)

r = client.chat.completions.create(
    model="glm-4-flash",
    messages=[{"role": "user", "content": "你好"}],
    max_tokens=800,
)
print(r.choices[0].message.content)

这一点挺省事——你原来接 OpenAI 写的代码,基本不用动逻辑。

我自己会怎么选

按实测结果,我的分工是这样:

日常高频、要快的活,用 glm-4-flash零点七秒返回,并发 6 个全过,是这一批里唯一能扛住批量的。我自己项目里的兜底就挂它。

要它动脑子的活,用 glm-4.5-flash,别用 4.7。4.5 虽然慢(十几到四十秒),但不像 4.7 那样十次限六次。记得 max_tokens 给足 800 以上。

离线批处理、需要长上下文,才轮到 glm-4.7-flash配好重试,接受它的脾气。

看图用 glm-4v-flash一点一秒,免费的图像理解,这个是真香。

别碰 glm-4-flashx要钱。

另外两家我顺手试的结果

硅基流动:我这个账号余额耗尽之后,调 Kolors 生图返回 {"code":30001,"message":"Sorry, your account balance is insufficient"}。这里有个容易误解的点——很多人以为平台上标着免费的模型不吃余额,实测是账户余额一空,整个 key 就调不动了。

Geminigemini-2.5-flash-image 返回 429,错误信息里明确写着 limit: 0——免费层在这个模型上的配额就是零,不是我用超了,是压根没给。它的模型列表里倒是有一长串图像模型(imagen-4.0、gemini-3-pro-image 等等),但免费能不能调是另一回事。

Kimi、DeepSeek、ModelScope 这些我没有可用 key,没测,不写。

你可以自己复现

上面所有结论都来自下面这段脚本,跑一遍大概两分钟。换成你自己的 key 就行:

const KEY = process.env.ZHIPU_API_KEY
const MODELS = ['glm-4-flash', 'glm-4-flash-250414', 'glm-4-flashx',
  'glm-4.5-flash', 'glm-4.7-flash', 'glm-z1-flash', 'glm-4v-flash']

for (const model of MODELS) {
  const t0 = Date.now()
  const r = await fetch('https://open.bigmodel.cn/api/paas/v4/chat/completions', {
    method: 'POST',
    headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      model,
      messages: [{ role: 'user', content: '用一句话说明什么是 API。' }],
      max_tokens: 1500,
    }),
  })
  const j = await r.json()
  const m = j.choices?.[0]?.message ?? {}
  console.log(model.padEnd(24),
    j.error ? '✗ ' + j.error.code + ' ' + j.error.message
      : `✓ ${Date.now() - t0}ms content=${(m.content || '').length} reasoning=${(m.reasoning_content || '').length}`)
}

平台的免费额度和限流策略改得很勤,我这份数据的保质期大概就是几周。真要上线之前,自己跑一遍最保险——毕竟别人写的清单,包括这一篇,都可能已经过期了。

QUEST COMPLETEREWARD: +30 XP, +1 LEGENDARY ITEM
Build Progress100%
无信号
PULSE
0PULSES