# Describe objects in an image with MandAPI

Send a local image to a current Vision-capable model through Chat Completions.

## Choose a Vision-capable model

The current public MandAPI pricing metadata marks several enabled models as **Vision**. Choose an exact model ID available to your account from the [current model list](/en/pricing). Replace `MODEL_ID` below with that ID; a text-only model is not a substitute.

This is a compatibility recipe checked against current metadata and local request handling. No real image inference was run for this guide. Object descriptions can be uncertain; they are not guaranteed measurements or a tested detection benchmark.

## Save a small request script

Use Python 3.10 or newer and save the following as `vision.py`. The key stays in your environment. The image is sent as a data URL, so the provider does not need to fetch a public image link.

```python
import argparse
import base64
import json
import mimetypes
import os
from pathlib import Path
import urllib.error
import urllib.request

URL = "https://api.mandapi.com/v1/chat/completions"

def main():
    parser = argparse.ArgumentParser()
    mode = parser.add_mutually_exclusive_group(required=True)
    mode.add_argument("--demo", action="store_true")
    mode.add_argument("--live", action="store_true")
    parser.add_argument("--model", default="MODEL_ID")
    parser.add_argument("--image", type=Path)
    parser.add_argument("--language", choices=("en", "es"), default="en")
    args = parser.parse_args()

    image_url = "data:image/jpeg;base64,IMAGE_BYTES"
    if args.live:
        key = os.environ.get("MANDAPI_API_KEY")
        if not key or args.model == "MODEL_ID" or not args.image:
            parser.error("--live needs a private key, a real --model and --image")
        mime = mimetypes.guess_type(args.image.name)[0]
        if mime not in {"image/jpeg", "image/png", "image/webp"}:
            parser.error("Use a JPEG, PNG or WebP image")
        image_url = "data:" + mime + ";base64," + base64.b64encode(
            args.image.read_bytes()
        ).decode("ascii")

    payload = {
        "model": args.model,
        "messages": [{
            "role": "user",
            "content": [
                {"type": "text", "text": (
                    "Enumera los objetos visibles con una descripción breve. "
                    "Indica la incertidumbre; no deduzcas medidas exactas."
                    if args.language == "es" else
                    "List the visible objects and a short description. "
                    "Say when uncertain. Do not infer exact measurements."
                )},
                {"type": "image_url", "image_url": {"url": image_url}}
            ]
        }],
        "max_tokens": 300
    }
    if args.demo:
        print(json.dumps({"mode": "offline-preview", "request": payload}, indent=2))
        return

    request = urllib.request.Request(
        URL, data=json.dumps(payload).encode("utf-8"),
        headers={"Authorization": "Bearer " + key,
                 "Content-Type": "application/json"}
    )
    try:
        with urllib.request.urlopen(request, timeout=90) as response:
            result = json.load(response)
    except urllib.error.HTTPError as error:
        raise SystemExit("MandAPI HTTP " + str(error.code)) from None
    print(result["choices"][0]["message"]["content"])

if __name__ == "__main__":
    main()
```

## Preview without making a request

```bash
python vision.py --demo
```

This prints the request shape with a placeholder for the image bytes. It does not read a photo or call MandAPI. Pick a small, ordinary JPEG, PNG or WebP for your later live check.

## Run with your own image

Set the key privately in the same terminal. In Bash:

```bash
read -rsp "MandAPI key: " MANDAPI_API_KEY
printf '\n'
export MANDAPI_API_KEY
```

Or in PowerShell:

```powershell
$env:MANDAPI_API_KEY = [Net.NetworkCredential]::new('', (Read-Host 'MandAPI key' -AsSecureString)).Password
```

```bash
python vision.py --live --image photo.jpg --model MODEL_ID
```

The explicit `--live` command sends one chargeable model request. After you run it, compare the answer with the photo and find the corresponding MandAPI usage entry. Keep the API key on your backend if you integrate this into a web app.

## Fix common failures

If the model rejects images, recheck its current Vision capability and the exact ID. A valid image extension does not repair corrupt image bytes; open the file locally and re-export it when needed. Check the selected model's image size and format requirements if it rejects a file. If a model rejects `max_tokens`, use that model's documented output-limit setting rather than inventing another field.

Ask for a list of visible objects or a short description first. Add structured extraction only after checking that the selected model follows your schema; do not promise precise bounding boxes from this example.

## References

- [https://app.mandapi.com/api/pricing](https://app.mandapi.com/api/pricing)
- [https://app.mandapi.com/catalog.json](https://app.mandapi.com/catalog.json)
- [https://openrouter.ai/docs/cookbook/image-understanding/find-objects-in-images](https://openrouter.ai/docs/cookbook/image-understanding/find-objects-in-images)
