Skip to content

Photo Compliance Check ​

Product overview: Photo compliance check.

Check an existing portrait against a document specification without generating a new image. The response is a pass or fail, a score, one result per rule, and either retake guidance or a suggestion to call another endpoint.

Field names use camelCase, the same convention as the Passport Photo API and the Fix Passport Photo API.

A passing result means the rules in this response did not fail. It is not an acceptance decision by a passport office, visa center, employer, or school. IDPhoto.ai does not claim NIST, BSI, FedRAMP, or SOC 2 certification, and this page does not publish an accuracy rate.

When to auto-fix ​

Read spec.alterationAllowed and fixable before you edit the photo.

United States passport photos, United Kingdom passport photos, and US Diversity Visa (DV) lottery photos must not be digitally altered. For those specifications the service returns alterationAllowed: false and fixable: false. Show retakeTips. Do not call POST /v2/aiEdit, and do not replace the background.

That matches the published rule on the pricing page: do not use the Fix Passport Photo API on a US or UK passport photograph. The US Department of State says not to upload a photo created or edited with artificial intelligence or other digital tools. UK digital photo rules say the photo must be unedited. A manipulated DV lottery photo can make the entry ineligible. See the State Department photo requirements.

For any other specification, trust the boolean in the response. Do not infer it from the country name. When fixable is true, fixSuggestion.steps names the next call. Check the result again after that call.

fixable is true only when alterationAllowed is true and every failing rule is one that the suggested endpoint can address. A closed-eye photo, a missing face, a head covering that hides the face, or a digital-alteration hint stays a retake even on a specification that otherwise allows a correction.

Before you start ​

You need an API key and API secret from the Dashboard. If this endpoint is not enabled on the key, contact support@idphoto.ai.

Send requests from your server. Keep both credentials out of browser code, mobile application bundles, source control, and logs. Do not log the image payload.

Choose the API region configured for your account:

RegionBase URL
United Stateshttps://api-us.idphotoapp.com
European Unionhttps://api-eu.idphotoapp.com

Other regional deployments may be available on request. JSON requests use Content-Type: application/json.

Pricing for POST /v2/checkPhoto is available on request. It is not the published starting price of $0.20, which covers preparing a photo. See Pricing.

API endpoint ​

http
POST /v2/checkPhoto

Set a client timeout of at least 60 seconds. A timeout is not proof the check was skipped. Avoid an automatic retry until you know whether a result was produced.

Request ​

Send exactly one image, as Base64 JSON, as an HTTPS URL, or as a multipart file.

FieldTypeRequiredDescription
apiKeystringYesYour API key.
apiSecretstringYesYour API secret.
imageBase64stringOne image sourceBase64-encoded JPEG or PNG, with or without a data URI prefix.
imageUrlstringOne image sourceHTTPS URL of a JPEG or PNG the service can fetch. Private, loopback, and link-local addresses are rejected.
filefileOne image sourceJPEG or PNG, only on a multipart/form-data request.
specCodestringYesTarget document specification, for example us-passport. Use the same codes as the Passport Photo API.
languagestringNoBCP 47 language for message and retakeTips, for example en or de. Defaults to en. An unknown tag falls back to English. The response language field is the language actually used.

The uploaded file must be 10 MB or smaller. JPEG and PNG only.

specCode values are the codes in Spec Code, such as us-passport, uk-passport, us-dvlottery, and germany-passport. Confirm the live code with POST /v2/listIdPhotoSpecCode. Page slugs such as us-passport-photo are not request values.

JSON request ​

json
{
  "apiKey": "your-api-key",
  "apiSecret": "your-api-secret",
  "imageBase64": "data:image/jpeg;base64,...",
  "specCode": "us-passport",
  "language": "en"
}

To send a URL instead of Base64, omit imageBase64 and set imageUrl to an https:// address. Do not send both.

Multipart request ​

bash
curl --request POST \
  --url https://api-us.idphotoapp.com/v2/checkPhoto \
  --form 'apiKey=your-api-key' \
  --form 'apiSecret=your-api-secret' \
  --form 'specCode=us-passport' \
  --form 'language=en' \
  --form 'file=@portrait.jpg;type=image/jpeg'

Response ​

HTTP 200 means the check ran. Read pass to see whether any rule failed. Photo problems are not HTTP errors.

FieldTypeDescription
passbooleantrue when no check has status fail. A warn does not fail the photo. Show warnings anyway.
scoreinteger0 to 100, summarizing the checks on this image. It is not a probability that an authority will accept the photo.
fixablebooleantrue only when an automated correction is allowed for this specification and every failing rule can be handed to the suggested endpoint.
languagestringLanguage used for messages and retake tips.
specobjectSpecification the check used.
spec.specCodestringCode you sent.
spec.specCodeNamestringDisplay name from the catalog.
spec.alterationAllowedbooleanfalse for specifications that forbid a digital alteration, including background replacement.
spec.photoSizestringPhysical size when the specification defines one.
spec.photoPixelstringPixel size when the specification defines one.
spec.backgroundColorstringExpected background when the specification defines one.
checksarrayOne object per rule that ran.
checks[].idstringStable identifier. See the table below.
checks[].namestringShort label for a user interface.
checks[].statusstringpass, warn, or fail.
checks[].measuredstring or nullWhat was measured. null when the rule is qualitative or could not be measured.
checks[].expectedstring or nullWhat the specification asks for.
checks[].messagestringSentence you can show, in the response language.
fixSuggestionobject or nullnull when fixable is false.
fixSuggestion.summarystringWhat to do next.
fixSuggestion.stepsarrayOrdered calls. Each step has endpoint and reason.
retakeTipsarray of stringsEmpty when the photo passed. Otherwise, capture instructions.

Check identifiers ​

idWhat it covers
face_detectedA single usable face. Other rules are not measured when this fails.
size_dimensionsPixel size, physical size, and crop against the specification.
head_size_positionHead height and position in the frame, including a turned or tilted head.
backgroundBackground color and whether it is uniform, with no objects or texture.
lighting_shadowsEven light on the face, and shadows on the face or behind the head.
glasses_glareGlasses where the specification refuses them, sunglasses, and glare on the lenses.
expressionNeutral expression and a closed mouth, when the specification asks for that.
eyesEyes open and looking at the camera.
focusFocus and blur.
compression_resolutionResolution, file size, and heavy compression.
head_coveringHead coverings. A religious or medical covering that leaves the face visible is a warn for a person to review, not an instruction to remove it.
digital_alterationHints of a morph, a generated face, or a retouch that changes the person. Ordinary crop and background processing are not a fail on a specification that allows them.

These rules follow the kinds of requirements published in ICAO 9303, ISO/IEC 19794-5, and ISO/IEC 29794-5, applied to the selected specification. Citing those standards is not a claim that this service is certified against them.

digital_alteration is a hint on this call. A separate early-access screen for AI generation or AI editing is described on the AI photo detection page. Neither result is a government decision.

US passport example ​

This payload is illustrative. alterationAllowed is false, so the client shows retake tips and does not call the fix endpoint.

json
{
  "pass": false,
  "score": 64,
  "fixable": false,
  "language": "en",
  "spec": {
    "specCode": "us-passport",
    "specCodeName": "US Passport",
    "alterationAllowed": false,
    "photoSize": "2 x 2 in",
    "photoPixel": "600 x 600 px",
    "backgroundColor": "#FFFFFF"
  },
  "checks": [
    {
      "id": "face_detected",
      "name": "Face detected",
      "status": "pass",
      "measured": "1 face",
      "expected": "1 face",
      "message": "One face was found."
    },
    {
      "id": "head_size_position",
      "name": "Head size and position",
      "status": "fail",
      "measured": "Head height 42% of the frame",
      "expected": "Head height about 50% to 69% of the frame (1 to 1 3/8 in)",
      "message": "The head is too small. Move closer, or step the camera back in and retake."
    },
    {
      "id": "background",
      "name": "Background",
      "status": "warn",
      "measured": "Off-white with a shadow behind the head",
      "expected": "Plain white or off-white, uniform, no shadow",
      "message": "The background is uneven. Retake against a plain wall with the light in front of the subject."
    },
    {
      "id": "expression",
      "name": "Expression",
      "status": "pass",
      "measured": null,
      "expected": "Neutral, mouth closed",
      "message": "The mouth is closed and the expression is neutral."
    }
  ],
  "fixSuggestion": null,
  "retakeTips": [
    "Move closer so the head fills about half to two-thirds of the frame.",
    "Stand in front of a plain white or off-white wall, with the light on the face and no shadow behind the head.",
    "Do not replace the background or edit the photo. US passport photos must be unaltered."
  ]
}

A real response includes every rule that ran. The list above is shortened.

Specification that allows a correction ​

When alterationAllowed is true and every failure can be corrected, fixable is true. Badge specifications are account-specific. The YOUR_BADGE_SPEC code below is the same placeholder the badge photo page uses. Branch on the booleans, not on this sample code.

json
{
  "pass": false,
  "score": 71,
  "fixable": true,
  "language": "en",
  "spec": {
    "specCode": "YOUR_BADGE_SPEC",
    "specCodeName": "Employee badge",
    "alterationAllowed": true
  },
  "checks": [
    {
      "id": "expression",
      "name": "Expression",
      "status": "fail",
      "measured": null,
      "expected": "Neutral, mouth closed",
      "message": "The mouth is open. A correction is allowed for this specification."
    },
    {
      "id": "lighting_shadows",
      "name": "Lighting and shadows",
      "status": "fail",
      "measured": "Strong light from the left",
      "expected": "Even light on the face",
      "message": "One side of the face is much brighter than the other."
    }
  ],
  "fixSuggestion": {
    "summary": "Correct the expression and the lighting with the Fix Passport Photo API, then check the portrait again.",
    "steps": [
      {
        "endpoint": "POST /v2/aiEdit",
        "reason": "Expression and lighting can be corrected without a new capture on this specification."
      }
    ]
  },
  "retakeTips": [
    "If you would rather retake, ask for a closed mouth and a light source in front of the face."
  ]
}

If the only failures are size or background, and the specification allows that processing, the step is POST /v2/makeIdPhoto with the same specCode. Recheck the file it returns. Do not send a US passport, UK passport, or DV lottery photo down that path: those responses keep fixable false.

After POST /v2/aiEdit, call POST /v2/checkPhoto on the edited image before you treat it as ready. The fix endpoint does not select a document size and does not guarantee acceptance. See the Fix Passport Photo API.

Code examples ​

curl ​

bash
curl --request POST \
  --url https://api-us.idphotoapp.com/v2/checkPhoto \
  --header 'Content-Type: application/json' \
  --data '{
    "apiKey": "your-api-key",
    "apiSecret": "your-api-secret",
    "imageBase64": "your-base64-encoded-image",
    "specCode": "uk-passport",
    "language": "en"
  }'

Use https://api-eu.idphotoapp.com when the account is configured for the EU region.

Node.js ​

Run this on the server. Do not ship the secret to a browser.

javascript
import { readFile } from "node:fs/promises";

const image = await readFile("portrait.jpg");
const response = await fetch("https://api-us.idphotoapp.com/v2/checkPhoto", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    apiKey: process.env.IDPHOTO_API_KEY,
    apiSecret: process.env.IDPHOTO_API_SECRET,
    imageBase64: image.toString("base64"),
    specCode: "us-passport",
    language: "en",
  }),
  signal: AbortSignal.timeout(60_000),
});

if (!response.ok) {
  throw new Error(`Check failed with HTTP ${response.status}: ${await response.text()}`);
}

const result = await response.json();
if (result.pass) {
  console.log("No failing rule. This is not an acceptance decision.");
} else if (result.fixable && result.spec.alterationAllowed) {
  console.log(result.fixSuggestion.summary);
} else {
  console.log(result.retakeTips.join("\n"));
}

Python ​

python
import base64
import os

import requests

with open("portrait.jpg", "rb") as image_file:
    image_base64 = base64.b64encode(image_file.read()).decode("ascii")

response = requests.post(
    "https://api-us.idphotoapp.com/v2/checkPhoto",
    json={
        "apiKey": os.environ["IDPHOTO_API_KEY"],
        "apiSecret": os.environ["IDPHOTO_API_SECRET"],
        "imageBase64": image_base64,
        "specCode": "us-dvlottery",
        "language": "en",
    },
    timeout=60,
)
response.raise_for_status()
result = response.json()

# DV lottery photos are not auto-fixed. alterationAllowed is false.
if not result["pass"]:
    for tip in result["retakeTips"]:
        print(tip)

More Python samples are in Sample Python Code.

Error codes ​

These are transport and input errors. A photo that fails a rule still returns HTTP 200 and pass: false.

The error body is JSON:

json
{
  "code": "INVALID_SPEC_CODE",
  "message": "No specification named us-passport-photo was found. Query POST /v2/listIdPhotoSpecCode."
}
codeHTTPWhen it is returned
INVALID_CREDENTIALS401The key or secret is missing or not valid.
FORBIDDEN403The endpoint is not enabled for this key.
MALFORMED_REQUEST400The JSON could not be read, or a required field other than the image is missing.
MISSING_IMAGE400No imageBase64, imageUrl, or file was sent.
MULTIPLE_IMAGE_SOURCES400More than one image source was sent.
INVALID_IMAGE400The bytes are not a readable JPEG or PNG.
IMAGE_TOO_LARGE413The file is larger than 10 MB.
INVALID_IMAGE_URL400imageUrl is not an HTTPS URL, or it points at a private address.
IMAGE_FETCH_FAILED422The URL could not be fetched or was not a JPEG or PNG.
INVALID_SPEC_CODE400specCode is not on this account. Query the specification list.
PROCESSING_FAILED500The check could not be completed. Safe to retry once with the same image.

Distinguish these from a successful body whose checks contain fail. Map message for operators. Map checks[].message and retakeTips for the person in front of the camera.