Files and images
Kalle can read what people see: screenshots, photos of a whiteboard, PDFs and documents. Send them as blocks in a message, next to your text.
curl https://api.kalle.build/v1/messages \
-H "Authorization: Bearer $KALLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "url", "url": "https://example.com/landing-page.png" }
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
}'const response = await fetch("https://api.kalle.build/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KALLE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "kalle-design",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: { type: "url", url: "https://example.com/landing-page.png" }
},
{
type: "text",
text: "Suggest three changes that would make this page easier to scan."
}
]
}
]
}),
});
console.log(await response.json());import os
import requests
response = requests.post(
"https://api.kalle.build/v1/messages",
headers={"Authorization": f"Bearer {os.environ['KALLE_API_KEY']}"},
json={
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://example.com/landing-page.png"
}
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
},
)
response.raise_for_status()
print(response.json())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
body := strings.NewReader(`{
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "url", "url": "https://example.com/landing-page.png" }
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
}`)
req, _ := http.NewRequest("POST", "https://api.kalle.build/v1/messages", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KALLE_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}require "json"
require "net/http"
uri = URI("https://api.kalle.build/v1/messages")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("KALLE_API_KEY")}"
request["Content-Type"] = "application/json"
request.body = <<~JSON
{
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "url", "url": "https://example.com/landing-page.png" }
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
}
JSON
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.pretty_generate(JSON.parse(response.body))<?php
$body = <<<'JSON'
{
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "url", "url": "https://example.com/landing-page.png" }
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
}
JSON;
$ch = curl_init("https://api.kalle.build/v1/messages");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KALLE_API_KEY"),
"Content-Type: application/json",
],
]);
echo curl_exec($ch);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class KalleExample {
public static void main(String[] args) throws Exception {
String body = """
{
"model": "kalle-design",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "url", "url": "https://example.com/landing-page.png" }
},
{
"type": "text",
"text": "Suggest three changes that would make this page easier to scan."
}
]
}
]
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.kalle.build/v1/messages"))
.header("Authorization", "Bearer " + System.getenv("KALLE_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}Images
Add an image block to the content of a user message. Kalle Design is the best fit for screenshots of interfaces, layouts and brand material, and every model can read images.
Supported formats are PNG, JPEG, WebP and GIF. For a GIF, Kalle reads the first frame.
Documents
Add a document block for PDFs and text files. Kalle reads the text and, for PDFs, the pages as images too, so tables, charts and scanned pages work.
{
"type": "document",
"source": { "type": "url", "url": "https://example.com/annual-report.pdf" },
"title": "Annual report 2026"
}Supported formats are PDF, plain text, Markdown and CSV.
Sources
Every image and document has a source. Use a URL when the file is public, and base64 when it is not.
source.type | Fields | Use it when |
|---|---|---|
url | url | The file is reachable over HTTPS without signing in. |
base64 | media_type, data | The file is private or lives on your server. |
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAA..."
}
}What files cost
Images and documents are counted as input tokens, like text. A larger image or a longer document costs more. usage.input_tokens in the response includes them, so you always see the real cost of a request.
Tips
- Put the files before your question in the same message. Kalle reads in order.
- Crop screenshots to what matters. Less noise gives sharper answers and costs fewer tokens.
- Tell Kalle what the file is: "This is our current pricing page" helps more than you think.