Batch Prediction¶
Submit one or more images and receive a complete result once all processing is done. Use this when you don't need real-time progress and prefer a single response object.
Two versions are available:
- v1 — upload files directly as
multipart/form-data - v2 — pass image URLs in a JSON body (no file upload needed)
POST /inference/predict/batch (v1)¶
Request¶
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
api_key |
string |
Yes | Your API key |
query |
string |
No | Natural language query for LLM analysis |
files |
File[] |
Yes | One or more image files |
metadata |
string[] |
Yes | One JSON string per file controlling inference flags (see below) |
Each metadata entry is a JSON-stringified object that controls how the corresponding file is processed:
| Key | Type | Description |
|---|---|---|
is_yolo |
boolean |
Run YOLO object detection on this file |
is_llm |
boolean |
Run LLM-based analysis on this file |
Metadata ordering
metadata is a parallel array to files. The first metadata entry applies to the first file, the second to the second, and so on. Both arrays must have the same length.
Example form fields¶
query = "find me the serial number from the images"
api_key = "e2bc19ba-9015-4956-a712-dd96eb22dcb9"
files = [image1.jpg, image2.jpg, image3.jpg]
metadata = {"is_yolo": true, "is_llm": false}
metadata = {"is_yolo": true, "is_llm": true}
metadata = {"is_yolo": false, "is_llm": true}
Response¶
Code Examples¶
async function predictBatch(apiKey, query, files, metadata) {
const form = new FormData();
form.append("api_key", apiKey);
if (query) form.append("query", query);
for (const file of files) {
form.append("files", file);
}
// Each metadata entry must be a JSON string
for (const m of metadata) {
form.append("metadata", JSON.stringify(m));
}
const response = await fetch(
"https://sightapi.raku.so/api/v1/inference/predict/batch",
{ method: "POST", body: form }
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail?.message ?? "Request failed");
}
return response.json();
}
// Usage (browser)
const fileInput = document.querySelector("input[type=file]");
const result = await predictBatch(
"YOUR_API_KEY",
"find me the serial number from the images",
[...fileInput.files],
[
{ is_yolo: true, is_llm: false },
{ is_yolo: true, is_llm: true },
{ is_yolo: false, is_llm: true },
]
);
console.log(result);
interface FileMetadata {
is_yolo: boolean;
is_llm: boolean;
}
interface BatchResult {
result: unknown;
code: number;
}
async function predictBatch(
apiKey: string,
query: string | null,
files: File[],
metadata: FileMetadata[]
): Promise<BatchResult> {
const form = new FormData();
form.append("api_key", apiKey);
if (query) form.append("query", query);
for (const file of files) {
form.append("files", file);
}
// Each metadata entry must be a JSON string
for (const m of metadata) {
form.append("metadata", JSON.stringify(m));
}
const response = await fetch(
"https://sightapi.raku.so/api/v1/inference/predict/batch",
{ method: "POST", body: form }
);
if (!response.ok) {
const error = await response.json();
throw new Error((error.detail as { message: string })?.message ?? "Request failed");
}
return response.json() as Promise<BatchResult>;
}
// Usage
const result = await predictBatch(
"YOUR_API_KEY",
"find me the serial number from the images",
files,
[
{ is_yolo: true, is_llm: false },
{ is_yolo: true, is_llm: true },
{ is_yolo: false, is_llm: true },
]
);
import json
import httpx
def predict_batch(
api_key: str,
query: str | None,
file_paths: list[str],
metadata: list[dict],
) -> dict:
files = [("files", open(p, "rb")) for p in file_paths]
# metadata entries must be JSON strings
data = {
"api_key": api_key,
"metadata": [json.dumps(m) for m in metadata],
}
if query:
data["query"] = query
with httpx.Client() as client:
response = client.post(
"https://sightapi.raku.so/api/v1/inference/predict/batch",
data=data,
files=files,
)
response.raise_for_status()
return response.json()
# Usage
result = predict_batch(
api_key="YOUR_API_KEY",
query="find me the serial number from the images",
file_paths=["./img1.jpg", "./img2.jpg", "./img3.jpg"],
metadata=[
{"is_yolo": True, "is_llm": False},
{"is_yolo": True, "is_llm": True },
{"is_yolo": False, "is_llm": True },
],
)
print(result)
using System.Net.Http;
using System.Text.Json;
public record FileMetadata(bool IsYolo, bool IsLlm);
public class RakuSightClient
{
private static readonly HttpClient Http = new();
public static async Task<JsonDocument> PredictBatchAsync(
string apiKey,
string? query,
IEnumerable<(string FileName, Stream Content)> files,
IEnumerable<FileMetadata> metadata)
{
using var form = new MultipartFormDataContent();
form.Add(new StringContent(apiKey), "api_key");
if (query is not null)
form.Add(new StringContent(query), "query");
foreach (var (fileName, content) in files)
form.Add(new StreamContent(content), "files", fileName);
// Each metadata entry must be a JSON string
foreach (var m in metadata)
{
var json = JsonSerializer.Serialize(new { is_yolo = m.IsYolo, is_llm = m.IsLlm });
form.Add(new StringContent(json), "metadata");
}
var response = await Http.PostAsync(
"https://sightapi.raku.so/api/v1/inference/predict/batch",
form);
response.EnsureSuccessStatusCode();
return JsonDocument.Parse(await response.Content.ReadAsStringAsync());
}
}
// Usage
await using var s1 = File.OpenRead("img1.jpg");
await using var s2 = File.OpenRead("img2.jpg");
await using var s3 = File.OpenRead("img3.jpg");
var result = await RakuSightClient.PredictBatchAsync(
"YOUR_API_KEY",
"find me the serial number from the images",
[("img1.jpg", s1), ("img2.jpg", s2), ("img3.jpg", s3)],
[
new FileMetadata(IsYolo: true, IsLlm: false),
new FileMetadata(IsYolo: true, IsLlm: true),
new FileMetadata(IsYolo: false, IsLlm: true),
]);
Console.WriteLine(result.RootElement);
Recommended: OkHttp
File multipart uploads in plain Java are verbose. Use OkHttp for a simpler experience:
import okhttp3.*;
import java.io.File;
OkHttpClient client = new OkHttpClient();
// Each metadata entry is a JSON string
RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("api_key", "YOUR_API_KEY")
.addFormDataPart("query", "find me the serial number from the images")
.addFormDataPart("metadata", "{\"is_yolo\": true, \"is_llm\": false}")
.addFormDataPart("metadata", "{\"is_yolo\": true, \"is_llm\": true}")
.addFormDataPart("metadata", "{\"is_yolo\": false, \"is_llm\": true}")
.addFormDataPart("files", "img1.jpg",
RequestBody.create(new File("img1.jpg"), MediaType.parse("image/jpeg")))
.addFormDataPart("files", "img2.jpg",
RequestBody.create(new File("img2.jpg"), MediaType.parse("image/jpeg")))
.addFormDataPart("files", "img3.jpg",
RequestBody.create(new File("img3.jpg"), MediaType.parse("image/jpeg")))
.build();
Request request = new Request.Builder()
.url("https://sightapi.raku.so/api/v1/inference/predict/batch")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
For dynamic metadata, use Jackson to serialize each entry:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
ObjectMapper mapper = new ObjectMapper();
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("api_key", "YOUR_API_KEY")
.addFormDataPart("query", "find me the serial number from the images");
List<Map<String, Boolean>> metadataList = List.of(
Map.of("is_yolo", true, "is_llm", false),
Map.of("is_yolo", true, "is_llm", true),
Map.of("is_yolo", false, "is_llm", true)
);
for (Map<String, Boolean> m : metadataList) {
builder.addFormDataPart("metadata", mapper.writeValueAsString(m));
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
)
type FileMetadata struct {
IsYolo bool `json:"is_yolo"`
IsLlm bool `json:"is_llm"`
}
func predictBatch(
apiKey, query string,
filePaths []string,
metadata []FileMetadata,
) (map[string]any, error) {
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
_ = w.WriteField("api_key", apiKey)
if query != "" {
_ = w.WriteField("query", query)
}
// Each metadata entry must be a JSON string
for _, m := range metadata {
b, _ := json.Marshal(m)
_ = w.WriteField("metadata", string(b))
}
for _, path := range filePaths {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
part, _ := w.CreateFormFile("files", filepath.Base(path))
io.Copy(part, f)
}
w.Close()
resp, err := http.Post(
"https://sightapi.raku.so/api/v1/inference/predict/batch",
w.FormDataContentType(),
&buf,
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
func main() {
result, err := predictBatch(
"YOUR_API_KEY",
"find me the serial number from the images",
[]string{"./img1.jpg", "./img2.jpg", "./img3.jpg"},
[]FileMetadata{
{IsYolo: true, IsLlm: false},
{IsYolo: true, IsLlm: true},
{IsYolo: false, IsLlm: true},
},
)
if err != nil {
panic(err)
}
fmt.Println(result)
}
POST /inference/predict/batch/v2¶
No file uploads — pass image URLs directly in a JSON body. Useful when images are already hosted (e.g., cloud storage).
Request¶
Content-Type: application/json
{
"api_key": "YOUR_API_KEY",
"query": "What objects are in this image?",
"data": [
{
"image_url": "https://storage.example.com/images/photo.jpg",
"filename": "photo.jpg",
"is_yolo": true,
"is_llm": false
}
]
}
Response¶
Code Examples¶
async function predictBatchV2(apiKey, query, data) {
const response = await fetch(
"https://sightapi.raku.so/api/v1/inference/predict/batch/v2",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ api_key: apiKey, query, data }),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail?.message ?? "Request failed");
}
return response.json();
}
// Usage
const result = await predictBatchV2("YOUR_API_KEY", "Describe the scene", [
{
image_url: "https://storage.example.com/images/photo.jpg",
filename: "photo.jpg",
is_yolo: true,
is_llm: false,
},
]);
console.log(result);
interface DataItem {
image_url: string;
filename: string;
is_yolo: boolean;
is_llm: boolean;
}
interface BatchResult {
result: unknown;
code: number;
}
async function predictBatchV2(
apiKey: string,
query: string,
data: DataItem[]
): Promise<BatchResult> {
const response = await fetch(
"https://sightapi.raku.so/api/v1/inference/predict/batch/v2",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ api_key: apiKey, query, data }),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error((error.detail as { message: string })?.message ?? "Request failed");
}
return response.json() as Promise<BatchResult>;
}
import httpx
def predict_batch_v2(api_key: str, query: str, data: list[dict]) -> dict:
payload = {
"api_key": api_key,
"query": query,
"data": data,
}
with httpx.Client() as client:
response = client.post(
"https://sightapi.raku.so/api/v1/inference/predict/batch/v2",
json=payload,
)
response.raise_for_status()
return response.json()
# Usage
result = predict_batch_v2(
api_key="YOUR_API_KEY",
query="Describe the scene",
data=[
{
"image_url": "https://storage.example.com/images/photo.jpg",
"filename": "photo.jpg",
"is_yolo": True,
"is_llm": False,
}
],
)
print(result)
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
public record DataItem(string ImageUrl, string Filename, bool IsYolo, bool IsLlm);
public class RakuSightClient
{
private static readonly HttpClient Http = new();
public static async Task<JsonDocument> PredictBatchV2Async(
string apiKey, string query, IEnumerable<DataItem> data)
{
var payload = new
{
api_key = apiKey,
query,
data = data.Select(d => new
{
image_url = d.ImageUrl,
filename = d.Filename,
is_yolo = d.IsYolo,
is_llm = d.IsLlm,
})
};
var response = await Http.PostAsJsonAsync(
"https://sightapi.raku.so/api/v1/inference/predict/batch/v2",
payload);
response.EnsureSuccessStatusCode();
return JsonDocument.Parse(await response.Content.ReadAsStringAsync());
}
}
// Usage
var result = await RakuSightClient.PredictBatchV2Async(
"YOUR_API_KEY",
"Describe the scene",
[new DataItem("https://storage.example.com/images/photo.jpg", "photo.jpg", true, false)]);
Console.WriteLine(result.RootElement);
import java.net.http.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
public class RakuSightClient {
private static final HttpClient CLIENT = HttpClient.newHttpClient();
public static String predictBatchV2(String apiKey, String query, String dataJson) throws Exception {
String body = String.format(
"{\"api_key\":\"%s\",\"query\":\"%s\",\"data\":%s}",
apiKey, query, dataJson
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://sightapi.raku.so/api/v1/inference/predict/batch/v2"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = CLIENT.send(
request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Request failed: " + response.body());
}
return response.body();
}
public static void main(String[] args) throws Exception {
String data = "[{\"image_url\":\"https://storage.example.com/images/photo.jpg\"," +
"\"filename\":\"photo.jpg\",\"is_yolo\":true,\"is_llm\":false}]";
String result = predictBatchV2("YOUR_API_KEY", "Describe the scene", data);
System.out.println(result);
}
}
Tip
Use a JSON library like Jackson or Gson to avoid manual string formatting.
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type DataItem struct {
ImageURL string `json:"image_url"`
Filename string `json:"filename"`
IsYolo bool `json:"is_yolo"`
IsLlm bool `json:"is_llm"`
}
type PredictRequest struct {
APIKey string `json:"api_key"`
Query string `json:"query"`
Data []DataItem `json:"data"`
}
func predictBatchV2(apiKey, query string, data []DataItem) (map[string]any, error) {
payload, _ := json.Marshal(PredictRequest{
APIKey: apiKey,
Query: query,
Data: data,
})
resp, err := http.Post(
"https://sightapi.raku.so/api/v1/inference/predict/batch/v2",
"application/json",
bytes.NewReader(payload),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
func main() {
result, err := predictBatchV2("YOUR_API_KEY", "Describe the scene", []DataItem{
{
ImageURL: "https://storage.example.com/images/photo.jpg",
Filename: "photo.jpg",
IsYolo: true,
IsLlm: false,
},
})
if err != nil {
panic(err)
}
fmt.Println(result)
}