--- title: Quickstart | Luma Agents description: Generate and edit images with the Luma Agents REST API — quickstart, SDKs, and authentication. --- The Luma Agents API provides access to [uni-1](https://lumalabs.ai/uni-1), Luma’s image generation and editing model. The API is asynchronous: submit a generation request, poll for the result, and download your image. ## Quickstart The workflow is three steps: 1. **Submit** a generation request via `POST /v1/generations` 2. **Poll** for status via `GET /v1/generations/{generation_id}` 3. **Download** images from presigned URLs once the state reaches `completed` **Prerequisites:** A Luma API key (set as `LUMA_AGENTS_API_KEY`) and one of the official SDKs installed — see [SDKs](#sdks) below. ### Generate an image - [Python](#tab-panel-54) - [TypeScript](#tab-panel-55) - [Go](#tab-panel-56) - [cURL](#tab-panel-57) ``` import time from luma_agents import Luma client = Luma() # 1. Submit generation = client.generations.create( prompt="A glass of iced coffee on a marble countertop, morning light streaming through a window", aspect_ratio="16:9", ) # 2. Poll while generation.state not in ("completed", "failed"): time.sleep(2) generation = client.generations.get(generation.id) # 3. Download if generation.state == "completed": print(f"Image URL: {generation.output[0].url}") else: print(f"Failed: {generation.failure_reason} (code: {generation.failure_code})") ``` ``` import Luma from "luma-agents"; const client = new Luma(); // 1. Submit let generation = await client.generations.create({ prompt: "A glass of iced coffee on a marble countertop, morning light streaming through a window", aspect_ratio: "16:9", }); // 2. Poll while (generation.state !== "completed" && generation.state !== "failed") { await new Promise((r) => setTimeout(r, 2000)); generation = await client.generations.get(generation.id); } // 3. Download if (generation.state === "completed") { console.log(`Image URL: ${generation.output![0].url}`); } else { console.error(`Failed: ${generation.failure_reason} (code: ${generation.failure_code})`); } ``` ``` package main import ( "context" "fmt" "log" "time" "github.com/lumalabs/luma-agents-go" ) func main() { if err := run(context.Background()); err != nil { log.Fatal(err) } } func run(ctx context.Context) error { client := lumaagents.NewClient() // 1. Submit generation, err := client.Generations.New(ctx, lumaagents.GenerationNewParams{ Prompt: lumaagents.F("A glass of iced coffee on a marble countertop, morning light streaming through a window"), AspectRatio: lumaagents.F(lumaagents.GenerationNewParamsAspectRatio16_9), }) if err != nil { return fmt.Errorf("submit: %w", err) } // 2. Poll for generation.State != lumaagents.GenerationStateCompleted && generation.State != lumaagents.GenerationStateFailed { time.Sleep(2 * time.Second) generation, err = client.Generations.Get(ctx, generation.ID) if err != nil { return fmt.Errorf("poll: %w", err) } } // 3. Download if generation.State == lumaagents.GenerationStateCompleted { fmt.Printf("Image URL: %s\n", generation.Output[0].URL) } else { fmt.Printf("Failed: %s (code: %s)\n", generation.FailureReason, generation.FailureCode) } return nil } ``` Terminal window ``` # 1. Submit RESPONSE=$(curl -s -X POST https://agents.lumalabs.ai/v1/generations \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "A glass of iced coffee on a marble countertop, morning light streaming through a window", "aspect_ratio": "16:9" }') ID=$(echo "$RESPONSE" | jq -r '.id') # 2. Poll while true; do RESULT=$(curl -s -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" \ "https://agents.lumalabs.ai/v1/generations/$ID") STATE=$(echo "$RESULT" | jq -r '.state') [ "$STATE" = "completed" ] || [ "$STATE" = "failed" ] && break sleep 2 done # 3. Download echo "$RESULT" | jq -r '.output[0].url' ``` ### Edit an image To edit an existing image instead, set `type` to `"image_edit"` and provide a `source`. Polling and download are identical to the generate flow above. - [Python](#tab-panel-58) - [TypeScript](#tab-panel-59) - [Go](#tab-panel-60) - [cURL](#tab-panel-61) ``` generation = client.generations.create( type="image_edit", prompt="Change the sky to a dramatic sunset with orange and purple clouds", source={"url": "https://example.com/landscape.jpg"}, ) ``` ``` const generation = await client.generations.create({ type: "image_edit", prompt: "Change the sky to a dramatic sunset with orange and purple clouds", source: { url: "https://example.com/landscape.jpg" }, }); ``` ``` generation, err := client.Generations.New(ctx, lumaagents.GenerationNewParams{ Type: lumaagents.F(lumaagents.GenerationNewParamsTypeImageEdit), Prompt: lumaagents.F("Change the sky to a dramatic sunset with orange and purple clouds"), Source: lumaagents.F(lumaagents.GenerationNewParamsSource{ URL: lumaagents.F("https://example.com/landscape.jpg"), }), }) ``` Terminal window ``` curl -X POST https://agents.lumalabs.ai/v1/generations \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "image_edit", "prompt": "Change the sky to a dramatic sunset with orange and purple clouds", "source": { "url": "https://example.com/landscape.jpg" } }' ``` See [Image editing](/guides/image-editing/index.md) for `image_ref`, style transfer, and base64 source images. ### Production polling Don’t bother with exponential backoff — uni-1 generations typically take 30–60 seconds, so polling every 2 seconds is only \~15–30 GETs per job. What’s worth adding: a hard deadline so a stalled job can’t hang your worker, and a short initial wait so the first few polls aren’t wasted. - [Python](#tab-panel-62) - [TypeScript](#tab-panel-63) - [Go](#tab-panel-64) - [cURL](#tab-panel-65) ``` deadline = time.time() + 120 # 2-minute hard timeout time.sleep(20) # uni-1 p50 is ~30s — no point polling sooner while generation.state not in ("completed", "failed"): if time.time() > deadline: raise TimeoutError(f"Generation {generation.id} did not complete in time") generation = client.generations.get(generation.id) time.sleep(2) ``` ``` const deadline = Date.now() + 120_000; // 2-minute hard timeout await new Promise((r) => setTimeout(r, 20_000)); // uni-1 p50 is ~30s while (generation.state !== "completed" && generation.state !== "failed") { if (Date.now() > deadline) { throw new Error(`Generation ${generation.id} did not complete in time`); } generation = await client.generations.get(generation.id); await new Promise((r) => setTimeout(r, 2_000)); } ``` ``` deadline := time.Now().Add(2 * time.Minute) time.Sleep(20 * time.Second) // uni-1 p50 is ~30s for generation.State != lumaagents.GenerationStateCompleted && generation.State != lumaagents.GenerationStateFailed { if time.Now().After(deadline) { return fmt.Errorf("generation %s did not complete in time", generation.ID) } generation, err = client.Generations.Get(ctx, generation.ID) if err != nil { return fmt.Errorf("poll: %w", err) } time.Sleep(2 * time.Second) } ``` Terminal window ``` DEADLINE=$(($(date +%s) + 120)) # 2-minute hard timeout sleep 20 # uni-1 p50 is ~30s while true; do if [ "$(date +%s)" -gt "$DEADLINE" ]; then echo "Generation $ID did not complete in time" >&2 exit 1 fi RESULT=$(curl -s -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" \ "https://agents.lumalabs.ai/v1/generations/$ID") STATE=$(echo "$RESULT" | jq -r '.state') [ "$STATE" = "completed" ] || [ "$STATE" = "failed" ] && break sleep 2 done ``` ## Authentication Every request requires a Bearer token in the `Authorization` header. Set your API key as the `LUMA_AGENTS_API_KEY` environment variable — the official SDKs read it automatically. Terminal window ``` export LUMA_AGENTS_API_KEY="luma-api-..." ``` ## Base URL ``` https://agents.lumalabs.ai/v1 ``` ## Endpoints | Method | Path | Description | | ------ | --------------------------------- | -------------------------------------- | | `POST` | `/v1/generations` | Submit an image generation or edit job | | `GET` | `/v1/generations/{generation_id}` | Poll for generation status and output | See the [API Reference](/api/index.md) for complete request/response schemas. ## SDKs - [Python](#tab-panel-66) - [TypeScript](#tab-panel-67) - [Go](#tab-panel-68) - [CLI](#tab-panel-69) Terminal window ``` pip install luma-agents ``` Terminal window ``` npm install luma-agents ``` Terminal window ``` go get github.com/lumalabs/luma-agents-go ``` Terminal window ``` go install 'github.com/lumalabs/luma-agents-cli/cmd/luma-agents-cli@latest' ``` ## Response headers Every response includes `X-Request-Id` and `X-API-Version` headers. Successful generation requests also include rate limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`). See [Rate limits and headers](/guides/rate-limits/index.md) for details. ## Next steps - [**Models**](/guides/model/index.md) — Model capabilities, limitations, and output specifications - [**Image generation**](/guides/image-generation/index.md) — Full parameter reference for text-to-image - [**Image editing**](/guides/image-editing/index.md) — Modify existing images with text prompts and reference images - [**Pricing**](/guides/pricing/index.md) — Pay-as-you-go and Provisioned Throughput plans - [**Rate limits and headers**](/guides/rate-limits/index.md) — Rate limiting, response headers, and retry strategies - [**Error handling**](/guides/error-handling/index.md) — Every error code with troubleshooting steps - [**FAQ**](/guides/faq/index.md) — Quick answers to common questions - [**API Reference**](/api/index.md) — Complete endpoint specifications