这是一个为Sora提供OpenAI兼容接口的API服务。该服务使用cloudscraper绕过Cloudflare验证,支持多key轮询、并发处理和标准的OpenAI接口格式。
- OpenAI兼容接口:完全兼容OpenAI的
/v1/chat/completions
接口 - CF验证绕过:使用cloudscraper库成功绕过Cloudflare验证
- 多key轮询:支持多个Sora认证token,根据权重和速率限制智能选择
- 并发处理:支持多个并发请求
- 流式响应:支持SSE格式的流式响应
- 图像处理:支持文本到图像生成和图像到图像生成(Remix)
- 异步处理:支持异步生成图像,返回立即响应,防止请求超时
- 状态查询:提供API端点查询异步任务的状态和结果
- 优化性能:经过代码优化,提高请求处理速度和资源利用率
- 健康检查:支持容器健康检查功能
- Python 3.8+
- FastAPI 0.95.0+
- cloudscraper 1.2.71+
- 其他依赖见requirements.txt
你可以直接运行不指定任何环境变量,所有环境变量都可以在面板里面配置
管理员登录密钥默认:sk-123456
api请求密钥不设置默认和管理员登录密钥相同
docker 一键运行
docker run -d -p 8890:8890 --name sora-api 1hei1/sora-api:latest
-
克隆仓库
git clone https://github.com/1hei1/sora-api.git cd sora-api
-
安装依赖
pip install -r requirements.txt
-
配置API Keys(两种方式)
- 方式1: 创建api_keys.json文件
[ {"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60} ]
- 方式2: 设置环境变量
# Linux/macOS export API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' # Windows (PowerShell) $env:API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' # Windows (CMD) set API_KEYS=[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]
- 方式1: 创建api_keys.json文件
-
配置代理(可选,如果需要)
# Linux/macOS - 基本代理 export PROXY_HOST=127.0.0.1 export PROXY_PORT=7890 # Linux/macOS - 带认证的代理 export PROXY_HOST=127.0.0.1 export PROXY_PORT=7890 export PROXY_USER=username export PROXY_PASS=password # Windows (PowerShell) - 基本代理 $env:PROXY_HOST="127.0.0.1" $env:PROXY_PORT="7890" # Windows (PowerShell) - 带认证的代理 $env:PROXY_HOST="127.0.0.1" $env:PROXY_PORT="7890" $env:PROXY_USER="username" $env:PROXY_PASS="password" # Windows (CMD) - 基本代理 set PROXY_HOST=127.0.0.1 set PROXY_PORT=7890 # Windows (CMD) - 带认证的代理 set PROXY_HOST=127.0.0.1 set PROXY_PORT=7890 set PROXY_USER=username set PROXY_PASS=password
-
启动服务
python run.py
-
访问服务
- API服务地址: http://localhost:8890
- 后台管理面板: http://localhost:8890/admin
-
构建Docker镜像
docker build -t sora-api .
-
运行Docker容器(不同配置选项)
基本运行方式:
docker run -d -p 8890:8890 --name sora-api sora-api
使用预打包镜像:
docker run -d -p 8890:8890 --name sora-api 1hei1/sora-api:latest
使用预打包镜像并配置API密钥:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' \ --name sora-api \ 1hei1/sora-api:v0.1
使用预打包镜像并配置代理:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \ -e PROXY_HOST=host.docker.internal \ -e PROXY_PORT=7890 \ --name sora-api \ 1hei1/sora-api:v0.1
带API密钥配置:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' \ --name sora-api \ sora-api
带基本代理配置:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \ -e PROXY_HOST=host.docker.internal \ -e PROXY_PORT=7890 \ --name sora-api \ sora-api
带认证代理配置:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \ -e PROXY_HOST=host.docker.internal \ -e PROXY_PORT=7890 \ -e PROXY_USER=username \ -e PROXY_PASS=password \ --name sora-api \ sora-api
使用外部配置文件:
# 首先确保api_keys.json文件已正确配置 docker run -d -p 8890:8890 \ -v $(pwd)/api_keys.json:/app/api_keys.json \ -e PROXY_HOST=host.docker.internal \ -e PROXY_PORT=7890 \ --name sora-api \ sora-api
挂载本地目录保存图片:
docker run -d -p 8890:8890 \ -v /your/local/path:/app/src/static/images \ --name sora-api \ sora-api
启用详细日志:
docker run -d -p 8890:8890 \ -e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \ -e VERBOSE_LOGGING=true \ --name sora-api \ sora-api
注意: 在Docker中使用宿主机代理时,请使用
host.docker.internal
而不是127.0.0.1
作为代理主机地址。 -
检查容器状态
docker ps docker logs sora-api
-
停止和移除容器
docker stop sora-api docker rm sora-api
环境变量 | 描述 | 默认值 | 示例 |
---|---|---|---|
API_HOST |
API服务监听地址 | 0.0.0.0 |
127.0.0.1 |
API_PORT |
API服务端口 | 8890 |
9000 |
BASE_URL |
API基础URL(生成图片的时候需要用到) | http://0.0.0.0:8890 |
https://api.example.com |
PROXY_HOST |
HTTP代理主机 | 空(不使用代理) | 127.0.0.1 |
PROXY_PORT |
HTTP代理端口 | 空(不使用代理) | 7890 |
PROXY_USER |
HTTP代理用户名 | 空(不使用认证) | username |
PROXY_PASS |
HTTP代理密码 | 空(不使用认证) | password |
IMAGE_SAVE_DIR |
图片保存目录 | src/static/images |
/data/images |
IMAGE_LOCALIZATION |
是否启用图片本地化 | False |
True |
ADMIN_KEY |
管理员API密钥(登录界面输入的密码) | sk-123456 |
sk-youradminkey |
API_AUTH_TOKEN |
API认证令牌(使用api服务传入的key) | 空 | your-auth-token |
VERBOSE_LOGGING |
是否启用详细日志 | False |
True |
API密钥配置采用JSON格式,每个密钥包含以下属性:
key
: Sora认证令牌(必须包含Bearer前缀)weight
: 轮询权重,数字越大被选中概率越高max_rpm
: 每分钟最大请求数(速率限制)
示例:
[
{
"key": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MzQ0ZTY1LWJiYzktNDRkMS1hOWQwLWY5NTdiMDc5YmQwZSIsInR5cCI6IkpXVCJ9...",
"weight": 1,
"max_rpm": 60
},
{
"key": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MzQ0ZTY1LWJiYzktNDRkMS1hOWQwLWY5NTdiMDc5YmQwZSIsInR5cCI6IkpXVCJ9...",
"weight": 2,
"max_rpm": 60
}
]
# 文本到图像请求(非流式)
curl -X POST http://localhost:8890/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "sora-1.0",
"messages": [
{"role": "user", "content": "生成一只在草地上奔跑的金毛犬"}
],
"n": 1,
"stream": false
}'
# 文本到图像请求(流式)
curl -X POST http://localhost:8890/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "sora-1.0",
"messages": [
{"role": "user", "content": "生成一只在草地上奔跑的金毛犬"}
],
"n": 1,
"stream": true
}'
# 查询异步任务状态
curl -X GET http://localhost:8890/v1/generation/chatcmpl-123456789abcdef \
-H "Authorization: Bearer your-api-key"
-
连接超时或无法连接
- 检查代理配置是否正确
- 如使用代理认证,确认用户名密码正确
- 确认Sora服务器是否可用
- 检查本地网络连接
-
API密钥加载失败
- 确认api_keys.json格式正确
- 检查环境变量API_KEYS是否正确设置
- 查看日志中的错误信息
-
图片生成失败
- 确认Sora令牌有效性
- 查看日志中的错误信息
- 检查是否超出账户额度限制
-
Docker容器启动失败
- 检查端口是否被占用
- 确认环境变量设置正确
- 查看Docker日志中的错误信息
-
环境变量API_AUTH_TOKEN和ADMIN_KEY的区别
- 环境变量 API_AUTH_TOKEN代表你在cheery studio 或者newapi里调用填写的令牌
- 环境变量 ADMIN_KEY 代表你管理面板的登录密码
- 当不设置API_AUTH_TOKE时 其值默认等于ADMIN_KEY的值
-
环境变量BASE_URL的作用
- 这个是图片本地化时设置的,比如生成了一张图片,获取到图片原始url,由于有的客户端不能访问sora,所以要本地化,这个base_url就是指定本地化图片的url前缀。
-
token无效
- 首次使用的token需要设置用户名,可以使用下面的脚本批量设置用户名:
import random
import string
import logging
import cloudscraper
# Configure logging
tlogging = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# --- Configuration ---
PROXY = {
"http": "http://127.0.0.1:7890",
"https": "http://127.0.0.1:7890"
}
PROFILE_API = "https://sora.chatgpt.com/backend/me"
TOKENS_FILE = "tokens.txt" # 每行一个 Bearer token
RESULTS_FILE = "update_results.txt" # 保存更新结果
USERNAME_LENGTH = 8 # 随机用户名长度
# --- Utilities ---
def random_username(length: int = USERNAME_LENGTH) -> str:
"""生成全小写随机用户名"""
return ''.join(random.choices(string.ascii_lowercase, k=length))
def sanitize_headers(headers: dict) -> dict:
"""
移除所有非 Latin-1 字符,确保 headers 可以被底层 HTTP 库正确编码。
"""
new = {}
for k, v in headers.items():
if isinstance(v, str):
new[k] = v.encode('latin-1', 'ignore').decode('latin-1')
else:
new[k] = v
return new
class SoraBatchUpdater:
def __init__(self, proxy: dict = None):
self.proxy = proxy or {}
def update_username_for_token(self, token: str) -> tuple[bool, str]:
"""
针对单个 Bearer token,生成随机用户名并发送更新请求。
返回 (success, message)。
"""
scraper = cloudscraper.create_scraper()
if self.proxy:
scraper.proxies = self.proxy
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
"sec-ch-ua": '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
}
headers = sanitize_headers(headers)
new_username = random_username()
payload = {"username": new_username}
try:
resp = scraper.post(
PROFILE_API,
headers=headers,
json=payload,
allow_redirects=False,
timeout=15
)
status = resp.status_code
if resp.ok:
msg = f"OK ({new_username})"
logging.info("Token %s: updated to %s", token[:6], new_username)
return True, msg
else:
text = resp.text.replace('\n', '')
msg = f"Failed {status}: {text}" # 简要错误信息
logging.warning("Token %s: %s", token[:6], msg)
return False, msg
except Exception as e:
msg = str(e)
logging.error("Token %s exception: %s", token[:6], msg)
return False, msg
def batch_update(self, tokens: list[str]) -> None:
"""
对一组 Bearer token 批量更新用户名,并将结果写入 RESULTS_FILE。
"""
results = []
for token in tokens:
success, message = self.update_username_for_token(token)
results.append((token, success, message))
# 写入结果文件
with open(RESULTS_FILE, 'w', encoding='utf-8') as f:
for token, success, msg in results:
status = 'SUCCESS' if success else 'ERROR'
f.write(f"{token} ---- {status} ---- {msg}\n")
logging.info("Batch update complete. Results saved to %s", RESULTS_FILE)
def load_tokens(filepath: str) -> list[str]:
"""从文件加载每行一个 token 的列表"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return [line.strip() for line in f if line.strip()]
except FileNotFoundError:
logging.error("Tokens file not found: %s", filepath)
return []
if __name__ == '__main__':
tokens = load_tokens(TOKENS_FILE)
if not tokens:
logging.error("No tokens to update. Exiting.")
else:
updater = SoraBatchUpdater(proxy=PROXY)
updater.batch_update(tokens)
最新版本包含以下性能优化:
- 代码重构:简化了代码结构,提高可读性和可维护性
- 内存优化:减少不必要的内存使用,优化大型图像处理
- 异步处理:全面使用异步处理提高并发性能
- 错误处理:改进了错误处理和日志记录
- 密钥管理:优化了密钥轮询算法,提高了可靠性
- 容器优化:增强了Docker容器配置,支持健康检查
欢迎提交问题报告和改进建议!
MIT