> ## Documentation Index
> Fetch the complete documentation index at: https://docs.photalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Go from zero to your first edited image in five minutes

export const MAX_PROFILE_PHOTOS = 50;

export const MIN_PROFILE_PHOTOS = 10;

export const DEVELOPER_PORTAL_URL = "https://platform.photalabs.com";

This guide walks you through the core Phota API workflow: authenticate, create a profile, wait for training, and run
your first edit, generation, and enhancement.

<Info>
  **Prerequisites**: You need an API key from the <a href={DEVELOPER_PORTAL_URL}>Developer Portal</a>. All requests
  require the `X-API-Key` header.
</Info>

<Steps>
  <Step title="Authenticate">
    Every request must include your API key in the `X-API-Key` header. Verify your key works by listing your profiles:

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.photalabs.com/v1/phota/profiles/ids \
        -H "X-API-Key: YOUR_API_KEY"
      ```

      ```python Python theme={null}
      import requests

      resp = requests.get(
          "https://api.photalabs.com/v1/phota/profiles/ids",
          headers={"X-API-Key": "YOUR_API_KEY"},
      )
      print(resp.json())
      ```
    </CodeGroup>

    You should receive a JSON response with your profiles (empty if you haven't created any yet):

    ```json theme={null}
    {
      "profiles": []
    }
    ```
  </Step>

  <Step title="Create a profile">
    A profile represents a subject whose identity you want to preserve across edits and generations. Provide {MIN_PROFILE_PHOTOS} to {MAX_PROFILE_PHOTOS}
    publicly accessible image URLs of the subject (person, dog, or cat):

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.photalabs.com/v1/phota/profiles/add \
        -H "X-API-Key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "image_urls": [
            "https://example.com/photo1.jpg",
            "https://example.com/photo2.jpg",
            "https://example.com/photo3.jpg"
          ],
          "tag": "user_abc123"
        }'
      ```

      ```python Python theme={null}
      import requests

      resp = requests.post(
          "https://api.photalabs.com/v1/phota/profiles/add",
          headers={
              "X-API-Key": "YOUR_API_KEY",
              "Content-Type": "application/json",
          },
          json={
              "image_urls": [
                  "https://example.com/photo1.jpg",
                  "https://example.com/photo2.jpg",
                  "https://example.com/photo3.jpg",
              ],
              "tag": "user_abc123",
          },
      )
      profile_id = resp.json()["profile_id"]
      print(f"Profile created: {profile_id}")
      ```
    </CodeGroup>

    The response includes the new profile ID:

    ```json theme={null}
    {
      "profile_id": "abc123"
    }
    ```

    <Note>
      Training starts immediately and runs asynchronously. The profile is not ready for use until training completes. See
      the [Profiles guide](/guides/profiles) for tips on choosing good training photos.
    </Note>
  </Step>

  <Step title="Poll for training status">
    Check the profile status until it reaches `READY`:

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.photalabs.com/v1/phota/profiles/abc123/status \
        -H "X-API-Key: YOUR_API_KEY"
      ```

      ```python Python theme={null}
      import time
      import requests

      headers = {"X-API-Key": "YOUR_API_KEY"}

      while True:
          resp = requests.get(
              "https://api.photalabs.com/v1/phota/profiles/abc123/status",
              headers=headers,
          )
          data = resp.json()
          print(f"Status: {data['status']}")

          if data["status"] == "READY":
              break
          elif data["status"] == "ERROR":
              print(f"Training failed: {data['message']}")
              break

          time.sleep(10)
      ```
    </CodeGroup>

    Possible status values:

    | Status        | Meaning                                                 |
    | ------------- | ------------------------------------------------------- |
    | `IN_PROGRESS` | Training is still running. Poll again in a few seconds. |
    | `READY`       | Training completed. The profile is ready for edits.     |
    | `ERROR`       | Training failed. Check the `message` field for details. |
  </Step>

  <Step title="Edit an image">
    Once the profile is `READY`, you can reference it in an edit request. Provide input images as base64 strings or public
    URLs, along with a text prompt. You can refer to a profile inline in the prompt with `[[profile_id]]` and/or list
    candidate profiles in the `profile_ids` field — either mechanism is enough on its own:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.photalabs.com/v1/phota/edit \
        -H "X-API-Key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "prompt": "Make [[abc123]] smile wider",
          "images": ["https://example.com/photo.jpg"],
          "profile_ids": ["abc123"],
          "aspect_ratio": "1:1",
          "resolution": "4K",
          "num_output_images": 2,
          "output_format": "jpg"
        }'
      ```

      ```python Python theme={null}
      import requests

      resp = requests.post(
          "https://api.photalabs.com/v1/phota/edit",
          headers={
              "X-API-Key": "YOUR_API_KEY",
              "Content-Type": "application/json",
          },
          json={
              "prompt": "Make [[abc123]] smile wider",
              "images": ["https://example.com/photo.jpg"],
              "profile_ids": ["abc123"],
              "aspect_ratio": "1:1",
              "resolution": "4K",
              "num_output_images": 2,
              "output_format": "jpg",
          },
      )
      data = resp.json()
      # Default response_mode is "bytes" — images are base64-encoded strings
      print(f"Received {len(data['images'])} image(s)")
      print(f"Known subjects: {data['known_subjects']['counts']}")
      ```
    </CodeGroup>

    <Tip>
      This example shows both mechanisms for illustration. In practice you'd pick one — see the
      [Profiles guide](/guides/profiles#referencing-profiles-in-requests) for when each approach is best.
    </Tip>

    The response contains the output images as base64-encoded strings (default `response_mode: "bytes"`):

    ```json theme={null}
    {
      "images": ["/9j/4AAQ...", "/9j/4AAQ..."],
      "download_urls": [],
      "known_subjects": {
        "counts": {
          "abc123": 2
        }
      }
    }
    ```

    <Tip>
      You can control the output aspect ratio (e.g. `"9:16"`), resolution (up to `"4K"`), and `output_format` (`"png"` or `"jpg"`) on every request.
      Set `response_mode: "urls"` to receive signed CDN download URLs instead of base64 bytes.
    </Tip>

    <Tip>
      Add `"base_model": "gpt-image-2"` (or another supported value) to pick which image model powers the request. Defaults
      to `"nb2"` (Nano Banana 2). See [Base model selection](/guides/base-models).
    </Tip>

    To get download URLs instead of base64 bytes, set `response_mode: "urls"`:

    ```bash curl theme={null}
    curl -X POST https://api.photalabs.com/v1/phota/edit \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "prompt": "Make [[abc123]] smile wider",
        "images": ["https://example.com/photo.jpg"],
        "profile_ids": ["abc123"],
        "resolution": "4K",
        "num_output_images": 2,
        "output_format": "jpg",
        "response_mode": "urls"
      }'
    ```

    ```json theme={null}
    {
      "images": [],
      "download_urls": [
        "https://cache-cdn.photalabs.com/20260408/abc123.jpg?token=...&expires=...",
        "https://cache-cdn.photalabs.com/20260408/def456.jpg?token=...&expires=..."
      ],
      "known_subjects": {
        "counts": {
          "abc123": 2
        }
      }
    }
    ```
  </Step>

  <Step title="Generate an image">
    You can also generate images from scratch -- no input image needed. Reference profiles directly in the prompt using
    `[[profile_id]]` syntax:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.photalabs.com/v1/phota/generate \
        -H "X-API-Key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "prompt": "A professional portrait shot of [[abc123]] on a tropical beach at sunset",
          "num_output_images": 2,
          "output_format": "jpg"
        }'
      ```

      ```python Python theme={null}
      import requests

      resp = requests.post(
          "https://api.photalabs.com/v1/phota/generate",
          headers={
              "X-API-Key": "YOUR_API_KEY",
              "Content-Type": "application/json",
          },
          json={
              "prompt": "A professional portrait shot of [[abc123]] on a tropical beach at sunset",
              "num_output_images": 2,
              "output_format": "jpg",
          },
      )
      data = resp.json()
      print(f"Received {len(data['images'])} image(s)")
      ```
    </CodeGroup>

    The response format is the same as `/edit`:

    ```json theme={null}
    {
      "images": ["/9j/4AAQ...", "/9j/4AAQ..."],
      "download_urls": [],
      "known_subjects": {
        "counts": {
          "abc123": 2
        }
      }
    }
    ```

    <Tip>
      Generation is non-deterministic — request multiple variations with `num_output_images` and let the user pick the best one.
    </Tip>

    <Note>
      The `/generate` endpoint does not accept `images` or `profile_ids`. Reference profiles in the prompt with
      `[[profile_id]]` syntax instead. If you need to transform an existing photo, use `/edit`.
    </Note>

    <Tip>
      `/generate` also accepts `base_model` — same set of values as `/edit`, same default of `"nb2"`. See
      [Base model selection](/guides/base-models).
    </Tip>
  </Step>

  <Step title="Enhance an image">
    Enhancement improves a photo automatically -- no prompt needed:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.photalabs.com/v1/phota/enhance \
        -H "X-API-Key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "image": "https://example.com/photo.jpg",
          "profile_ids": ["abc123"],
          "num_output_images": 2,
          "output_format": "jpg"
        }'
      ```

      ```python Python theme={null}
      import requests

      resp = requests.post(
          "https://api.photalabs.com/v1/phota/enhance",
          headers={
              "X-API-Key": "YOUR_API_KEY",
              "Content-Type": "application/json",
          },
          json={
              "image": "https://example.com/photo.jpg",
              "profile_ids": ["abc123"],
              "num_output_images": 2,
              "output_format": "jpg",
          },
      )
      data = resp.json()
      print(f"Received {len(data['images'])} image(s)")
      print(f"Known subjects: {data['known_subjects']['counts']}")
      ```
    </CodeGroup>

    <Note>
      `/enhance` does not accept `base_model`. Enhancement is pinned to Nano Banana 2 at 2K — see
      [Pricing](/pricing).
    </Note>
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Asynchronous requests" icon="clock" href="/api/async-requests">
    Submit long-running edits as jobs and poll (or get a callback) for the result.
  </Card>

  <Card title="Authentication" icon="key" href="/api/authentication">
    Learn about API key management and best practices.
  </Card>

  <Card title="Use cases" icon="images" href="/guides/use-cases">
    See detailed examples of each capability with visual previews.
  </Card>

  <Card title="Profiles guide" icon="user-group" href="/guides/profiles">
    Deep-dive into profiles, identity preservation, and prompt syntax.
  </Card>

  <Card title="Pricing" icon="dollar-sign" href="/pricing">
    Per-image and per-profile rates.
  </Card>
</CardGroup>
