100 lines
3.0 KiB
Markdown
100 lines
3.0 KiB
Markdown
# 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:
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
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:
|
|
|
|
1. **Parse SSE lines** → message.content.parts → regex for `` and raw image URLs
|
|
2. **Full-text regex** over entire response: `r'!\[.*?\]\((https?://[^\)]+)\)'`
|
|
3. **Domain search**: `r'https?://(?:oaistatic|files\.oaiusercontent)\.com[^\s<>"\']+'`
|
|
|
|
## Rate Limits & Token Expiry
|
|
|
|
- Session tokens from `__Secure-next-auth.session-token` expire 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
|
|
|
|
```python
|
|
_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 + status
|
|
- `GET /accounts` — list accounts with index + token status
|
|
- `POST /generate` — `{"prompt": "...", "account_index": null}` → `{"success": bool, "images": [...], "account_used": "...", "error": "..."}`
|