Skip to content

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:

{ "is_yolo": true, "is_llm": false }
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

{
  "result": { },
  "code": 0
}

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

{
  "result": { },
  "code": 0
}

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)
}