Loading…

API endpoint update

New integrations should use . Existing URLs remain supported.

Detection jobs

Metered / image on completion
POST/v1/jobs

Use Detection jobs for high-volume workflows such as camera fleets, scheduled imports, partner integrations, or monthly volumes in the tens of thousands. Create a job, upload or provide the image, submit it, then fetch the result when processing finishes.

Parameters

Required

detect | detect_urban | filter | filter_urban

Workflow to run. Use detect for species output and filter for animal/human/vehicle screening. Urban variants use the urban model route.

Optional

string

Required for direct upload. Send only the original file name, such as img_001.jpg; do not send image bytes here.

image/jpeg | image/png | image/webp | image/heic | image/heif

Required for direct upload. Use the same value when uploading bytes to the returned upload URL.

URL string

Alternative input mode. Use only when the image is already available at a public URL.

integer

Optional direct-upload validation. Maximum job input size is 10 MB.

64-character hex string

Optional direct-upload validation checksum for the image bytes.

string

Optional ID from your system, such as a camera ID, image ID, or import row ID.

object

Processing options from the Instant detection endpoints: threshold, country, classify, metadata, smooth_herd, top_candidate, latitude, and longitude.

Request notes

Recommended flow: call POST /v1/jobs with filename and content_type, upload image bytes to the returned upload.url with PUT, then call submit_url. This avoids sending large image files through the Detection API server. If the image is already public, send source_url instead and the job is queued immediately.

Response fields

uuid

Required. Work ID used for submit, status, and result calls.

awaiting_upload | queued | running | succeeded | failed | expired

Required. Current processing state.

object

Optional. Temporary direct-upload target. Present when you create a job with filename and content_type.

URL string

Optional. Call this after direct upload succeeds. Public-URL inputs are submitted automatically.

URL string

Required. Poll this URL until processing reaches succeeded, failed, or expired.

URL string

Required. Fetch this URL after status is succeeded. The response matches the selected Instant detection endpoint shape.

Status codes

201Job created.
200Idempotent replay returned an existing job.
400Validation error.
401Invalid, missing, or revoked API key.
402Free monthly quota used up with no active API billing on the organisation, or API keys paused.
409Idempotency key was reused with a different payload.
429Rate limit exceeded.
503Database, storage, queue, or worker dependency temporarily unavailable.

Notes

  • Direct upload is preferred for high-volume processing.
  • filename is metadata only. Send a file name string such as img_001.jpg; do not send file bytes in POST /v1/jobs.
  • Use source_url only for images that are already publicly reachable.
  • For retries, send one stable Idempotency-Key per image/job and reuse it only with the same payload.
  • Completed jobs are metered per processed image once the free monthly quota is used.
  • Track job volume with GET /v1/jobs/usage (organisation-wide today, this month, and all-time counts per operation, split into free-quota-covered and billable jobs, priced at your effective rate, plus the free quota left this month). Metered images from completed jobs also count in GET /v1/usage.
  • Multi-image jobs, OCR jobs, and webhooks are later-phase additions.

Examples

cURL
export AD_API_KEY="sk_..."
export AD_API_BASE_URL="https://api.animaldetect.com/v1"
export IMAGE_PATH="/path/to/img_001.jpg"

curl -sS -X POST "$AD_API_BASE_URL/jobs" \
  -H "Authorization: Bearer $AD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: camera-a-img-001" \
  -d '{
    "operation": "detect",
    "filename": "img_001.jpg",
    "content_type": "image/jpeg",
    "client_reference_id": "camera-a/2026-06-17/img_001",
    "options": { "threshold": 0.2, "classify": true }
  }' > job.json

UPLOAD_URL="$(jq -r '.upload.url' job.json)"
SUBMIT_URL="$(jq -r '.submit_url' job.json)"
STATUS_URL="$(jq -r '.status_url' job.json)"
RESULT_URL="$(jq -r '.result_url' job.json)"

curl -sS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  --upload-file "$IMAGE_PATH"

curl -sS -X POST "$SUBMIT_URL" \
  -H "Authorization: Bearer $AD_API_KEY"

curl -sS "$STATUS_URL" \
  -H "Authorization: Bearer $AD_API_KEY"

curl -sS "$RESULT_URL" \
  -H "Authorization: Bearer $AD_API_KEY"
Node.js / Express
const apiKey = process.env.ANIMAL_DETECT_API_KEY
const baseUrl = 'https://api.animaldetect.com/v1'
const imageFile = new File([imageBytes], 'img_001.jpg', { type: 'image/jpeg' })

const createResponse = await fetch(baseUrl + '/jobs', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + apiKey,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'camera-a-img-001',
  },
  body: JSON.stringify({
    operation: 'detect',
    filename: imageFile.name,
    content_type: imageFile.type,
    expected_size_bytes: imageFile.size,
    client_reference_id: 'camera-a/2026-06-17/img_001',
    options: {
      threshold: 0.2,
      classify: true,
    },
  }),
})

const job = await createResponse.json()

await fetch(job.upload.url, {
  method: job.upload.method,
  headers: {
    ...job.upload.headers,
    'Content-Type': imageFile.type,
  },
  body: imageFile,
})

await fetch(job.submit_url, {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + apiKey,
  },
})

let status
do {
  await new Promise((resolve) => setTimeout(resolve, 2000))
  const statusResponse = await fetch(job.status_url, {
    headers: { Authorization: 'Bearer ' + apiKey },
  })
  status = await statusResponse.json()
} while (status.status === 'queued' || status.status === 'running')

if (status.status !== 'succeeded') {
  throw new Error(status.error?.message ?? 'Detection job failed')
}

const resultResponse = await fetch(job.result_url, {
  headers: { Authorization: 'Bearer ' + apiKey },
})
const result = await resultResponse.json()
Example response
{
  "job_id": "5fd3825f-46a3-4892-8600-44a319e2d18f",
  "status": "awaiting_upload",
  "operation": "detect",
  "client_reference_id": "camera-a/2026-06-17/img_001",
  "upload": {
    "method": "PUT",
    "url": "https://storage.googleapis.com/animal_detect_prod/users/...",
    "headers": {
      "Content-Type": "image/jpeg"
    },
    "expires_at": "2026-06-17T13:15:00.000Z"
  },
  "submit_url": "https://api.animaldetect.com/v1/jobs/5fd3825f-46a3-4892-8600-44a319e2d18f/submit",
  "status_url": "https://api.animaldetect.com/v1/jobs/5fd3825f-46a3-4892-8600-44a319e2d18f",
  "result_url": "https://api.animaldetect.com/v1/jobs/5fd3825f-46a3-4892-8600-44a319e2d18f/result",
  "expires_at": "2026-07-17T13:00:00.000Z"
}