---
title: "Images"
description: "Check an image with POST /v1/image, sent as bytes or as a public URL."
canonical_url: "https://openshield.dev/docs/images"
---
# Images

> Check an image with POST /v1/image, sent as bytes or as a public URL.

`POST /v1/image` checks one image and answers with the decision. One image is one check, whatever the number of categories it is checked against.

## Request

<table>
<thead>
  <tr>
    <th>
      Part
    </th>
    
    <th>
      Where
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      The image
    </td>
    
    <td>
      Body
    </td>
    
    <td>
      The raw bytes. JPEG, PNG, WebP or GIF (the first frame). The API recognises the format from the bytes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        workflow
      </code>
    </td>
    
    <td>
      Query
    </td>
    
    <td>
      The <a href="https://openshield.dev/docs/workflows">
        workflow
      </a>
      
       to check against. <code>
        default
      </code>
      
       when absent.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ref
      </code>
    </td>
    
    <td>
      Query
    </td>
    
    <td>
      Your own id for the image, 1 to 128 characters, no control character. It is echoed in the answer and kept in the <a href="https://openshield.dev/docs/journal-and-ref">
        journal
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        url
      </code>
    </td>
    
    <td>
      Query
    </td>
    
    <td>
      The address of the image, instead of a body. See below.
    </td>
  </tr>
</tbody>
</table>

Limits: at most 15 MB, 50 megapixels (12 on the free plan) and 16,384 pixels on a side.

<code-group>

```bash [curl]
curl -X POST "https://api.openshield.dev/v1/image?workflow=default&ref=avatar_4821" \
  -H "Authorization: Bearer $OPENMOD_API_KEY" \
  --data-binary @photo.jpg
```

```ts [TypeScript]
import { readFile } from "node:fs/promises"

const response = await fetch("https://api.openshield.dev/v1/image?ref=avatar_4821", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.OPENMOD_API_KEY}` },
  body: await readFile("photo.jpg"),
})
const answer = await response.json()
console.log(answer.summary.action)
```

</code-group>

## Checking a public URL

Instead of sending the bytes, pass the address of the image and an empty body. The service downloads it from the public internet:

```bash
curl -X POST "https://api.openshield.dev/v1/image?url=https%3A%2F%2Fexample.com%2Fphoto.jpg" \
  -H "Authorization: Bearer $OPENMOD_API_KEY"
```

The download is guarded: only `http` and `https` addresses of the public internet, at most 15 MB, 3 redirects and 10 seconds, and the answer must have `Content-Type: image/*`. A refused address gets `400 url_refused`, with the reason in `message`.

## What is checked

The categories of the workflow. The `default` workflow checks nudity, gore, weapons, hate symbols and self-harm (rejected at 0.8, sent to review at 0.5), drugs and minors (sent to review at 0.5), and the text and QR codes found in the image: links to adult, unsafe or gambling sites, and extremist text. [Workflows](https://openshield.dev/docs/workflows) explains how to change that.

## Answer

The answer has the [shape described here](https://openshield.dev/docs/responses-and-errors). For an image rejected for nudity:

```json
{
  "status": "success",
  "workflow": "default",
  "workflow_version": 1,
  "summary": {
    "action": "reject",
    "reject_prob": 0.97,
    "reject_reason": [
      {
        "id": "nudity",
        "text": "Nudity",
        "message": "This content contains nudity or sexual content.",
        "score": 0.97,
        "threshold": 0.8,
        "source": "marqo_nsfw+owen_safety"
      }
    ],
    "review_reason": []
  },
  "scores": { "nudity": 0.97, "gore": 0.01, "weapon": 0.02 },
  "time_ms": 212,
  "request_id": "0192b7c4-5f3a-7c1e-9a52-3d8f1e6b0a47",
  "ref": "avatar_4821"
}
```

## Errors

<table>
<thead>
  <tr>
    <th>
      Status
    </th>
    
    <th>
      <code>
        error.type
      </code>
    </th>
    
    <th>
      When
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      400
    </td>
    
    <td>
      <code>
        bad_request
      </code>
    </td>
    
    <td>
      The body is empty and there is no <code>
        url
      </code>
      
      , or <code>
        ref
      </code>
      
       is refused.
    </td>
  </tr>
  
  <tr>
    <td>
      400
    </td>
    
    <td>
      <code>
        corrupt_media
      </code>
    </td>
    
    <td>
      The file does not decode.
    </td>
  </tr>
  
  <tr>
    <td>
      400
    </td>
    
    <td>
      <code>
        unknown_workflow
      </code>
    </td>
    
    <td>
      There is no workflow of that name.
    </td>
  </tr>
  
  <tr>
    <td>
      400
    </td>
    
    <td>
      <code>
        url_refused
      </code>
    </td>
    
    <td>
      The <code>
        url
      </code>
      
       is not public, too large, too slow or not an image.
    </td>
  </tr>
  
  <tr>
    <td>
      413
    </td>
    
    <td>
      <code>
        body_too_large
      </code>
    </td>
    
    <td>
      The body is over 15 MB.
    </td>
  </tr>
  
  <tr>
    <td>
      413
    </td>
    
    <td>
      <code>
        image_too_large
      </code>
    </td>
    
    <td>
      The image has more than 50 megapixels (12 on the free plan), or a side over 16,384 pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      415
    </td>
    
    <td>
      <code>
        unsupported_media
      </code>
    </td>
    
    <td>
      The file is not JPEG, PNG, WebP or GIF.
    </td>
  </tr>
  
  <tr>
    <td>
      503
    </td>
    
    <td>
      <code>
        workflow_unavailable
      </code>
    </td>
    
    <td>
      The workflow has no rule for images, or none this server can check.
    </td>
  </tr>
</tbody>
</table>

Authentication, rate limit and quota errors are in [responses and errors](https://openshield.dev/docs/responses-and-errors).

<tip>

Downscale large photos to about 1,280 pixels on the longer side before sending them: the upload is smaller and faster. Keep the full size of screenshots, where small text or a QR code matters.

</tip>


## Sitemap

See the full [sitemap](https://openshield.dev/sitemap.md) for all pages.
