Fun with MiniMax API

3 min read • September 30, 2024

Table of contents

  1. Introduction
  2. Preparing prompts
  3. Executing script
  4. Examples
  5. Conclusion

Zoom in on a cute red shiny robot holding a white banner with text 'useapi.net' standing in the big data center, in the Pixar animation style.

Introduction

As you probably already figured out, it takes many attempts before you get your perfect 5-second video. This trial and error cycle can be very tedious and time-consuming. But it does not have to be, enter the bright and shiny world of Automation. With a simple bash script provided below and some basic file editing, you can cut out all the waiting time and get straight to picking the winners.

In this article, we will show how to use an experimental MiniMax API for MiniMaxHailuo AI to batch-generate videos.

Preparing prompts

The first step will be to create a file prompts.json as shown below and edit it so it contains desired prompts. You can put as many prompt variations as you can think of. No need to hold back, as you will not be running them manually, not anymore.

It may help to ask ChatGPT, Claude or perhaps Gemini to build prompts for you. This way, you can build a lot of prompts very quickly. Later, you can see what is working and what’s not, and refine them.

Expand prompts.json
[
    "From below shot of a cat catholic priest performing an exorcism on a demonic cat, a parody on The Exorcist movie.",
    "Zoom in on a cute red shiny robot holding a white banner with text 'useapi.net' standing in the big data center, in the Pixar animation style."
]

Executing script

The real magic starts here. We assume that you subscribed to useapi.net. Our subscription is essentially free when you consider the time you will save. It takes only a few minutes to set up MiniMax with our API. We suggest hooking up a dozen or two free Hailuo AI accounts to have enough bandwidth to play with. It usually takes 5-10 minutes of your time max.

🚀 With 15…20 free Hailuo AI accounts configured, you can easily execute approximately 100 generations each hour.

Finally, if you are curious, you can glance over the very detailed documentation we provide. Each endpoint provides the ability to Try It right from the browser. If you don’t feel like reading the docs, that is fine too, the script below is all you need.

We will be using bash, which is built into most *nix systems and Mac OS, and is also available on the Windows WSL subsystem.

Create a file minimax.sh with the code provided below. Execute from the terminal chmod +x minimax.sh to allow execution of this script.

Your prompts.json should be in the same folder as minimax.sh.

Finally, execute the script ./minimax.sh API_TOKEN where API_TOKEN is your useapi.net API token.

All generated videos will be downloaded locally to your drive, so you can view them once they are ready.

Expand minimax.sh
#!/bin/bash
# Usage: ./minimax.sh API_TOKEN 
# NOTE: You may need to install jq, see https://jqlang.github.io/jq/download/

# Check if jq and curl are installed
if ! command -v jq &>/dev/null || ! command -v curl &>/dev/null; then
    echo "Both jq and curl are required but not installed. Exiting."
    exit 1
fi

# Check if API_TOKEN is provided
if [ -z "$1" ]; then
    echo "Usage: $0 <API_TOKEN>"
    exit 1
fi

API_TOKEN="$1"
PROMPTS_FILE="prompts.json"
RESULTS_FILE="results.txt"
ERRORS_FILE="errors.txt"
MAX_AVAILABLE_ACCOUNTS=1 # Define global variable to control parallel execution. You can set it to higher number to execute multiple generations of one prompt.
SLEEP_429=10             # Seconds to wait when 429 received before trying again, see https://useapi.net/docs/api-minimax-v1/post-minimax-videos-create#responses
SLEEP_DOWNLOAD=20        # Seconds to wait when generation is not ready before trying again, see https://useapi.net/docs/api-minimax-v1/get-minimax-videos-videoId

# Fetch API accounts, see https://useapi.net/docs/api-minimax-v1/get-minimax-accounts
accounts_response=$(curl -s https://api.useapi.net/v1/minimax/accounts \
    -H "Accept: application/json" \
    -H "Authorization: Bearer $API_TOKEN")

# Parse the list of accounts
accounts=$(echo "$accounts_response" | jq -r 'keys[]')

# Check if the number of configured accounts is >= MAX_AVAILABLE_ACCOUNTS
num_accounts=$(echo "$accounts" | wc -l)
if [ "$num_accounts" -lt "$MAX_AVAILABLE_ACCOUNTS" ]; then
    echo "⛔ number of configured accounts ($num_accounts) is less than required ($MAX_AVAILABLE_ACCOUNTS)."
    exit 1
fi

echo ""
echo "🚀 Experimental MiniMax API by https:\\\useapi.net"
echo "🔗 Please visit https://useapi.net/docs/api-minimax-v1"
echo ""

echo "Configured MiniMax accounts: $accounts"

# Function to submit a prompt to available accounts
submit_prompt() {
    local prompt=$1
    local prompt_index=$2
    local available_accounts=()

    echo "👉 prompt #$prompt_index: $prompt"

    # Fetch currently running jobs, see https://useapi.net/docs/api-minimax-v1/get-minimax-scheduler
    local running_jobs_response=$(curl -s "https://api.useapi.net/v1/minimax/scheduler/" \
        -H "Accept: application/json" \
        -H "Authorization: Bearer $API_TOKEN")

    # Extract account numbers from running jobs
    local running_accounts=$(echo "$running_jobs_response" | jq -r '.[].videoId' | awk -F '[-:]' '{print $4}')

    echo "Executing (busy) accounts: ${running_accounts[*]}"

    # Determine available accounts
    for account in $accounts; do
        if ! grep -q "$account" <<<"$running_accounts"; then
            available_accounts+=("$account")
        fi
    done

    echo "Available accounts: ${available_accounts[*]}"

    if [ ${#available_accounts[@]} -ge $MAX_AVAILABLE_ACCOUNTS ]; then
        local result_locks=()
        local result_tmp_file=$(mktemp)

        # Submit prompt to the first $MAX_AVAILABLE_ACCOUNTS available accounts
        for i in $(seq 0 $(($MAX_AVAILABLE_ACCOUNTS - 1))); do
            local account="${available_accounts[$i]}"

            echo "#$((i + 1)) account $account …"

            # Construct JSON payload with jq
            local json_payload=$(jq -n --arg prompt "$prompt" '{"prompt": $prompt}')

            (
                # See https://useapi.net/docs/api-minimax-v1/post-minimax-videos-create
                response=$(curl -s -i \
                    -H "Accept: application/json" \
                    -H "Content-Type: application/json" \
                    -H "Authorization: Bearer $API_TOKEN" \
                    -X POST "https://api.useapi.net/v1/minimax/videos/create" \
                    -d "$json_payload")

                http_code=$(echo "$response" | head -n 1 | awk '{print $2}')
                response_body=$(echo "$response" | awk 'BEGIN {RS="\r\n\r\n"} NR==2')

                echo "#$((i + 1)) account $account: HTTP code $http_code"
                echo "Response body: $response_body"

                echo "$http_code" >>"$result_tmp_file"

                if [[ "$http_code" == "200" ]]; then
                    videoId=$(echo "$response_body" | jq -r '.videoId')
                    echo "#$((i + 1)) account $account: videoId: $videoId"
                    if [[ -n "$videoId" && "$videoId" != "null" ]]; then
                        echo "$videoId,$prompt" >>"$RESULTS_FILE"
                    else
                        # This should never happen
                        echo "❓ #$((i + 1)) account $account: No videoId found in response"
                    fi
                else
                    if [[ "$http_code" == "422" ]]; then
                        echo "🛑 #$((i + 1)) account $account: MODERATED prompt $response_body"
                    else
                        if [[ "$http_code" == "429" ]]; then
                            echo "🔄️ #$((i + 1)) account $account: retry on HTTP code $http_code"
                        else
                            echo "❗ #$((i + 1)) account $account: FAILED with $response_body"
                        fi
                    fi
                fi
            ) &
            result_locks+=("$!")
        done

        # Wait for all background jobs to finish
        for pid in "${result_locks[@]}"; do
            wait "$pid"
        done

        local results=()

        # Read result_tmp_file into results array
        mapfile -t results <"$result_tmp_file"

        # Print the results array
        echo "Results array: ${results[@]}"

        # Cleanup temporary file
        rm "$result_tmp_file"

        # Check if we got at least one successfull generation
        local has_200=false
        for result in "${results[@]}"; do
            if [[ "$result" == "200" ]]; then
                has_200=true
                break
            fi
        done

        if [ "$has_200" = true ]; then
            echo "✅ prompt #$prompt_index completed"
        else
            # Check if all results are 429
            local all_429=true
            for result in "${results[@]}"; do
                if [[ "$result" != "429" ]]; then
                    all_429=false
                    break
                fi
            done

            if [ "$all_429" = true ]; then
                echo "🔄️ all responses are 429, retrying prompt #$prompt_index"
                sleep $SLEEP_429 # Optional: Add sleep to avoid immediate retry
                submit_prompt "$prompt" "$prompt_index"
            else
                echo "🛑 MODERATED prompt: \"$prompt\""
                echo "HTTP codes: ${results[@]},$prompt" >>"$ERRORS_FILE"
            fi
        fi
    else
        echo "⌛ not enough available accounts, waiting and retrying…"
        sleep $SLEEP_429
        submit_prompt "$prompt" "$prompt_index"
    fi
}

execute() {
    # Load prompts from JSON file and process each prompt
    local prompt_count=$(jq '. | length' "$PROMPTS_FILE")
    echo "Total number of prompts to process: $prompt_count"
    echo ""
    local ind=0
    while ((ind < prompt_count)); do
        local prompt=$(jq -r --argjson index "$ind" '.[$index]' "$PROMPTS_FILE")
        echo ""
        submit_prompt "$prompt" "$ind"
        ((ind++)) # Increment the index
    done
}

# Extract videoId, retrieve video URL, and download video to local file
# See https://useapi.net/docs/api-minimax-v1/get-minimax-videos-videoId
download() {
    while IFS=',' read -r line; do
        # Extract the full videoId using regex
        # user:<user_id>-minimax:<account_id>-video:<videoId>
        videoId=$(echo "$line" | grep -oP 'user:\d+-minimax:\d+-video:\d+')
        video_filename="${videoId}.mp4"

        echo "👉 $videoId"

        # Check if the video file already exists
        if [[ -f "$video_filename" ]]; then
            echo "⚠️ $video_filename already exists. Skipping download."
            continue
        fi

        while true; do
            task_response=$(curl -s -i \
                -H "Accept: application/json" \
                -H "Authorization: Bearer $API_TOKEN" \
                "https://api.useapi.net/v1/minimax/videos/$videoId")

            # Extract HTTP status code and response body
            http_code=$(echo "$task_response" | head -n 1 | awk '{print $2}')
            task_response_body=$(echo "$task_response" | awk '/^{/ , /^}$/')

            if [[ "$http_code" == "200" ]]; then
                status=$(echo "$task_response_body" | jq -r '.status')
                videoURL=$(echo "$task_response_body" | jq -r '.videoURL')
                percent=$(echo "$task_response_body" | jq -r '.percent')

                if [[ "$status" -eq 2 && -n "$videoURL" ]]; then
                    video_url=$(echo "$task_response_body" | jq -r '.videoURL')
                    echo "✅ downloading $video_url to $video_filename"
                    curl -o "$video_filename" "$video_url"
                    break
                else
                    echo "⌛ $videoId is still in progress ($percent%), waiting…"
                    sleep $SLEEP_DOWNLOAD
                fi
            else
                echo "🛑 MODERATED $videoId. HTTP code: $http_code. Response: $task_response_body"
                break
            fi
        done
    done <$RESULTS_FILE
}

if [ -f "$RESULTS_FILE" ]; then
    while true; do
        read -p "$RESULTS_FILE file detected. Do you want to download the results now? (y/n): " user_input
        if [[ "$user_input" == "y" ]]; then
            download
            rm -f "$RESULTS_FILE"
            break
        elif [[ "$user_input" == "n" ]]; then
            break
        else
            echo "Invalid input. Please answer with 'y' or 'n'."
        fi
    done
fi

execute

download

Examples

From below shot of a cat catholic priest performing an exorcism on a demonic cat, a parody on The Exorcist movie.
Surreal fantasy world: light pink sky, ocean made entirely of glowing sparkling water, focus on Daenerys Targaryen quickly raising from the ocean, as she looks straight at the camera. Her skin is smooth and moist, she has shoulder-long platinum blonde wet hair, her tummy is toned. Eyes deep green color almost glowing. Sparkling liquid surrounding her because surreal ocean is composed of glowing sparkling water.
Camera zooming slowly on a very pretty lady wearing pink bikini, she is jogging on the beach.
Hungry snake hunting the mouse.
From below tracking shot of a tall man from his back walking down a long symmetrical alley with trees on both sides. He is wearing a long bright blue colored trench coat. The trees are magnificent boasting wide branches filled with abundant yellow leaves. It is windy so the man’s coat and leaves on the trees are billowing, moving.

Conclusion

Visit our Discord Server or Telegram Channel for any support questions and concerns.

We regularly post guides and tutorials on the YouTube Channel.

Check our GitHub repo with code examples.

Cross posted