3.0 KiB
3.0 KiB
ChatGPT DALL-E Image Generation API Pattern (2026-07-30)
Auth: Cookie-only, NO Bearer token
ChatGPT web auth is cookie-based, not Bearer token. The session token from DB works as a cookie:
headers = {
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 ... Chrome/[REDACTED-IP] Safari/537.36",
"Origin": "https://chatgpt.com",
"Referer": "https://chatgpt.com/",
"Accept": "text/event-stream",
}
if csrf_token:
headers["X-CSRF-Token"] = csrf_token.split("|")[0] # Before the pipe
cookies = {
"__Secure-next-auth.session-token": session_token,
}
if csrf_token:
cookies["__Host-next-auth.csrf-token"] = csrf_token
Do NOT use Authorization: Bearer ... — returns 403.
CSRF Token Format
CSRF token from DB: bad91657060abdd7517223227f1a51...%7C2a80b3e7...
- For header
X-CSRF-Token: use the part BEFORE%7C(or|) - For cookie
__Host-next-auth.csrf-token: use the FULL value
Endpoint: SSE Streaming
POST https://chatgpt.com/backend-api/conversation
Body: {
"action": "next",
"messages": [{
"id": "<uuid>",
"author": {"role": "user"},
"content": {"content_type": "text", "parts": ["Generate an image: {prompt}"]}
}],
"model": "auto",
"parent_message_id": "<uuid>",
"conversation_id": None,
"history_and_training_disabled": False,
"timezone_offset_min": 180,
}
The response is SSE (text/event-stream), NOT JSON. Parse as:
for line in resp.text.split('\n'):
if line.startswith('data: '):
data_str = line[6:]
if data_str == '[DONE]':
break
data = json.loads(data_str)
# Image URLs appear as markdown  in message parts
Extracting Image URLs from SSE
3 extraction methods, tried in order:
- Parse SSE lines → message.content.parts → regex for
and raw image URLs - Full-text regex over entire response:
r'!\[.*?\]\((https?://[^\)]+)\)' - Domain search:
r'https?://(?:oaistatic|files\.oaiusercontent)\.com[^\s<>"\']+'
Rate Limits & Token Expiry
- Session tokens from
__Secure-next-auth.session-tokenexpire in ~1 week - Free plan accounts may NOT have DALL-E access (returns text responses, not images)
- 429 rate limit = 1-2 minute cooldown between requests
- Each image generation burns credits on paid plans
Round-Robin Pattern
_account_idx = 0
_lock = asyncio.Lock()
async def _get_next_account() -> tuple[dict, int]:
global _account_idx
async with _lock:
idx = _account_idx % len(ACCOUNTS)
_account_idx += 1
return ACCOUNTS[idx], idx
Full Implementation
See C:/Users/User/Desktop/GPT-AUTOREG/image_api_server.py — FastAPI server on :8400 with:
GET /health— account count + statusGET /accounts— list accounts with index + token statusPOST /generate—{"prompt": "...", "account_index": null}→{"success": bool, "images": [...], "account_used": "...", "error": "..."}