跳转到清理工具Clear AI Metadata
DEVELOPER API / V1

元数据清理 API

通过私有异步 API 清理图片和视频元数据。上传文件、创建任务、轮询到终态,然后下载清理后的结果。

JPG · PNG · WebP

MP4 · MOV

API 文件会私有上传到临时存储,处理完成后删除。

用量

API 使用购买积分;Advanced pack 积分可用于 API,Pro 不包含 API 积分。

网页清理在设备上完成,不会上传文件。

限制与错误

每个账号 3 个进行中任务

结果约保留 24 小时

快速开始

请保密 API Key,只在服务端使用。API 文件会私有上传;网页清理不会上传文件。

POST /api/v1/uploadsPOST /api/v1/jobsGET /api/v1/jobs/{id}GET /api/v1/jobs/{id}/resultGET /api/v1/usage
cURL
# Bash + curl + jq. Stop on HTTP errors; do not save error JSON as an image.
set -euo pipefail
BASE='https://clearaimetadata.com'
UPLOAD=$(curl --fail-with-body -sS --max-time 120 -X POST "$BASE/api/v1/uploads"   -H "Authorization: Bearer $CLEAR_AI_METADATA_API_KEY"   -H 'Content-Type: image/png' --data-binary @photo.png | jq -er .uploadId)

# Keep this key and uploadId when retrying an uncertain create request.
REQUEST_ID=$(uuidgen)
JOB=$(curl --fail-with-body -sS --max-time 30 -X POST "$BASE/api/v1/jobs"   -H "Authorization: Bearer $CLEAR_AI_METADATA_API_KEY"   -H 'Content-Type: application/json' -H "Idempotency-Key: $REQUEST_ID"   -d "$(jq -nc --arg uploadId "$UPLOAD" '{uploadId:$uploadId}')" | jq -er .id)

STATE='ready'
for attempt in $(seq 1 120); do
  STATE=$(curl --fail-with-body -sS --max-time 30 "$BASE/api/v1/jobs/$JOB"     -H "Authorization: Bearer $CLEAR_AI_METADATA_API_KEY" | jq -er .status)
  case "$STATE" in
    succeeded) break ;;
    failed|canceled) echo "Job failed: $JOB" >&2; exit 1 ;;
  esac
  sleep 2
done
[ "$STATE" = 'succeeded' ] || { echo "Still pending: $JOB; query this job again, do not create another." >&2; exit 1; }
curl --fail -sS --max-time 300 "$BASE/api/v1/jobs/$JOB/result"   -H "Authorization: Bearer $CLEAR_AI_METADATA_API_KEY" -o cleaned.png
Node.js
import { readFile, writeFile } from 'node:fs/promises';

const base = 'https://clearaimetadata.com';
const key = process.env.CLEAR_AI_METADATA_API_KEY;
const headers = { Authorization: 'Bearer ' + key };

const upload = await fetch(base + '/api/v1/uploads', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'image/png' },
  body: await readFile('photo.png'),
});
if (!upload.ok) throw new Error('Upload failed: ' + upload.status);
const { uploadId } = await upload.json();

const requestId = crypto.randomUUID(); // Reuse for retries with this uploadId.
const created = await fetch(base + '/api/v1/jobs', {
  method: 'POST',
  headers: {
    ...headers,
    'Content-Type': 'application/json',
    'Idempotency-Key': requestId,
  },
  body: JSON.stringify({ uploadId }),
});
if (!created.ok) throw new Error('Job failed: ' + created.status);
const { id } = await created.json();

let current;
for (let attempt = 0; attempt < 120; attempt += 1) {
  const response = await fetch(base + '/api/v1/jobs/' + id, { headers });
  if (response.status === 429 || response.status === 503) {
    await new Promise(resolve => setTimeout(resolve, Math.min(60, Number(response.headers.get('Retry-After')) || 5) * 1000));
    continue;
  }
  if (!response.ok) throw new Error('Status failed: ' + response.status);
  current = await response.json();
  if (current.status === 'succeeded' || current.status === 'failed') break;
  await new Promise((resolve) => setTimeout(resolve, 2000));
}
if (current?.status !== 'succeeded') throw new Error('Job did not succeed');

const result = await fetch(base + '/api/v1/jobs/' + id + '/result', { headers });
if (!result.ok) throw new Error('Download failed: ' + result.status);
await writeFile('cleaned.png', Buffer.from(await result.arrayBuffer()));
Python
import os, time, uuid, requests

base = 'https://clearaimetadata.com'
headers = {'Authorization': 'Bearer ' + os.environ['CLEAR_AI_METADATA_API_KEY']}

with open('photo.png', 'rb') as image:
    upload = requests.post(base + '/api/v1/uploads', headers={**headers, 'Content-Type': 'image/png'}, data=image, timeout=120)
upload.raise_for_status()
upload_id = upload.json()['uploadId']

created = requests.post(
    base + '/api/v1/jobs',
    headers={**headers, 'Content-Type': 'application/json', 'Idempotency-Key': str(uuid.uuid4())},
    json={'uploadId': upload_id}, timeout=30,
)
created.raise_for_status()
job_id = created.json()['id']

current = {'status': 'ready'}
for _ in range(120):
    response = requests.get(base + '/api/v1/jobs/' + job_id, headers=headers, timeout=30)
    if response.status_code in (429, 503):
        time.sleep(min(60, int(response.headers.get('Retry-After', '5'))))
        continue
    response.raise_for_status()
    current = response.json()
    if current['status'] in ('succeeded', 'failed'):
        break
    time.sleep(2)
if current['status'] != 'succeeded':
    raise RuntimeError('Job did not succeed')

result = requests.get(base + '/api/v1/jobs/' + job_id + '/result', headers=headers, timeout=300)
result.raise_for_status()
with open('cleaned.png', 'wb') as output:
    output.write(result.content)
{"uploadId":"7a6e...","credits":1,"expiresAt":"..."}
{"id":"job_...","status":"ready","credits":1}
{"id":"job_...","status":"succeeded","progress":100}
{"balance":499,"apiUsesTotal":12,"apiAvailable":true}

限制与错误

Bearer API Key
Authorization: Bearer cam_…
每分钟 10 次上传 · 60 次请求
10 uploads/min · 60 requests/min
创建任务必须带 Idempotency-Key
Idempotency-Key
结果约保留 24 小时
24h
400INVALID_REQUESTCheck the JSON body, UUID and required headers.
401INVALID_API_KEYUse an active Bearer key from /account/api.
402INSUFFICIENT_CREDITSBuy any credit pack; Pro does not include API credits.
404UPLOAD_NOT_FOUND / NOT_FOUNDThe upload or job does not belong to this account, or expired.
409IDEMPOTENCY_CONFLICTThe idempotency key was already used with another upload.
409UPLOAD_ALREADY_USEDEach upload belongs to one job. Retry that job with its original idempotency key, or upload a new file.
409RESULT_NOT_READYThe job has not succeeded yet; poll the status endpoint.
410RESULT_EXPIREDThe download window has expired; submit a new job.
413FILE_TOO_LARGEImages are limited to 10 MiB and videos to 25 MiB.
415UNSUPPORTED_FORMATUse JPEG, PNG, WebP, MP4 or MOV.
429RATE_LIMITED / UPLOAD_LIMIT / CONCURRENCY_LIMITRespect Retry-After and reduce concurrent work.
503API_UNAVAILABLE / PROCESSOR_OFFLINEThe API or the local processor is temporarily unavailable.