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:
- The browser uploads the image to your application backend.
- Your backend calls Ice9 with the real
X-API-Key. - Your backend returns the result, or returns an
image_idand exposes a safe polling endpoint for the browser.
Recommended architecture
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.