Erased

API Reference

Base URL: https://erased.ink/api/v1

All successful responses are application/json. Errors are application/problem+json (RFC 7807) — see the Error codes page.

POST /uploads

Issue a short-lived presigned R2 PUT URL.

Request

{
  "filename": "photo.png",
  "content_type": "image/png",
  "size": 4823109
}
FieldTypeNotes
filenamestringUsed for response headers only; the server assigns the object_key.
content_typestringOne of image/png, image/jpeg, image/webp, image/avif, image/heic, image/heif, image/jxl.
sizeintegerMax 10485760 (10 MB).

Response 200

{
  "upload_url": "https://...",
  "object_key": "in/2026-05-24/job_xxx.png",
  "expires_at": "2026-05-24T10:15:00Z"
}

You must PUT the file to upload_url before expires_at.


POST /jobs

Create a processing job.

Request body — three shapes

POST /api/v1/jobs accepts three body forms. Pick one based on what you have at hand:

Content-TypeRequired fieldsBest for
multipart/form-dataimage (file, ≤10MB) or object_key; mode; options (JSON string, optional)One-shot upload-and-process. Drop-in curl -F use.
application/json + image_base64image_base64 (≤1MB decoded), content_type, modeJSON-only toolchains (Zapier, n8n). Small images.
application/json + object_keyobject_key (from POST /uploads), modeLarge files / batch — pair with the presigned upload.

Example (object_key form)

{
  "object_key": "in/2026-05-24/job_xxx.png",
  "mode": "visible"
}

Response — sync (visible, metadata)

{
  "id": "job_xxx",
  "status": "succeeded",
  "mode": "visible",
  "result_url": "https://...",
  "result_expires_at": "2026-05-25T10:00:00Z",
  "processing_ms": 412
}

Response — async (invisible, all)

{
  "id": "job_xxx",
  "status": "queued",
  "mode": "invisible",
  "poll_url": "/api/v1/jobs/job_xxx",
  "poll_after_ms": 2000
}

Response — detect (read-only, no result_url)

{
  "id": "job_xxx",
  "status": "succeeded",
  "mode": "detect",
  "processing_ms": 487,
  "report": {
    "visible": {
      "detected": true,
      "confidence": 0.87,
      "region": [1820, 120, 80, 80],
      "label": "Gemini sparkle"
    },
    "metadata": {
      "c2pa": { "present": true, "manifest": { "claim_generator": "Gemini ..." } },
      "ai_markers": { "Software": "Gemini" }
    },
    "invisible": { "status": "not_tested" },
    "overall_assessment": "Markers found in the completed checks: Gemini sparkle; C2PA Content Credentials (not proof of AI generation); Metadata: Software. Invisible watermarks, including SynthID and TreeRing, were not tested."
  }
}

detect never writes an output object; the original image is unchanged.


GET /jobs/:id

Poll an async job (and a valid read for sync jobs while their record is still in KV — about a 30-minute TTL).

Response — waiting for GPU

{
  "id": "job_xxx",
  "status": "queued",
  "mode": "invisible",
  "poll_after_ms": 2000
}

Response — GPU allocated and processing

{
  "id": "job_xxx",
  "status": "processing",
  "mode": "invisible",
  "poll_after_ms": 2000
}

Response — succeeded

Same shape as the sync success response above.

Response — failed

{
  "id": "job_xxx",
  "status": "failed",
  "mode": "invisible",
  "error": {
    "code": "pipeline_error",
    "message": "..."
  }
}

Authentication tiers

detect is the only mode available without an account (100 req/hour unauthenticated). All strip modes require at least a free account.

  • Free (signed in) — sign in via Google OAuth or an email magic link at /en/sign-in. Unlocks all strip modes. No API key issued.
  • Pro ($12 / month for new subscriptions) — issued an API key. Pass it as Authorization: Bearer uk_.... Each paid period includes 5,000 light operations, 100 heavy operations, unlimited detection, and 100 buffer credits for overage. Buffer credits expire at the end of that period. Existing subscriptions on the $9 early-bird plan keep that price.

Rate limits

detect is the only mode usable without an account: 100 req/hour unauthenticated, 500/hour for free accounts. All other modes require a free account (signed in) or Pro.

Quota groupModesFree (signed in)Pro
lightvisible, metadata10 / day5000 / period
heavyinvisible, all3 / day100 / period
uploadsPOST /uploads200 / h5000 / h
detectdetect500 / hunlimited

Anti-abuse rate limits still apply to all plans and return 429 with a Retry-After header and an RFC 7807 body. Daily free allowances reset at 00:00 UTC. Once the free or Pro allowance is used up, light jobs cost 1 credit and heavy jobs cost 10 credits; insufficient credits returns 402. Failed jobs automatically refund their quota and credits.