用户指南 API 参考

速率限制

理解 API 速率限制与并发,以及如何在这些限制范围内进行操作

概述

速率限制旨在防止 API 遭到滥用,并确保所有用户都能公平使用。Moknah 针对 HTTP 和 WebSocket 端点的请求频率、额度消耗及并发连接数实施了相应的限制措施。

严格禁止并发策略

Moknah 进行精确处理 每位用户同一时间仅限一项操作.
• HTTP: 请等待当前请求完成后再发送下一个请求。并发请求将收到 409 Conflict(冲突)或 429 Too Many Requests(请求过多)的响应。
• WebSocket: 您同一时间只能保持一个活跃的 WebSocket 连接。如果尝试建立第二个连接,该连接将立即关闭,并返回错误代码 4290。

默认限制

限制类型 价值 适用于 描述
每分钟请求数 (RPM) 60 仅限 HTTP 每分钟标准 API POST 请求的最大数量。
每分钟点数 (CPM) 10,000 HTTP 与 WebSocket 每分钟消耗的最大点数。
活动连接 1 仅限 WebSocket 每个用户同时进行的 WSS 流的最大数量。

WebSocket 限制与关闭代码

由于 WebSocket 维持的是持久连接,因此不使用 HTTP 标头进行速率限制。取而代之的是,限制措施通过断开连接和 JSON 控制消息来实施:

已达上限 行为 / 输出
并发限制 连接立即关闭,代码为 4290。
额度已用尽 接收到 JSON 错误并关闭。
数据块大小限制 如果单个文本片段超过 2,000 个字符,则会收到 JSON 错误。
消息速率限制 如果发送文本片段的速度超过了队列的处理速度,将会收到 JSON 错误。

注意:如果 WebSocket 意外断开连接,请至少等待 15 秒后再重新连接,以确保服务器端的锁已完全清除。

HTTP 速率限制标头

对于标准的 REST 端点,每个 API 响应都包含有助于您追踪状态的标头:

页眉 描述 示例
RateLimit-Limit 您的转速限制 60
RateLimit-Remaining 本分钟剩余请求数 45
RateLimit-Reset 限额重置时的 Unix 时间戳 1699574460
Moknah-Credits-Remaining 本分钟内仍有可用额度 10000
Moknah-Credits-Used 本分钟消耗的额度 5000

当受到 HTTP 速率限制时

如果您超过了 HTTP 速率限制,将会收到一个 429 Too Many Requests 响应,其中包含以下附加标头:

页眉 描述 示例
Retry-After 重试前等待的秒数 45
RateLimit-Reset 可重试的 Unix 时间戳 1699574460

学分计算

额度根据文本长度和处理选项进行计算。对于 WebSocket,额度会在数据流传输过程中按数据块动态扣除。

因素 乘数 描述
基础成本 1x 每个字符 1 点积分
AI 增强型归一化 2x 带变音符号的阿拉伯语高级处理
优质语音 +% 优质语音的额外百分比

示例: 经AI增强规范化处理的500字文本:

500 characters × 2 (AI-Enhanced) = 1,000 credits

最佳实践

1. 针对海量顺序文本使用 WebSocket

若需生成对话式 AI 响应或解析长文档,请勿连续快速发送 50 个 HTTP POST 请求。建议建立单个 WebSocket 连接,并通过该持久连接以流式方式传输数据片段。这样可以完全绕过每分钟 60 次请求(RPM)的 HTTP 限制(但仍受额度限制约束)。

2. 实现请求排队 (HTTP)

由于 Moknah 不支持并发请求,请将 HTTP 请求放入队列并按顺序处理:

import queue
import threading
import time

class TTSQueue:
    def __init__(self, api_key):
        self.api_key = api_key
        self.queue = queue.Queue()
        self.worker = threading.Thread(target=self._process_queue, daemon=True)
        self.worker.start()

    def _process_queue(self):
        while True:
            text, voice_id, callback = self.queue.get()
            try:
                result = self._generate(text, voice_id)
                callback(result, None)
            except Exception as e:
                callback(None, e)
            finally:
                self.queue.task_done()

    def _generate(self, text, voice_id):
        # Your API call here
        pass

    def add(self, text, voice_id, callback):
        self.queue.put((text, voice_id, callback))

# Usage
tts = TTSQueue("your_api_key")
tts.add("Hello world", "voice_123", lambda r, e: print(r or e))
class TTSQueue {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.queue = [];
    this.processing = false;
  }

  async add(text, voiceId) {
    return new Promise((resolve, reject) => {
      this.queue.push({ text, voiceId, resolve, reject });
      this.processNext();
    });
  }

  async processNext() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;
    const { text, voiceId, resolve, reject } = this.queue.shift();

    try {
      const result = await this.generate(text, voiceId);
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.processing = false;
      this.processNext(); // Process next in queue
    }
  }

  async generate(text, voiceId) {
    // Your API call here
  }
}

// Usage
const tts = new TTSQueue("your_api_key");
const audio = await tts.add("Hello world", "voice_123");

2. 实现请求排队 (HTTP)

由于 Moknah 不支持并发请求,请将 HTTP 请求放入队列并按顺序处理:

import time
import random

def request_with_backoff(func, max_retries=5):
    retries = 0

    while retries < max_retries:
        try:
            response = func()

            if response.status_code == 429:
                retry_after = int(response.headers.get("Retry-After", 60))
                # Add jitter to prevent thundering herd
                wait_time = retry_after + random.uniform(0, 1)
                print(f"Rate limited. Waiting {wait_time:.1f}s...")
                time.sleep(wait_time)
                retries += 1
                continue

            return response

        except Exception as e:
            # Exponential backoff for other errors
            wait_time = (2 ** retries) + random.uniform(0, 1)
            print(f"Error: {e}. Retrying in {wait_time:.1f}s...")
            time.sleep(wait_time)
            retries += 1

    raise Exception("Max retries exceeded")
async function requestWithBackoff(func, maxRetries = 5) {
  let retries = 0;

  while (retries < maxRetries) {
    try {
      const response = await func();

      if (response.status === 429) {
        const retryAfter = parseInt(response.headers.get("Retry-After") || 60);
        // Add jitter to prevent thundering herd
        const waitTime = retryAfter + Math.random();
        console.log(`Rate limited. Waiting ${waitTime.toFixed(1)}s...`);
        await new Promise(r => setTimeout(r, waitTime * 1000));
        retries++;
        continue;
      }

      return response;

    } catch (error) {
      // Exponential backoff for other errors
      const waitTime = Math.pow(2, retries) + Math.random();
      console.log(`Error: ${error.message}. Retrying in ${waitTime.toFixed(1)}s...`);
      await new Promise(r => setTimeout(r, waitTime * 1000));
      retries++;
    }
  }

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

3. 实现指数退避

当遇到速率限制 (HTTP 429) 或连接限制 (WebSocket 4290) 时,请使用指数退避策略进行安全重试,以避免向服务器发送过多请求:


摘要

快速参考

HTTP 限制: 每分钟 60 次请求,1 个并发请求。
WebSocket 限制: 1 个并发连接,每个数据块 2,000 个字符。
全局限制: 10,000 CPM(每分钟点数)。
需要更多容量吗? 电子邮件 sales@moknah.io

API 支持

如有关于 API 的问题或疑问,请联系我们: api@moknah.io.