zcpua/midjourney-api: driving Midjourney through Discord
MidJourney client. Unofficial Node.js client
At a glance
- What is it?
- An unofficial Node.js client that issues Midjourney slash commands inside a Discord channel and reads the bot's replies back, useful when you want image generation in a script rather than a chat window.
- Who is it for?
- Adopt this client when you already pay for Midjourney and want generation driven by code inside an existing pipeline, and accept that it depends on Discord behaviour rather than a supported interface. Do not adopt it for a product that needs a service level agreement or a stable contract.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 58 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What zcpua/midjourney-api actually connects to
The unofficial label here is not a disclaimer bolted onto an HTTP wrapper. There is no Midjourney HTTP endpoint in this project at all. The client reaches Midjourney the way a person does: through Discord. You give it the token of the Discord account that carries the paid Midjourney subscription, the identifier of the server where the Midjourney bot has been invited, and the identifier of the channel in which commands should be typed. The package then posts slash commands into that channel and listens for the bot's responses in the same conversation.
That design defines who the project is for. A person using Midjourney by hand needs nothing here, and a team that wants a stable, documented, first-party endpoint will not find one in this repository. The audience is engineers who already have a subscription and want generation inside a script, a queue worker, or an automation step, and who are willing to depend on Discord as the transport.
The published npm package is named midjourney. The README text still points at the erictik/midjourney-client repository and its example scripts, while the repository in front of you is zcpua/midjourney-api. The package description carried in package.json still reads "Node.js client for the unofficial MidJourney API", so the fork appears to preserve the original interface and naming rather than rebrand it.
How a prompt turns into an image
The flow has three parts. First the client opens a session with client.init(), which authenticates and, in websocket mode, subscribes to Discord gateway events for the configured channel. Second it calls Imagine with your prompt text and a callback that receives a URI and a progress string as the job advances. Third it returns a message object that carries the Discord message id, the flags that Midjourney attaches to a command, and an options array. Each entry in that array holds a label such as V1, V2, U1 or U4 together with a custom identifier.
Those custom identifiers are the interesting part, because they are the handle you need for the next step. To vary or upscale an image you look up the option you want, take its custom value, and pass the original message id and flags back into client.Custom(). Nothing about the second call is a guess. The remix variant differs only in that it also passes content, and the custom zoom variant passes the original prompt with a zoom argument appended, because Midjourney reads the extra instruction from the message body rather than from a parameter.
Remix mode is the case the README is most insistent about. It requires the client to be started with Ws enabled, since remix results arrive as ephemeral Discord messages that a plain polling reader would miss. The same websocket requirement applies to custom zoom. If you turn that flag off, some of the documented features quietly stop working while the basic imagine, upscale and variation paths keep working, which is the kind of failure that is easy to misdiagnose.
Installing the package and generating a first image
The published package is the normal route for application code. The README also documents cloning the tree and running the bundled examples, which is the better path when you want to read the example scripts first.
For application code, install the package into your project:
npm i midjourney
# or
yarn add midjourneyFor the bundled examples, clone the repository and install its dependencies:
git clone https://github.com/erictik/midjourney-client.git
cd midjourney-client
yarn
# or npm
npm installCredentials go into environment variables. The .env.example file in the tree lists the expected names, and the README shows the same values exported in a shell:
export SERVER_ID="1082500871478329374"
export CHANNEL_ID="1094892992281718894"
export SALAI_TOKEN="your-discord-token"The token comes from the Discord account that pays for Midjourney, the server id comes from the server where the Midjourney bot was invited, and the channel id is where commands are sent. The README also lists SESSION_ID, HUGGINGFACE_TOKEN and IMAGE_PROXY in .env.example; the last of those points at a proxy URL for image delivery, and the Hugging Face token is described there as a way to verify human.
With the environment in place, an example script is a single command:
npx tsx example/imagine-ws.tsThe package.json exposes the same run as a script name:
yarn example:imagine
# or npm
npm run example:imagineOne detail worth noticing: that script points at ./examples/imagine-ws.ts while the repository directory is named example, so the script name in package.json does not line up with the folder layout in the tree. The directory holds scripts for describe, blend, fast and relax, faceswap, niji, settings, vary, zoomout, reset, shorten and websocket variants, so reading those files is the fastest way to see which call each Midjourney command needs.
Configuration surface and where it is uneven
All behaviour is set through a single options object passed to the Midjourney constructor. The interface MJConfigParam in the README lists the accepted keys: SalaiToken, ChannelId, ServerId, BotId, Debug, ApiInterval, Limit, MaxWait, Remix, Ws, HuggingFaceToken, SessionId, DiscordBaseUrl, ImageProxy, WsBaseUrl, and injectable fetch and WebSocket implementations.
Several of these exist to solve real deployment problems. A custom fetch matters on Node.js releases below 18, and the interface comment says as much, because the global fetch did not exist there. Supplying your own WebSocket implementation, the documentation says, lets you use isomorphic-ws or a proxy. BotId selects between the Midjourney bot and the Niji bot, which matters if your server hosts both. DiscordBaseUrl and WsBaseUrl exist for pointing the client at a gateway or proxy, and ImageProxy covers hosts that cannot reach the image CDN directly.
The unevenness shows up in the version metadata. The package.json in the tree reports version 4.0.0, yet the published release history lists 4.3.18 as the most recent version on 2025-05-07, with 4.3.17 and 4.3.16 both dated 2024-02-28. The repository was not archived and the last push landed on 2026-08-26, so the tree moves, but the version field in the manifest does not track the release line. Pin an exact npm version rather than trusting the manifest number, and read the CHANGELOG or release notes before assuming a given API exists in your installed copy.
Limits that decide whether it fits your case
The first limit is the account requirement. The token must belong to a Discord account with a paid Midjourney subscription, and the README instructs the reader to extract it from the Discord web client developer console. Automating a consumer account credential is the kind of thing platform terms govern, and this project offers no contract, no rate limit documentation, and no promise that Discord traffic shapes will not change under it. The client also offers a Hugging Face token field for human verification, which signals that some requests are challenged and that the library expects help from a third-party inference service to pass that challenge.
The second limit is coverage. The route map at the end of the README marks commands including /imagine, /variation, /upscale, /reroll, /blend, /zoomout, /vary, /info, /fast, /relax, /prefer remix, remix-mode variation and /describe, and the list is visibly cut off. The documentation does not enumerate the full command surface, so plan on reading the source for anything outside that list.
The third limit is testing. The package.json test script reads "Error: no test specified" and exits with code 1, so the repository ships no test suite that the manifest would run. There is a test directory in the tree, but the published package has no passing test command to point at. This matters more than usual for a library whose correctness depends on parsing another party's message payloads: the failure modes are in Discord's response formats, not in your code.
Finally, the transport itself has limits that the configuration only partly exposes. Limit and MaxWait bound how many messages are fetched and how long the client waits, and ApiInterval spaces out API calls, but the README gives no default values or recommended numbers. If your Discord account is rate limited, the client does not document a retry or backoff policy, so a burst of concurrent jobs is on you.
Alternatives and the real difference in approach
The first alternative is to skip the library and use the Midjourney Discord bot directly. The route map shows the bot itself accepts /imagine, /describe, /info, /blend, /zoomout, /vary, /fast, /relax and /prefer remix as slash commands in a channel. Driving those by hand gives you the same images with none of the token handling, none of the custom-id parsing, and no code to maintain. The difference is that you lose programmatic results: there is no callback, no progress reporting, and no message id to feed into a later upscale or variation call, so each result has to be copied out of Discord into whatever system needs it.
The second alternative is to write your own thin client over the Discord gateway instead of adopting this package. The dependencies already indicate the shape of that work: ws and isomorphic-ws for the socket, async for control flow, and a hand-rolled fetch injection point. Doing it yourself means owning the message parsing, the bot-id handling, the ephemeral-message cases that remix needs, and the webhook-verification path. This library is a working implementation of exactly those details, which is what makes it worth adopting when your own logic has to be reliable.
Where neither works is the case where you need a supported interface with a versioned contract and a service commitment. Neither the direct bot usage nor this client provides that, and no amount of wrapping changes it.
Licence, dependencies and the cost of staying current
Two licence identifiers appear for the same code and they do not agree. The project listing for zcpua/midjourney-api shows Apache-2.0, while the package.json manifest declares ISC in its license field and names a specific author. The LICENSE file sits at the root of the tree, but the contents are not reproduced here, so if you need a definitive answer for compliance, read that file and reconcile it with the manifest before shipping. This is a question for your own legal review, not something this article can settle.
The runtime surface is modest but not tiny: isomorphic-ws and ws for the websocket, async for control flow, semiver for version comparison, snowyflake for identifier generation, tslib, and @huggingface/inference for the human-verification path. Node.js 18 or newer is required by the engines field, and the fetch and WebSocket injection points exist to smooth over older or unusual runtimes. Upgrading means watching Discord, not the library: a change in the gateway event shape or in the bot's message format is what would break your code, and the README documents no compatibility matrix.
The last push on 2026-08-26 shows the tree is still being touched, while the most recent published release, 4.3.18, dates from 2025-05-07. That gap is normal for a project whose breakages come from an outside service, but it means the npm tag and the repository head can carry different behaviour, and it means a pinned version is the safer default than a floating range.
Editorial conclusion
Adopt this client when you already pay for Midjourney and want generation driven by code inside an existing pipeline, and accept that it depends on Discord behaviour rather than a supported interface. Do not adopt it for a product that needs a service level agreement or a stable contract. Before anything else, verify that the Discord account token you intend to use may lawfully be automated, and check that the version you install from npm matches the 4.x release line you tested against, since the package.json in the tree still reports 4.0.0.
Frequently asked questions
What does the midjourney-api package need in order to work?
It needs the Discord token of an account with a paid Midjourney subscription, the id of a server where the Midjourney bot has been invited, and the id of the channel where commands are sent. These are supplied as SalaiToken, ServerId and ChannelId in the client options, or as the SERVER_ID, CHANNEL_ID and SALAI_TOKEN environment variables shown in the README.
Why does the midjourney-api client need Ws enabled for remix mode and custom zoom?
The README states that Ws is required for remix mode and for custom zoom because those results arrive as ephemeral Discord messages. With Ws set to true the client reads them over a websocket, so a client started without it loses these two features while the ordinary imagine, variation and upscale paths continue to work.
How do I upscale or vary an image after the midjourney-api imagine call finishes?
Read the returned message's options array, find the entry whose label matches the button you want, such as U1 or V1, and pass its custom value to client.Custom() together with the message id and the flags from the original result. Remix variants additionally pass the prompt as content, and the custom zoom variant passes the prompt with a zoom argument appended.
Which Node.js version does midjourney-api require?
The package.json engines field requires Node.js 18 or newer. The configuration also accepts an injectable fetch implementation, which the interface comment describes as a need for Node.js below 18, along with a WebSocket option that lets you pass isomorphic-ws or a proxy.
Does the midjourney-api repository have a test suite?
The package.json test script prints an error message and exits with code 1, so no runnable test command is configured. A test directory exists in the repository tree, but the manifest does not point to it, and the README documents no test procedure.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zcpua-midjourney-api)Was this useful?