Muse MuseConcept
Dashboard

Independent concept built on Meta's Model API. Not an official Meta product.

Vision input

View on dev.meta.ai (opens in a new tab)

What you'll build: a chat completion that takes an image plus a question and returns an analysis, using the same endpoint you already know.

SectionAPI fundamentals
CapabilitiesVision
Time to complete~10 min
Modelmuse-spark-1.3
LanguagePython
Prerequisitesthe chat completions recipe, Python 3.10+, the openai package, and a MODEL_API_KEY.

You send an image as a content part inside a user message. The image can be a public URL the server fetches, or a base64 data URL you inline. See the image understanding guide.

Setup

One cell wires up the client: it reads your key from the environment and falls back to Colab secrets when you run in a notebook.

python
import os

from openai import OpenAI

# Auth: in Colab, read the key from the secrets manager (the key icon in the
# left sidebar); otherwise read the MODEL_API_KEY environment variable.
try:
    from google.colab import userdata

    api_key = userdata.get("MODEL_API_KEY")
except Exception:
    api_key = os.environ.get("MODEL_API_KEY")

if not api_key:
    raise RuntimeError(
        "Set MODEL_API_KEY in your environment (or add it to Colab secrets)."
    )

# The OpenAI SDK does not auto-read MODEL_API_KEY, so pass it explicitly.
client = OpenAI(base_url="https://api.meta.ai/v1", api_key=api_key)

# Connectivity check: a one-line call that confirms the key and endpoint work.
ping = client.chat.completions.create(
    model="muse-spark-1.3",
    messages=[{"role": "user", "content": "Reply with the single word: ready"}],
)
print(ping.choices[0].message.content)

Send an image

A message's content becomes a list of parts: a text part for your question and an image_url part for the picture. Here the image is a base64 data URL, so the example runs with no external host.

python
# A small, self-contained test image (256x256: red left half, blue right half),
# encoded as a base64 data URL so this recipe needs no external image host.
SAMPLE_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAAC/0lEQVR42u3TsQkAAAzDsPz/dDvngiwCX2BQLtEwD8Z5AAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAA8AAAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAGQAAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAAAOABAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAADIAwAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAAADAAwAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgLoHSlsO8jZgvpcAAAAASUVORK5CYII="

response = client.chat.completions.create(
    model="muse-spark-1.3",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Describe this image in one short sentence."},
                {"type": "image_url", "image_url": {"url": SAMPLE_IMAGE}},
            ],
        }
    ],
)
print(response.choices[0].message.content)

Compare several images in one request

A single user message can hold multiple image_url parts, so you can compare or rank images in one call. The model sees them in the order you list them; the practical ceiling is about 50 images per request (also bounded by payload size and the context window). Here three solid-color swatches go in one request:

python
# Generate a few small solid-color PNGs with the standard library (no extra
# dependencies), so the multi-image example needs no external image host.
import base64
import struct
import zlib


def solid_png(rgb, size=64):
    """Return a base64 PNG data URL for a size x size solid-color square."""

    def chunk(tag, data):
        body = tag + data
        return (
            struct.pack(">I", len(data))
            + body
            + struct.pack(">I", zlib.crc32(body) & 0xFFFFFFFF)
        )

    ihdr = struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0)  # 8-bit RGB
    row = b"\x00" + bytes(rgb) * size  # filter byte + one pixel row
    idat = zlib.compress(row * size)
    png = (
        b"\x89PNG\r\n\x1a\n"
        + chunk(b"IHDR", ihdr)
        + chunk(b"IDAT", idat)
        + chunk(b"IEND", b"")
    )
    return "data:image/png;base64," + base64.b64encode(png).decode()


swatches = {
    "red": solid_png((220, 30, 30)),
    "green": solid_png((30, 200, 60)),
    "blue": solid_png((30, 60, 220)),
}

response = client.chat.completions.create(
    model="muse-spark-1.3",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Each image is a solid-color swatch. List the colors in the order shown.",
                },
                *(
                    {"type": "image_url", "image_url": {"url": url}}
                    for url in swatches.values()
                ),
            ],
        }
    ],
)
print(response.choices[0].message.content)

Send a hosted image by URL

Instead of inlining base64, you can point image_url at a public URL and the server fetches it. The request shape is identical:

python
response = client.chat.completions.create(
    model="muse-spark-1.3",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What animal is this?"},
                {"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}},
            ],
        }
    ],
)

Use base64 for local or private images, and a URL when the image is already hosted somewhere the server can reach. See the image understanding guide.

Images go in user messages only

Image content is accepted only on user-role messages. Put it in a developer or system message and the request is rejected with HTTP 400 rather than silently dropped.

python
from openai import BadRequestError

# Wrong: image content placed in a developer message
try:
    client.chat.completions.create(
        model="muse-spark-1.3",
        messages=[
            {
                "role": "developer",
                "content": [
                    {"type": "text", "text": "Reference image:"},
                    {"type": "image_url", "image_url": {"url": SAMPLE_IMAGE}},
                ],
            },
            {"role": "user", "content": "What colors are in the reference image?"},
        ],
    )
except BadRequestError as exc:
    print("HTTP 400:", str(exc)[:160])

The fix is the shape from the first example: keep the image in the user message.

Measure image tokens before you send

An image costs real input tokens, and the amount scales with resolution rather than a flat per-image fee. The image calls above use Chat Completions. To count tokens before you send, use the Responses API helper POST /v1/responses/input_tokens, which returns the token count without generating anything, it is a measurement utility, not a different way to send the image, and the cost it reports is the same on either endpoint. Note the Responses input shape differs: input_text and input_image, not the Chat text / image_url.

python
def count_input_tokens(input_items):
    result = client.post(
        "/responses/input_tokens",
        body={"model": "muse-spark-1.3", "input": input_items},
        cast_to=object,
    )
    return result["input_tokens"]


text_only = [
    {
        "role": "user",
        "content": [{"type": "input_text", "text": "Describe this image."}],
    }
]
with_image = [
    {
        "role": "user",
        "content": [
            {"type": "input_text", "text": "Describe this image."},
            {"type": "input_image", "image_url": SAMPLE_IMAGE},
        ],
    }
]

print("text only: ", count_input_tokens(text_only), "tokens")
print("with image:", count_input_tokens(with_image), "tokens")

Send images in production

  • Images go in user messages. A developer or system message with image content returns HTTP 400.
  • URL or base64. image_url accepts a public URL (the server fetches it) or a base64 data URL (inline, no hosting). Supported types: jpeg, jpg, png, gif, webp, x-icon.
  • Measure before sending. Image token cost scales with resolution. Use POST /v1/responses/input_tokens to budget large or multi-image requests.
  • Know the limits. Up to about 50 images per request (a model guideline, also bounded by payload size and the context window), 50 MB per inline image, or up to 1 GiB via the Files API. PDF page-images count toward the same image budget.

Summary

PatternHow
Send an imagean image_url content part inside a user message
Sourcepublic URL (server fetches) or base64 data URL (inline)
Rolesimage content only in user messages
Formatsjpeg, jpg, png, gif, webp, x-icon
Token costscales with resolution; measure with /v1/responses/input_tokens
Limits~50 images/request, 50 MB inline, 1 GiB via Files API

Next steps

Mirrored from dev.meta.ai/docs/cookbook/vision-input (synced Oct 30, 2026). Text and examples © Meta. Live demos run on Muse Muse's key.

Esc

Loading...