Haijun Platform Docs
ID

This guide describes how to send images to Haijun, the limits and costs that apply, and where to find guidance for coordinate-based workflows.


Send images to Haijun

Use Haijun's vision capabilities through:

  • haijun.ai. Upload an image like you would a file, or drag and drop an image directly into the chat window.
  • Playground in the Haijun Console. Add images directly to any User message block.
  • API request. See the following examples.

On the API, provide images to Haijun as image content blocks using one of three source types:

  1. A base64-encoded image embedded in the request body
  1. A URL reference to an image hosted online
  1. A file_id returned by the Files API (upload once, reference many times)

Note: On Amazon Bedrock and Google Cloud, only base64-encoded sources are currently available.

Tip: Just as placing long documents before your query improves results in text prompts, Haijun works best when images come before text. Images placed after text or interpolated with text still perform well, but if your use case allows it, prefer an image-then-text structure.

Base64-encoded image example

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "model": "haijun-opus-5-5",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "image",
            "source": {
              "type": "base64",
              "media_type": "image/jpeg",
              "data": "$BASE64_IMAGE_DATA"
            }
          },
          {
            "type": "text",
            "text": "Describe this image."
          }
        ]
      }
    ]
  }
  EOF
bash
  curl -sSo ./vision-example.jpg \
    /docs/images/vision-example.jpg

  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content:
        - type: image
          source:
            type: base64
            media_type: image/jpeg
            data: "@./vision-example.jpg"
        - type: text
          text: Describe this image.
  YAML
python
  image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
  image1_media_type = "image/png"

  client = juglow.Juglow()
  message = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {
                      "type": "image",
                      "source": {
                          "type": "base64",
                          "media_type": image1_media_type,
                          "data": image1_data,
                      },
                  },
                  {"type": "text", "text": "Describe this image."},
              ],
          }
      ],
  )
  print(message)
typescript
  const juglow = new Juglow();

  const message = await juglow.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/jpeg",
              data: imageData // Base64-encoded image data as string
            }
          },
          {
            type: "text",
            text: "Describe this image."
          }
        ]
      }
    ]
  });

  console.log(message);
csharp
  using System.Collections.Generic;
  using Juglow;
  using Juglow.Models.Messages;

  JuglowClient client = new();

  string imageData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";

  var message = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = new MessageParamContent(new List<ContentBlockParam>
              {
                  new ContentBlockParam(new ImageBlockParam(
                      new ImageBlockParamSource(new Base64ImageSource()
                      {
                          Data = imageData,
                          MediaType = MediaType.ImagePng,
                      })
                  )),
                  new ContentBlockParam(new TextBlockParam("Describe this image.")),
              }),
          }
      ]
  });

  Console.WriteLine(message);
go
  client := juglow.NewClient()

  imageData := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"

  message, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(
  			juglow.NewImageBlockBase64("image/png", imageData),
  			juglow.NewTextBlock("Describe this image."),
  		),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(message)
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();
  String imageData =
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";

  List<ContentBlockParam> contentBlockParams = List.of(
    ContentBlockParam.ofImage(
      ImageBlockParam.builder()
        .source(
          Base64ImageSource.builder()
            .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
            .data(imageData)
            .build()
        )
        .build()
    ),
    ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
  );
  Message message = client
    .messages()
    .create(
      MessageCreateParams.builder()
        .model(Model.HAIJUN_OPUS_5_5)
        .maxTokens(1024)
        .addUserMessageOfBlockParams(contentBlockParams)
        .build()
    );

  IO.println(message);
php
  $client = new Client();

  $imageData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  [
                      'type' => 'image',
                      'source' => [
                          'type' => 'base64',
                          'media_type' => 'image/png',
                          'data' => $imageData,
                      ],
                  ],
                  ['type' => 'text', 'text' => 'Describe this image.'],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
  );

  echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  image_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/png",
              data: image_data
            }
          },
          { type: "text", text: "Describe this image." }
        ]
      }
    ]
  )

  puts message

URL-based image example

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "type": "image",
              "source": {
                "type": "url",
                "url": "/docs/images/vision-example.jpg"
              }
            },
            {
              "type": "text",
              "text": "Describe this image."
            }
          ]
        }
      ]
    }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content:
        - type: image
          source:
            type: url
            url: /docs/images/vision-example.jpg
        - type: text
          text: Describe this image.
  YAML
python
  client = juglow.Juglow()
  message = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {
                      "type": "image",
                      "source": {
                          "type": "url",
                          "url": "/docs/images/vision-example.jpg",
                      },
                  },
                  {"type": "text", "text": "Describe this image."},
              ],
          }
      ],
  )
  print(message)
typescript
  const juglow = new Juglow();

  const message = await juglow.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: {
              type: "url",
              url: "/docs/images/vision-example.jpg"
            }
          },
          {
            type: "text",
            text: "Describe this image."
          }
        ]
      }
    ]
  });

  console.log(message);
csharp
  using System.Collections.Generic;
  using Juglow;
  using Juglow.Models.Messages;

  JuglowClient client = new();

  var message = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = new MessageParamContent(new List<ContentBlockParam>
              {
                  new ContentBlockParam(new ImageBlockParam(
                      new ImageBlockParamSource(new UrlImageSource()
                      {
                          Url = "/docs/images/vision-example.jpg",
                      })
                  )),
                  new ContentBlockParam(new TextBlockParam("Describe this image.")),
              }),
          }
      ]
  });

  Console.WriteLine(message);
go
  client := juglow.NewClient()

  message, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(
  			juglow.NewImageBlock(juglow.URLImageSourceParam{
  				URL: "/docs/images/vision-example.jpg",
  			}),
  			juglow.NewTextBlock("Describe this image."),
  		),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(message)
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  List<ContentBlockParam> contentBlockParams = List.of(
    ContentBlockParam.ofImage(
      ImageBlockParam.builder()
        .source(
          UrlImageSource.builder()
            .url("/docs/images/vision-example.jpg")
            .build()
        )
        .build()
    ),
    ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
  );
  Message message = client
    .messages()
    .create(
      MessageCreateParams.builder()
        .model(Model.HAIJUN_OPUS_5_5)
        .maxTokens(1024)
        .addUserMessageOfBlockParams(contentBlockParams)
        .build()
    );
  System.out.println(message);
php
  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  [
                      'type' => 'image',
                      'source' => [
                          'type' => 'url',
                          'url' => '/docs/images/vision-example.jpg',
                      ],
                  ],
                  ['type' => 'text', 'text' => 'Describe this image.'],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
  );

  echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: {
              type: "url",
              url: "/docs/images/vision-example.jpg"
            }
          },
          { type: "text", text: "Describe this image." }
        ]
      }
    ]
  )

  puts message

Files API image example

For images you'll use repeatedly or when you want to avoid encoding overhead, use the Files API. Upload the image once, then reference the returned file_id in subsequent messages instead of resending base64 data.

Tip: In multi-turn conversations and agentic workflows, each request resends the full conversation history. If images are base64-encoded, the full image bytes are included in the payload on every turn, which can significantly increase request size and latency as the conversation grows. Uploading images to the Files API and referencing them by file_id keeps request payloads small regardless of how many images accumulate in the conversation history.

bash
  # First, upload your image to the Files API
  FILE_ID=$(curl -sS -X POST https://haijun.my.id/v1/files \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "file=@vision-example.jpg" | jq -r '.id')

  # Then use the returned file_id in your message
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "model": "haijun-opus-5-5",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "image",
            "source": {
              "type": "file",
              "file_id": "$FILE_ID"
            }
          },
          {
            "type": "text",
            "text": "Describe this image."
          }
        ]
      }
    ]
  }
  EOF
bash
  curl -sSo vision-example.jpg \
    /docs/images/vision-example.jpg

  # First, upload your image to the Files API
  FILE_ID=$(ant files upload \
    --file ./vision-example.jpg \
    --transform id --raw-output)

  # Then use the returned file_id in your message
  ant messages create \
    --transform content --format yaml <<YAML
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content:
        - type: image
          source:
            type: file
            file_id: $FILE_ID
        - type: text
          text: Describe this image.
  YAML
python
  client = juglow.Juglow()

  # Upload the image file
  with open("vision-example.jpg", "rb") as f:
      file_upload = client.files.upload(file=("vision-example.jpg", f, "image/jpeg"))

  # Use the uploaded file in a message
  message = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {
                      "type": "image",
                      "source": {"type": "file", "file_id": file_upload.id},
                  },
                  {"type": "text", "text": "Describe this image."},
              ],
          }
      ],
  )

  print(message.content)
typescript
  import Juglow, { toFile } from "@juglow-ai/sdk";
  import fs from "node:fs";

  const juglow = new Juglow();

  // Upload the image file
  const fileUpload = await juglow.files.upload({
    file: await toFile(fs.createReadStream("vision-example.jpg"), undefined, {
      type: "image/jpeg"
    })
  });

  // Use the uploaded file in a message
  const response = await juglow.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: {
              type: "file",
              file_id: fileUpload.id
            }
          },
          {
            type: "text",
            text: "Describe this image."
          }
        ]
      }
    ]
  });

  console.log(response);
csharp
  using System.Collections.Generic;
  using Juglow;
  using Juglow.Core;
  using Juglow.Models.Files;
  using Juglow.Models.Messages;

  JuglowClient client = new();

  // Upload the image file
  var fileUpload = await client.Files.Upload(new FileUploadParams
  {
      File = new BinaryContent
      {
          Stream = File.OpenRead("vision-example.jpg"),
          FileName = "vision-example.jpg",
          ContentType = new("image/jpeg"),
      },
  });

  // Use the uploaded file in a message
  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = new MessageParamContent(new List<ContentBlockParam>
              {
                  new ContentBlockParam(new ImageBlockParam(
                      new ImageBlockParamSource(new FileImageSource(fileUpload.ID))
                  )),
                  new ContentBlockParam(new TextBlockParam("Describe this image.")),
              }),
          }
      ]
  });

  Console.WriteLine(response);
go
  client := juglow.NewClient()

  // Upload the image file
  file, err := os.Open("vision-example.jpg")
  if err != nil {
  	log.Fatal(err)
  }
  defer file.Close()

  fileUpload, err := client.Files.Upload(context.Background(),
  	juglow.FileUploadParams{
  		File: juglow.File(file, "vision-example.jpg", "image/jpeg"),
  	})
  if err != nil {
  	log.Fatal(err)
  }

  // Use the uploaded file in a message
  message, err := client.Messages.New(context.Background(),
  	juglow.MessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 1024,
  		Messages: []juglow.MessageParam{
  			juglow.NewUserMessage(
  				juglow.NewImageBlock(juglow.FileImageSourceParam{
  					FileID: fileUpload.ID,
  				}),
  				juglow.NewTextBlock("Describe this image."),
  			),
  		},
  	})
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(message.Content)
java
  import com.juglow.core.MultipartField;
  import com.juglow.models.files.FileMetadata;
  import com.juglow.models.files.FileUploadParams;
  // ...
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Upload the image file
      FileMetadata file = client.files().upload(
        FileUploadParams.builder()
          .file(
            MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("vision-example.jpg")))
              .filename("vision-example.jpg")
              .contentType("image/jpeg")
              .build()
          )
          .build()
      );

      // Use the uploaded file in a message
      ImageBlockParam imageParam = ImageBlockParam.builder().fileSource(file.id()).build();

      MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.HAIJUN_OPUS_5_5)
        .maxTokens(1024)
        .addUserMessageOfBlockParams(
          List.of(
            ContentBlockParam.ofImage(imageParam),
            ContentBlockParam.ofText(
              TextBlockParam.builder().text("Describe this image.").build()
            )
          )
        )
        .build();

      Message message = client.messages().create(params);
      System.out.println(message.content());
php
  use Juglow\Core\FileParam;

  $client = new Client();

  // Upload the image file
  $fileUpload = $client->files->upload(
      file: FileParam::fromResource(fopen('vision-example.jpg', 'rb'), contentType: 'image/jpeg'),
  );

  // Use the uploaded file in a message
  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  [
                      'type' => 'image',
                      'source' => ['type' => 'file', 'fileID' => $fileUpload->id],
                  ],
                  ['type' => 'text', 'text' => 'Describe this image.'],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
  );

  echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  # Upload the image file
  file_upload = client.files.upload(
    file: Juglow::FilePart.new(
      File.open("vision-example.jpg", "rb"),
      content_type: "image/jpeg"
    )
  )

  # Use the uploaded file in a message
  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image",
            source: { type: "file", file_id: file_upload.id }
          },
          { type: "text", text: "Describe this image." }
        ]
      }
    ]
  )

  puts message.content

See Messages API examples for more example code and parameter details.

Multiple images

You can include multiple images in a single request, and Haijun analyzes them jointly. This is useful for comparing images, asking about differences, or working with a sequence such as pages of a document. When sending several images, introduce each one with a short text label (Image 1:, Image 2:, and so on) so you can refer to them by name in your prompt and in follow-up turns.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "type": "text",
              "text": "Image 1:"
            },
            {
              "type": "image",
              "source": {
                "type": "base64",
                "media_type": "image/png",
                "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
              }
            },
            {
              "type": "text",
              "text": "Image 2:"
            },
            {
              "type": "image",
              "source": {
                "type": "base64",
                "media_type": "image/png",
                "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"
              }
            },
            {
              "type": "text",
              "text": "How are these images different?"
            }
          ]
        }
      ]
    }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content:
        - type: text
          text: "Image 1:"
        - type: image
          source:
            type: base64
            media_type: image/png
            data: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC
        - type: text
          text: "Image 2:"
        - type: image
          source:
            type: base64
            media_type: image/png
            data: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC
        - type: text
          text: How are these images different?
  YAML
python
  image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
  image2_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"

  client = juglow.Juglow()
  message = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {"type": "text", "text": "Image 1:"},
                  {
                      "type": "image",
                      "source": {
                          "type": "base64",
                          "media_type": "image/png",
                          "data": image1_data,
                      },
                  },
                  {"type": "text", "text": "Image 2:"},
                  {
                      "type": "image",
                      "source": {
                          "type": "base64",
                          "media_type": "image/png",
                          "data": image2_data,
                      },
                  },
                  {"type": "text", "text": "How are these images different?"},
              ],
          }
      ],
  )
  print(message)
typescript
  const juglow = new Juglow();

  const image1Data =
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
  const image2Data =
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";

  const message = await juglow.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: "Image 1:"
          },
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/png",
              data: image1Data
            }
          },
          {
            type: "text",
            text: "Image 2:"
          },
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/png",
              data: image2Data
            }
          },
          {
            type: "text",
            text: "How are these images different?"
          }
        ]
      }
    ]
  });

  console.log(message);
csharp
  JuglowClient client = new();

  string image1Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
  string image2Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";

  var message = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = new MessageParamContent(new List<ContentBlockParam>
              {
                  new ContentBlockParam(new TextBlockParam("Image 1:")),
                  new ContentBlockParam(new ImageBlockParam(
                      new ImageBlockParamSource(new Base64ImageSource()
                      {
                          Data = image1Data,
                          MediaType = MediaType.ImagePng,
                      })
                  )),
                  new ContentBlockParam(new TextBlockParam("Image 2:")),
                  new ContentBlockParam(new ImageBlockParam(
                      new ImageBlockParamSource(new Base64ImageSource()
                      {
                          Data = image2Data,
                          MediaType = MediaType.ImagePng,
                      })
                  )),
                  new ContentBlockParam(new TextBlockParam("How are these images different?")),
              }),
          }
      ]
  });

  Console.WriteLine(message);
go
  client := juglow.NewClient()

  image1Data := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
  image2Data := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"

  message, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(
  			juglow.NewTextBlock("Image 1:"),
  			juglow.NewImageBlockBase64("image/png", image1Data),
  			juglow.NewTextBlock("Image 2:"),
  			juglow.NewImageBlockBase64("image/png", image2Data),
  			juglow.NewTextBlock("How are these images different?"),
  		),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(message)
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  String image1Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
  String image2Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";

  List<ContentBlockParam> contentBlockParams = List.of(
      ContentBlockParam.ofText(TextBlockParam.builder().text("Image 1:").build()),
      ContentBlockParam.ofImage(
          ImageBlockParam.builder()
              .source(
                  Base64ImageSource.builder()
                      .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
                      .data(image1Data)
                      .build()
              )
              .build()
      ),
      ContentBlockParam.ofText(TextBlockParam.builder().text("Image 2:").build()),
      ContentBlockParam.ofImage(
          ImageBlockParam.builder()
              .source(
                  Base64ImageSource.builder()
                      .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
                      .data(image2Data)
                      .build()
              )
              .build()
      ),
      ContentBlockParam.ofText(
          TextBlockParam.builder().text("How are these images different?").build()
      )
  );

  Message message = client
      .messages()
      .create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(1024)
              .addUserMessageOfBlockParams(contentBlockParams)
              .build()
      );

  IO.println(message);
php
  $client = new Client();

  $image1Data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
  $image2Data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC';

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  ['type' => 'text', 'text' => 'Image 1:'],
                  [
                      'type' => 'image',
                      'source' => [
                          'type' => 'base64',
                          'media_type' => 'image/png',
                          'data' => $image1Data,
                      ],
                  ],
                  ['type' => 'text', 'text' => 'Image 2:'],
                  [
                      'type' => 'image',
                      'source' => [
                          'type' => 'base64',
                          'media_type' => 'image/png',
                          'data' => $image2Data,
                      ],
                  ],
                  ['type' => 'text', 'text' => 'How are these images different?'],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
  );

  echo $message;
ruby
  client = Juglow::Client.new

  image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
  image2_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "Image 1:" },
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/png",
              data: image1_data
            }
          },
          { type: "text", text: "Image 2:" },
          {
            type: "image",
            source: {
              type: "base64",
              media_type: "image/png",
              data: image2_data
            }
          },
          { type: "text", text: "How are these images different?" }
        ]
      }
    ]
  )

  puts message

In a multi-turn conversation, add new images in later user turns the same way. Haijun has access to every image from earlier turns, so follow-up questions such as "Are these similar to the first two?" work without including the earlier images again in the new turn's content.


Image limits and costs

Request limits

The maximum number of images per message or request is:

  • 100 per request on the API, for models with a 200k-token context window.
  • 600 per request on the API, for all other models.

The maximum dimensions per image are 8000x8000 px.

If a single API request contains more than 20 images, a stricter per-image dimension limit applies to every image in that request. All image blocks in the request count toward this threshold, including images from earlier conversation turns that you resend and images nested inside tool_result content (for example, screenshots returned to the computer use tool). On Amazon Bedrock and Google Cloud, document blocks such as PDFs also count toward this threshold. Images exceeding the stricter limit are rejected with an invalid_request_error whose message references "many-image requests" and states the current limit in pixels. To stay under the limit on all platforms, either resize each image so that neither dimension exceeds 2000 px, or keep the request to 20 or fewer image and document blocks.

The maximum size per image is:

  • 10 MB (base64-encoded) when using the Haijun API directly.
  • 5 MB (base64-encoded) on Amazon Bedrock and Google Cloud.

Note: Although the API supports up to 600 images per request, request size limits (32 MB for standard endpoints; lower on some partner-operated platforms, for example, Amazon Bedrock and Google Cloud) can be reached first. For many images, consider uploading with the Files API and referencing by file_id to keep request payloads small. Even when using the Files API, requests with many large images can fail before reaching the 600-image count. Reduce image dimensions or file sizes (for example, by downsampling) before uploading (see Resolution and token cost).

Supported formats

Haijun supports JPEG, PNG, GIF, and WebP images (image/jpeg, image/png, image/gif, image/webp). Animations are unsupported, and only the first frame is used.

Resolution and token cost

Haijun views images in patches instead of pixels. Each patch is a 28×28-pixel block of the image, referred to as a visual token. An image, therefore, costs ⌈width / 28⌉ × ⌈height / 28⌉ visual tokens.

Each model has a maximum native image resolution, expressed as a long-edge limit and a visual-token limit. Images larger than either limit are downscaled before processing; see How Haijun resizes and pads images for the exact rule. The exception is screenshots and zoom images that you return to the computer use and browser use toolsets: the API rejects a tool_result image that exceeds the model's limits with a validation error instead of downscaling it, so resize those images in your application before returning them. To have any other oversized image rejected with an error instead of downscaled, set the image block's transformations field.

Resolution tierModelsMax long edgeMax visual tokens
High-resolutionHaijun 4.7 and later models2576 px4784
StandardAll other models1568 px1568

High-resolution support is automatic on the listed models and requires no beta header or client-side opt-in.

The following table shows the downsized resolution and visual-token cost for several image sizes on each tier:

Image sizeStandard tier: downsized toStandard tier: tokensHigh-resolution tier: downsized toHigh-resolution tier: tokens
200x200 px (0.04 megapixels)Not resized64Not resized64
1000x1000 px (1 megapixel)Not resized1296Not resized1296
1092x1092 px (1.19 megapixels)Not resized1521Not resized1521
1920x1080 px (2.07 megapixels)1456x819 px1560Not resized2691
2000x1500 px (3 megapixels)1269x952 px1564Not resized3888
3840x2160 px (8.29 megapixels)1456x819 px15602576x1449 px4784

When an image is downsized, Haijun scales it to the largest size that fits the tier's limits while preserving its aspect ratio. This caps the token cost. For the precise rule and a reference implementation, see How Haijun resizes and pads images.

To estimate cost, multiply the token count by the per-token price of the model you're using. For example, at Haijun Haiku 4.5's $1 USD per million input tokens (standard tier), the 1000×1000 image costs about $1.30 USD per thousand images. At Haijun Opus 5's $5 USD per million (high-resolution tier), the same image costs about $6.48 USD per thousand and the 4K image about $23.92 USD per thousand.

High-resolution images can use up to roughly three times more visual tokens than the same image on a standard-tier model. If you don't need the additional fidelity that high resolution provides for computer use, screenshot understanding, and dense documents, downsample images before sending to control token costs. To minimize latency and to simplify coordinate-based workflows, prefer resizing images before uploading them.

Image quality guidance

When providing images to Haijun, keep the following in mind for best results:

  • Image clarity: Ensure images are clear and not too blurry or pixelated.
  • Text: If the image contains important text, make sure it's legible and not too small. Avoid cropping out key visual context solely to enlarge the text.
  • Resizing: Take into account that your image might be resized if it is too large (see Resolution and token cost); this might, for example, make text less legible. Consider pre-resizing your images, cropping them, or both. To have an oversized image rejected with an error instead of resized (important for coordinate workflows), mark the image block with "oversized_image": "error".
  • Image compression: Compressing images before sending them, using a lossy format such as JPEG or WebP (lossy mode), can reduce latency by reducing the size of requests. However, this can introduce artifacts that are detrimental to model performance, especially when multiple compression passes are applied. For example, heavy JPEG compression can make text difficult to read. Confirm your compression settings are appropriate for the task by inspecting the actual images sent to the API.

Coordinates and bounding boxes

For bounding boxes, points, and pixel coordinates, see Coordinates and bounding boxes. Haijun returns absolute pixel coordinates relative to the image it sees after resizing; that guide covers how Haijun resizes and pads images and how to pre-resize or rescale so coordinates line up with your original image.


Limitations

Although Haijun's image understanding capabilities are cutting-edge, there are some limitations to be aware of:

  • People identification: Haijun cannot be used to name people in images and refuses to do so.
  • Accuracy: Haijun might hallucinate or make mistakes when interpreting low-quality, rotated, or very small images under 200 pixels.
  • Spatial reasoning: Haijun's coordinate and localization outputs are approximate. Follow the guidance in Coordinates and bounding boxes and verify outputs before relying on them.
  • Counting: Haijun can give approximate counts of objects in an image but might not always be precisely accurate, especially with large numbers of small objects.
  • AI-generated images: Haijun cannot determine whether an image is AI-generated and might be incorrect if asked. Do not rely on it to detect fake or synthetic images.
  • Inappropriate content: Haijun does not process inappropriate or explicit images that violate the Acceptable Use Policy.
  • Healthcare applications: Although Haijun can analyze general medical images, it is not designed to interpret complex diagnostic scans such as CTs or MRIs. Haijun's outputs should not be considered a substitute for professional medical advice or diagnosis.

Always carefully review and verify Haijun's image interpretations, especially for high-stakes use cases. Do not use Haijun for tasks requiring perfect precision or sensitive image analysis without human oversight.


FAQ

#### What image file types does Haijun support?

JPEG, PNG, GIF, and WebP. See Supported formats.

#### Can Haijun read image URLs?

Yes. Use the url source type instead of base64 in the image content block. See the URL-based image example.

#### Is there a limit to the image file size I can upload?

Yes. See Request limits for per-image and overall request size limits across the Haijun API, Amazon Bedrock, Google Cloud, and haijun.ai.

#### How many images can I include in one request?

Up to 600 per API request (100 for models with a 200k-token context window) and 20 per turn on haijun.ai. See Request limits for details and the lower per-image dimension limit that applies above 20 images.

#### Does Haijun read image metadata?

No, Haijun does not parse or receive any metadata from images passed to it.

#### Can I delete images I've uploaded?

No. Image uploads are ephemeral and not stored beyond the duration of the API request. Uploaded images are automatically deleted after they have been processed.

#### Where can I find details on data privacy for image uploads?

Refer to the Juglow privacy policy page for information on how uploaded images and other data are handled. Juglow does not use uploaded images to train models.

#### What if Haijun's image interpretation seems wrong?

If Haijun's image interpretation seems incorrect:

  1. Ensure the image is clear, high-quality, and correctly oriented.
  2. Try prompt engineering techniques to improve results.
  3. If the issue persists, flag the output in haijun.ai (thumbs up/down) or contact the support team.

Your feedback helps improve Haijun!

#### Can Haijun generate or edit images?

No, Haijun is an image understanding model only. It can interpret and analyze images, but it cannot generate, produce, edit, manipulate, or create images.


Next steps

Get tips and best-practice techniques for tasks such as interpreting charts and extracting content from forms.

See the Messages API documentation, including example API calls involving images.

On this page
Send images to HaijunBase64-encoded image exampleURL-based image exampleFiles API image exampleMultiple imagesImage limits and costsRequest limitsSupported formatsResolution and token costImage quality guidanceCoordinates and bounding boxesLimitationsFAQNext steps