Frontend Integration

Ice9 should not be called directly from browser-only JavaScript with a long-lived secret API key.

Security model

The current API key model is designed for trusted server-side environments. If you expose a long-lived X-API-Key in browser code, users can copy it from the page, network traffic, or bundled source and then reuse it outside your application.

That means the supported frontend architecture is:

  1. The browser uploads the image to your application backend.
  2. Your backend calls Ice9 with the real X-API-Key.
  3. Your backend returns the result, or returns an image_id and exposes a safe polling endpoint for the browser.

Browser

  • collects the file from the user
  • sends it to your backend
  • renders progress and results from your backend

Backend

  • stores the real Ice9 API key
  • forwards the file to Ice9
  • polls or streams Ice9 results
  • returns a browser-safe payload

Browser + backend example

Browser code

<input id="file" type="file" accept="image/*" />
<button id="analyze">Analyze</button>
<pre id="output"></pre>

<script type="module">
  const fileInput = document.querySelector("#file");
  const button = document.querySelector("#analyze");
  const output = document.querySelector("#output");

  button.addEventListener("click", async () => {
    const file = fileInput.files?.[0];
    if (!file) {
      output.textContent = "Choose an image first.";
      return;
    }

    const form = new FormData();
    form.set("file", file);
    form.set("tier", "basic");

    const response = await fetch("/api/ice9/analyze", {
      method: "POST",
      body: form,
    });

    if (!response.ok) {
      output.textContent = `Request failed: ${response.status}`;
      return;
    }

    const result = await response.json();
    output.textContent = JSON.stringify(result, null, 2);
  });
</script>

Node.js backend example

import express from "express";
import multer from "multer";
import FormData from "form-data";

const app = express();
const upload = multer();

app.post("/api/ice9/analyze", upload.single("file"), async (req, res) => {
  if (!req.file) {
    res.status(400).json({ error: "Missing file" });
    return;
  }

  const form = new FormData();
  form.append("file", req.file.buffer, req.file.originalname || "upload.jpg");
  form.append("tier", req.body.tier || "free");
  form.append("image_group", "web");

  const analyzeResponse = await fetch("https://api.ice9.ai/analyze", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.ICE9_API_KEY,
    },
    body: form,
  });

  if (!analyzeResponse.ok) {
    const detail = await analyzeResponse.text();
    res.status(analyzeResponse.status).send(detail);
    return;
  }

  const { image_id } = await analyzeResponse.json();

  while (true) {
    const statusResponse = await fetch(`https://api.ice9.ai/status/${image_id}`, {
      headers: {
        "X-API-Key": process.env.ICE9_API_KEY,
      },
    });

    const status = await statusResponse.json();
    if (status.is_complete) {
      res.json(status);
      return;
    }

    await new Promise((resolve) => setTimeout(resolve, 500));
  }
});

Real-time progress in web apps

If your UI needs live progress:

  • keep the Ice9 API call on your backend
  • stream updates from your backend to the browser with SSE or WebSockets
  • do not move the Ice9 API key into browser code

When to use an SDK

If your backend is Python, use Python SDK.

If your backend is Node.js, use Node.js SDK.

If you are building your own proxy or custom client, use API Reference.