cURL · Ghost mannequin API

Ghost mannequin API with cURL

In one sentence

Export PHOTTA_API_KEY, POST to https://api.photta.app/api/v1/api/v1/ghost-mannequin with the documented JSON body, capture the generation ID, then loop a GET on /api/v1/ghost-mannequin/{id} every 3 seconds until data.status is completed. Pair with jq to extract the output URL.

  • Typical time: 1.5–4 min
  • Credits: 4 credits at 2K · 5 credits at 4K
  • Processing: async, polling
  • Limits: 600 requests/min
  • Authentication: Bearer key

Every figure on this page comes from the Photta API docs · checked

POST/api/v1/ghost-mannequin
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/ghost-mannequin \
  -H "Authorization: Bearer $PHOTTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
  "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
  "image_side": "front",
  "angle": "side",
  "style": "3d",
  "background_color": "white",
  "resolution": "2K",
  "aspect_ratio": "3:4",
  "user_instruction": "closed front, cream interior lining",
  "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')

# $ID is data.id from the POST response. Poll every 3 seconds until status is "completed" or "failed".
for i in $(seq 1 120); do
  RESULT=$(curl -s "https://api.photta.app/api/v1/ghost-mannequin/$ID" -H "Authorization: Bearer $PHOTTA_API_KEY")
  STATUS=$(echo "$RESULT" | jq -r '.data.status')
  [ "$STATUS" = "completed" ] && break
  [ "$STATUS" = "failed" ] && echo "generation failed" && exit 1
  sleep 3
done
echo "$RESULT" | jq -r '.data.output_url'

At a glance

At a glance

Endpoint
POST /api/v1/ghost-mannequin
Authentication
Bearer API key
Processing
Asynchronous — poll the status endpoint
Typical time
1.5–4 min
Credits
4 credits at 2K · 5 credits at 4K
Aspect ratios
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Input limits
Up to 10 MB per image; JPEG, PNG, WebP
Idempotent
No

How it works

Five shell steps, zero dependencies beyond curl and jq.

  1. Step 1

    Sign up and generate a key

    Head to ai.photta.app, open the Developers tab, click Generate API key. Live keys start with `photta_live_`.

  2. Step 2

    Export the key

    Put `export PHOTTA_API_KEY="photta_live_xxx"` in your shell profile or a `.envrc` file so every subsequent curl call picks it up automatically.

    curl --version   # macOS, Linux, WSL and Windows 10+ ship it
    export PHOTTA_API_KEY="photta_live_xxxxx"
  3. Step 3

    Pin the base URL

    `export PHOTTA_BASE_URL="https://api.photta.app/api/v1"` keeps the curl commands short.

    export PHOTTA_BASE_URL="https://api.photta.app/api/v1"
    export AUTH_HEADER="Authorization: Bearer $PHOTTA_API_KEY"
  4. Step 4

    Submit and poll

    POST to `$PHOTTA_BASE_URL/api/v1/ghost-mannequin` to get an ID, then loop `curl $PHOTTA_BASE_URL/api/v1/ghost-mannequin/{id}` every 3 seconds. Pipe the response to jq to extract `.data.status` cleanly.

    RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/ghost-mannequin \
      -H "Authorization: Bearer $PHOTTA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
      "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
      "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
      "image_side": "front",
      "angle": "side",
      "style": "3d",
      "background_color": "white",
      "resolution": "2K",
      "aspect_ratio": "3:4",
      "user_instruction": "closed front, cream interior lining",
      "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
    }')
    ID=$(echo "$RESPONSE" | jq -r '.data.id')
    
    # $ID is data.id from the POST response. Poll every 3 seconds until status is "completed" or "failed".
    for i in $(seq 1 120); do
      RESULT=$(curl -s "https://api.photta.app/api/v1/ghost-mannequin/$ID" -H "Authorization: Bearer $PHOTTA_API_KEY")
      STATUS=$(echo "$RESULT" | jq -r '.data.status')
      [ "$STATUS" = "completed" ] && break
      [ "$STATUS" = "failed" ] && echo "generation failed" && exit 1
      sleep 3
    done
    echo "$RESULT" | jq -r '.data.output_url'
  5. Step 5

    Persist the result

    When the job completes, pull `.data.output_url` out with jq and `curl -o` the bytes into local storage — ready for your next pipeline step.

Errors

400invalid_request_error
The request is malformed or contains invalid parameters.
401authentication_error
The API key is missing, invalid, or expired.
402insufficient_credits_error
Your account does not have enough credits.
404not_found_error
The requested resource does not exist.
409conflict_error
The request conflicts with current state — e.g. your concurrent-batch cap is reached.
429rate_limit_error
Too many requests. Slow down and retry.Retry-After: 30
500api_error
An internal server error occurred. Retry later.
case "$STATUS_CODE" in
  402) echo "insufficient_credits_error — top up before retrying" ;;
  429) echo "rate_limit_error — wait the Retry-After seconds (30 by default)" ;;
  4??) echo "$BODY" | jq '.error.type, .error.message' ;;
  5??) echo "api_error — retry with backoff" ;;
esac

Limits

  • 600 requests per minute per API key (Fixed window; the limit is the same on every plan)
  • A 429 carries Retry-After: 30 seconds
  • Up to 10 MB per image
  • Formats: JPEG, PNG, WebP
  • Concurrent batches per plan: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Polling only — the API sends no webhooks.

Why cURL is still the fastest way to verify an API

  • Zero install — cURL ships with every major OS
  • Pairs with jq for scripted pipelines (CI, cron, shell hooks)
  • The fastest way to smoke-test an endpoint before wiring it into code
  • Every command on this page pastes into any POSIX shell

What it does not do

  • The polling loop blocks the terminal — move to Node or Python for parallel workers
  • Polling is the only completion signal; the API sends no webhooks
  • Windows Command Prompt users should run WSL or PowerShell's curl.exe

Questions developers ask

Is there a Photta CLI tool?

Not yet — but cURL plus a fifteen-line bash wrapper gets you most of the way. The docs prioritise cURL-first examples because every language can run a subprocess, and the shell is the common denominator in CI pipelines.

Do I need jq to use the API?

Not strictly — but scripted pipelines are much easier with it. jq lets you pull `.data.id`, `.data.status` and `.data.output_url` out of the JSON response without parsing it by hand. Install with `brew install jq` on macOS or `apt install jq` on Debian/Ubuntu.

How do I authenticate with curl?

Add `-H "Authorization: Bearer $PHOTTA_API_KEY"` to every request. Keep the key in an env var, never inline it in a script you commit. `curl --oauth2-bearer` also works but the explicit header form is clearer in docs and logs.

Can I use curl-based scripts in production?

For one-off batch backfills and cron jobs, yes — thousands of teams ship catalog backfills this way. For a long-lived application, a real language is usually better because you get proper error handling, retries, and observability. The curl examples on this page stay accurate even after you port to Node or Python; the request shape is the same.

How do I poll from a bash loop?

Use `for i in $(seq 1 120); do … ; sleep 3; done` with the status check in the loop body. Break out when `.data.status` is `completed` or `failed`. The code sample on this page shows the exact pattern, including a hard upper bound so a stuck job can't hang the script forever.

How do I read error responses in curl?

Use `curl -w "\n%{http_code}"` to capture the HTTP status code alongside the JSON body. Branch on the status: 402 for insufficient credits, 429 for rate limits (honour the Retry-After value), 5xx for transient server errors. The error body always has an `error.code` and `error.message` field.

Try the API in five minutes

Generate a Photta API key, paste the cURL snippet above, and you'll have your first on-model image before the coffee finishes brewing.

  • 1.5–4 min
  • 4 credits at 2K · 5 credits at 4K
  • async, polling
  • 600 requests/min
  • Bearer key