Skip to content

Classifying Images

The image classification endpoint runs a Vision Transformer over an input image and returns a set of labels with confidence scores. The recommended model is Freepik/nsfw_image_detector, an EVA-02 based classifier that produces a four-bucket severity breakdown — far more useful for tunable moderation than a single normal / nsfw flag.

The model returns probabilities across four labels that sum to 1.0:

LabelMeaning
neutralSafe — no NSFW content
lowMildly suggestive
mediumSuggestive or borderline
highExplicit

Two endpoints are exposed:

  • inference.vit.img2label.v1 — returns a single JSON document. Labels are embedded under config.labels on the returned job.
  • inference.vit.img2label.v2 — returns a multipart response containing labels.json and the original input image. Useful when chaining classification into a wider workflow that also needs the image bytes downstream.
Terminal window
# Create a project directory.
mkdir prodia-classifying-images
cd prodia-classifying-images

Install Node (if not already installed):

Terminal window
brew install node
# Close the current terminal and open a new one so that node is available.

Create project skeleton:

Terminal window
# Requires node --version >= 18
# Initialize the project with npm.
npm init -y
# Install the prodia-js library.
npm install prodia --save
Terminal window
# Export your token so it can be used by the main code.
export PRODIA_TOKEN=your-token-here

Your token is exported to an environment variable. If you close or switch your shell you’ll need to run export PRODIA_TOKEN=your-token-here again.

Create a main file for your project:

main.js
const { createProdia } = require("prodia/v2");
const prodia = createProdia({
token: process.env.PRODIA_TOKEN // get it from environment
});

You’re now ready to make some API calls!

Pass the input image and request Freepik/nsfw_image_detector. The response contains a labels object with one score per severity bucket.

main.js
const { createProdia } = require("prodia/v2");
const prodia = createProdia({
token: process.env.PRODIA_TOKEN,
});
(async () => {
// get input image
const inputBuffer = await (await fetch("https://docs.prodia.com/sunny-day.jpg")).arrayBuffer();
const { job } = await prodia.job({
type: "inference.vit.img2label.v1",
config: {
model: "Freepik/nsfw_image_detector",
},
}, {
inputs: [ inputBuffer ],
});
console.log(job.config.labels);
// => { neutral: 0.9995, high: 0.00038, low: 0.000087, medium: 0.000056 }
})();
Terminal window
node main.js

The full response looks like this:

{
"type": "inference.vit.img2label.v1",
"id": "65a10fac-b90f-40a8-8f5c-e41dbfbb4991",
"state": { "current": "completed" },
"config": {
"model": "Freepik/nsfw_image_detector",
"labels": {
"neutral": 0.9994731545448303,
"high": 0.0003829085035249591,
"low": 0.00008746454113861546,
"medium": 0.00005647135549224913
}
},
"metrics": { "elapsed": 0.16 }
}

The four scores sum to 1.0 and represent the model’s belief that the image primarily belongs to that severity bucket. To turn them into an allow/block decision, the Freepik model card recommends a cumulative scheme: pick the lowest severity you want to flag, sum that bucket and all higher ones, then compare against a threshold (typically 0.5).

PolicyScore formulaUse case
Strict — block only explicitP(high)Permissive platforms; reject only the most extreme content
Moderate — block suggestive and upP(medium) + P(high)General audiences; common default
Lenient — block anything non-neutralP(low) + P(medium) + P(high)Family-safe / under-13 surfaces

The closer the threshold is to 0, the more aggressively borderline images are flagged (more false positives). The closer to 1, the more permissive (more false negatives).

A minimal moderate-policy check looks like this:

const { neutral, low, medium, high } = job.config.labels;
const flagged = (medium + high) > 0.5;

If you want both the labels and a pass-through copy of the original image in a single response — for example, to pipe straight into a downstream job in a workflow — use the v2 endpoint. It returns a multipart body containing labels.json followed by the original image bytes.

main.js
const { createProdia } = require("prodia/v2");
const fs = require("node:fs/promises");
const prodia = createProdia({
token: process.env.PRODIA_TOKEN,
});
(async () => {
const inputBuffer = await (await fetch("https://docs.prodia.com/sunny-day.jpg")).arrayBuffer();
const result = await prodia.job({
type: "inference.vit.img2label.v2",
config: { model: "Freepik/nsfw_image_detector" },
}, {
accept: "multipart/form-data",
inputs: [ inputBuffer ],
});
const form = await result.formData();
const outputs = form.getAll("output");
// outputs[0] is labels.json, outputs[1] is the original image
const labels = JSON.parse(await outputs[0].text());
console.log(labels);
const imageBuffer = await outputs[1].arrayBuffer();
await fs.writeFile("passthrough.jpg", new Uint8Array(imageBuffer));
})();
Terminal window
node main.js
ConstraintValue
Accepted formatsPNG, JPEG, WebP
Minimum dimensions128 x 128
Maximum dimensions2048 x 2048
Maximum file size10 MB