An unofficial Midjourney API service that interacts with Midjourney via Discord Bot, providing standard RESTful interfaces with support for multi-account concurrency, task queuing, and automatic image upload to object storage.
- Image Generation: Call Midjourney
/imaginecommand with a prompt to generate images - Task Actions: Support post-processing actions such as Upscale and Zoom Out
- Multi-Account Management: Manage Discord accounts via API/database for concurrent processing and load balancing
- Task Queue: Redis-based async task queue with multi-worker concurrent consumption
- Task Callback: Support task status change callback, including progress updates
- Object Storage: Automatically upload generated images to Aliyun OSS or AWS S3
- Swagger Docs: Built-in API documentation, accessible at
/swagger/index.html
| Category | Technology |
|---|---|
| Language | Go 1.23+ |
| Web Framework | Gin |
| Database | PostgreSQL + GORM |
| Cache / Queue | Redis |
| Discord | discordgo (WebSocket) |
| Object Storage | Aliyun OSS / AWS S3 |
| Logging | Zap |
| Configuration | Viper |
| Containerization | Docker / Docker Compose |
midjourney-api/
├── cmd/server/ # Entry point
├── config/ # Configuration files
├── internal/
│ ├── app/ # Application initialization and lifecycle
│ ├── config/ # Configuration structs
│ ├── discord/ # Discord WebSocket listener and message parser
│ ├── handler/ # HTTP request handlers
│ ├── middleware/ # Middleware (logging, recovery, etc.)
│ ├── model/ # Data models
│ ├── oss/ # Object storage uploader
│ ├── redis/ # Redis client
│ ├── repository/ # Database access layer
│ ├── router/ # Route registration
│ ├── service/ # Business logic layer
│ └── worker/ # Async task workers
└── pkg/
├── constants/ # Constant definitions
├── errors/ # Error definitions
├── logger/ # Logger initialization
└── response/ # Unified response format
- Go 1.23+
- PostgreSQL 15+
- Redis 7+
- One or more Discord Bots that have joined a Midjourney server
# 1. Clone the repository
git clone https://github.com/your-username/midjourney-api.git
cd midjourney-api
# 2. Edit the configuration file
cp config/config.yaml.example config/config.yaml
# Fill in global Discord command, database, Redis, and OSS settings.
# Discord account credentials are added later via POST /api/v1/accounts.
# 3. Start the service
docker-compose up -d
# 4. Add Discord accounts through the API after the service is running
# POST /api/v1/accounts with guild_id, channel_id, and user_token.Access the service at http://localhost:8080
# 1. Install dependencies
go mod download && go mod tidy
# 2. Start dependency services (PostgreSQL + Redis)
docker-compose up -d postgres redis
# 3. Edit the configuration file
# Edit config/config.yaml
# Fill in global Discord command, database, Redis, and OSS settings.
# Discord account credentials are added later via POST /api/v1/accounts.
# 4. Generate Swagger docs
make docs
# 5. Build
go build -o bin/server.exe ./cmd/server
# 6. Run
./bin/server
# Optional: ./bin/server -config config/config.yaml
# Or set MJ_CONFIG=config/config.yaml
# Config values can also be overridden with MJ_* environment variables,
# for example MJ_SERVER_PORT, MJ_DATABASE_HOST, MJ_REDIS_HOST, MJ_TASK_WORKER_COUNT.
# 7. Add Discord accounts through the API after the service is running
# POST /api/v1/accounts with guild_id, channel_id, and user_token.Edit config/config.yaml:
config.yaml stores global runtime settings. Add, update, disable, or delete Discord accounts through /api/v1/accounts.
Environment variables use the MJ_ prefix and uppercase config path, for example task.worker_count becomes MJ_TASK_WORKER_COUNT.
server:
port: 8080
mode: debug # debug / release
database:
host: localhost
port: 5432
user: mj_admin
password: 123456
dbname: midjourney
redis:
host: localhost
port: 6379
discord:
application_id: "936929561302675456"
imagine_command_id: "938956540159881230"
imagine_command_version: "1237876415471554623"
describe_command_id: "1092492867185950852"
describe_command_version: "1493662068505706617"
api_base_url: "https://discord.com/api/v9"
task:
timeout: 300 # Worker timeout and stale active task timeout in seconds
max_retries: 3 # Maximum retry attempts
worker_count: 3 # Worker concurrency
oss:
enable: false # Enable OSS upload
provider: aliyun # s3 / aliyunflowchart TB
classDef process fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
classDef decision fill:#fff9c4,stroke:#fbc02d,stroke-width:2px,rx:5,ry:5;
classDef result fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
A[POST /tasks/imagine<br/>Input Prompt] --> B[Generate 2x2 Grid Image<br/>Returns task_id]
B --> C{Choose Action}
C -->|upscale index=1~4<br/>POST /tasks/action| D[Get Single HD Image<br/>Final Result]
C -->|zoom_out_2x index=1~4<br/>POST /tasks/action| E1[Generate Zoom Out 2x<br/>Grid Image<br/>Returns new task_id]
E1 --> F1{Choose Action}
F1 -->|upscale index=1~4<br/>POST /tasks/action| G1[Get Single Zoomed Image<br/>Final Result]
C -->|zoom_out_1_5x index=1~4<br/>POST /tasks/action| E2[Generate Zoom Out 1.5x<br/>Grid Image<br/>Returns new task_id]
E2 --> F2{Choose Action}
F2 -->|upscale index=1~4<br/>POST /tasks/action| G2[Get Single Zoomed Image<br/>Final Result]
C -->|upscale_subtle index=1~4<br/>POST /tasks/action| H[Directly Returns Single<br/>Detail-Enhanced Image]
C -->|upscale_creative index=1~4<br/>POST /tasks/action| I[Directly Returns Single<br/>Creatively-Enhanced Image]
class A,B,E1,E2,H,I process;
class C,F1,F2 decision;
class D,G1,G2 result;
Notes:
upscale: Upscales the selected image from the grid into a single HD image; further actions can be performed on the resultzoom_out_2x: Applies a 2x zoom-out (canvas expansion) on the selected image, generating a new grid; requires another Upscale to get a single imagezoom_out_1_5x: Applies a 1.5x zoom-out (canvas expansion) on the selected image, generating a new grid; requires another Upscale to get a single imageupscale_subtle/upscale_creative: Directly applies detail or creative enhancement to the selected image and returns a single image immediately, no second action required
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/tasks/imagine |
Create an image generation task, supports callback_url |
POST |
/api/v1/tasks/describe |
Create an image describe task, supports callback_url |
POST |
/api/v1/tasks/action |
Perform a task action (Upscale / Zoom Out, etc.), supports callback_url |
GET |
/api/v1/tasks/:task_id |
Get task details |
GET |
/api/v1/tasks |
List all tasks |
GET |
/api/v1/tasks/queue |
Get the waiting queue |
Supported action_type values for /api/v1/tasks/action:
| action_type | Description |
|---|---|
upscale |
Standard upscale — enlarges the selected grid image into a single HD image |
zoom_out_2x |
2x zoom-out (canvas expansion) — generates a new 2x2 grid image |
zoom_out_1_5x |
1.5x zoom-out (canvas expansion) — generates a new 2x2 grid image |
upscale_subtle |
Subtle upscale (detail preservation) — directly returns a single enhanced image |
upscale_creative |
Creative upscale (re-render) — directly returns a single enhanced image |
Midjourney Accounts are managed dynamically via API.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/accounts |
Add an account and start its Discord listener |
GET |
/api/v1/accounts |
List all accounts |
PUT |
/api/v1/accounts/:id |
Update account settings; listener-changing fields are blocked while current_jobs > 0 |
POST |
/api/v1/accounts/:id/restart |
Restart the listener and re-check account health |
DELETE |
/api/v1/accounts/:id |
Delete an idle account and stop its listener |
For how to obtain the user_token, guild_id, and channel_id required when adding an account, please refer to DOC.md.
| Method | Path | Description |
|---|---|---|
GET |
/live |
Liveness check |
Create an image generation task
curl 'http://localhost:8080/api/v1/tasks/imagine' \
-H 'Content-Type: application/json' \
--data-raw $'{\n "prompt": "a cute cat",\n "callback_url": ""\n}'Query task status
curl http://localhost:8080/api/v1/tasks/{task_id}Task status reference:
| Status | Description |
|---|---|
PENDING |
Task created, waiting to be queued |
SUBMITTED |
Submitted to Discord, awaiting response |
IN_QUEUE |
Accepted by Discord, waiting to be processed |
PROCESSING |
Image is being generated (progress field 0~100) |
SUCCESS |
Task completed, image_url / oss_image_url available |
FAILED |
Task failed, error_message contains the reason |
TIMEOUT |
Task timed out |
Perform an Upscale action
curl 'http://localhost:8080/api/v1/tasks/action' \
-H 'Content-Type: application/json' \
--data-raw $'{\n "action_type": "upscale",\n "index": 1,\n "task_id": "xxx"\n}'Once the service is running, visit: http://localhost:8080/swagger/index.html
Apache License 2.0