Wildlife Detection API – AI Species Recognition

Wildlife Detection API

Add AI animal recognition to your own product — one call, no ML to build

The same wildlife AI that powers TrailCamHub, available to your code. Send an image or video URL and get back the species, how many animals, antler hints and more — in a single REST request. No models to train, no GPUs to run, no datasets to label.

Generate your API key → · Read the docs →


Why build on TrailCamHub instead of your own model

  • Skip the machine learning entirely. You call one endpoint; we run proven wildlife models behind it — MegaDetector for detection, DeepFaune in Europe and Google's SpeciesNet worldwide. No training, no infrastructure, no MLOps.
  • Images and video. One endpoint handles both — send image_url or video_url.
  • Region-aware accuracy. Choose the EU or global model, geofence the global model with country or coordinates, and restrict results to the species you actually expect.
  • Rich, structured results. Species with confidence, deduplicated animal counts, buck/antler hints for the deer species you choose, people-and-vehicle detection, and bounding boxes you can draw straight onto your images.
  • Pay as you go. Each call draws from your credit balance — 1 credit per image, 10 credits per video. No per-camera fees, no monthly minimums.
  • Self-serve and live in minutes. Generate a key in your account and start sending requests immediately.

How it works

  1. Generate an API key in your TrailCamHub account and keep it secret.
  2. POST a JSON body with a single image_url or video_url, plus any optional overrides (region, model, species, coordinates).
  3. Read the JSON response — detected species, counts, antler/sex hints and bounding boxes. Credits are drawn automatically per call.

Quick start

Endpoint

POST https://trailcamhub.com/api/public/detect

Headers

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Minimal request

curl -X POST https://trailcamhub.com/api/public/detect \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "image_url": "https://example.com/photo.jpg" }'

With overrides (all optional — defaults come from your profile)

curl -X POST https://trailcamhub.com/api/public/detect \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://example.com/photo.jpg",
    "model": "global",
    "species": ["roe_deer", "red_fox"],
    "latitude": 47.12,
    "longitude": 11.34,
    "country": "AT"
  }'

JavaScript (fetch)

const res = await fetch("https://trailcamhub.com/api/public/detect", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ image_url: "https://example.com/photo.jpg" }),
});
const data = await res.json();

Species keys must match the model's vocabulary — deepfaune_label_en for the EU model, speciesnet_key for the global model. Omit species and we fall back to your region plus your profile favorites.


Built on proven wildlife models

You're not getting a black box trained on who-knows-what. Under the hood are the same conservation-grade models used across wildlife research — MegaDetector to find the animals, DeepFaune for European species and SpeciesNet for global coverage of 2,000+ species — wrapped in a clean API and constrained to the species and region you specify. The result: fewer odd misclassifications, and the doubtful cases surfaced rather than hidden.


Who it's for

  • Trail-camera and hunting-tech companies that want to add AI recognition to their product without building an ML team.
  • App and platform developers — hunting, wildlife, outdoor and farm-monitoring apps that need species detection in their own pipeline.
  • Researchers and conservation projects processing camera-trap imagery at scale.
  • Anyone with their own photo or video workflow who'd rather call an API than run GPUs.

Pricing

Calls draw from your TrailCamHub credit balance: 1 credit per image, 10 credits per video. The same credit packs you use across TrailCamHub apply here — top up once and spend it wherever you like, API or app. No subscription tied to the API, no per-camera fees.


Start detecting in minutes

Generate a key, send your first request, and you're live.

Generate your API key → · Read the full docs →

Frequently asked questions

A successful call to POST /api/public/detect returns HTTP 200 with a JSON body describing everything found in your image or video. This page documents every field.

The response shape is the same for both the EU and global models — only the species vocabulary differs (deepfaune_label_en for eu, SpeciesNet common names for global).


Example response (image)

json

{
  "species": {
    "red deer": 2
  },
  "metadata": {
    "photo_id": "ca947365-cbd7-4651-bfc3-b7a505f99b33",
    "camera_id": "b5fd3fef-7d54-4c14-86d2-699187eb0728"
  },
  "has_human": false,
  "n_animals": 2,
  "n_persons": 0,
  "detections": [
    {
      "box": [0.7787, 0.4361, 0.9992, 0.7783],
      "label": "animal",
      "antlers": "no",
      "species": "red deer",
      "confidence": 0.909
    },
    {
      "box": [0.0594, 0.2822, 0.1589, 0.4604],
      "label": "animal",
      "antlers": "yes",
      "species": "red deer",
      "sex_hint": "likely_male",
      "antler_box": [0.0898, 0.1735, 0.1156, 0.2697],
      "confidence": 0.522
    }
  ],
  "media_type": "image",
  "n_vehicles": 0,
  "needs_review": false
}

Top-level fields

Field

Type

Description

media_type

string

"image" or "video". Tells you which response shape to expect.

species

object

A summary map of species name → count, aggregated across the capture. In the example, two red deer were found, so { "red deer": 2 }. Use this for a quick total without iterating detections.

n_animals

integer

Total number of animals detected.

n_persons

integer

Number of people detected.

n_vehicles

integer

Number of vehicles detected.

has_human

boolean

true if at least one person was detected (a convenience flag equal to n_persons > 0). Useful for privacy handling.

needs_review

boolean

true if any detection was uncertain or flagged — for example an uncertain antler call or a species outside your allow-list. It is not an error; it's a cue to surface the result for human confirmation.

detections

array

One entry per detected object (animal, person or vehicle). See below.

metadata

object

Echoed back from your request unchanged. Whatever you send in metadata (e.g. photo_id, camera_id) is returned so you can correlate the response with your own records.


The detections array

Each element describes one detected object.

Field

Type

Present when

Description

box

number[4]

always

Bounding box [x1, y1, x2, y2], normalized to 0–1, origin top-left. (x1, y1) is the top-left corner, (x2, y2) the bottom-right, relative to the full image. See Bounding boxes below.

label

string

always

The detection category: "animal", "person" or "vehicle".

confidence

number

always

Model confidence for this detection, 0–1. In the example the standing deer scored 0.909; the second, partly obscured, 0.522.

species

string

animals only

The recognised species, in the model's vocabulary (e.g. "red deer"). Not present on person / vehicle detections.

antlers

string

checked deer only

"yes", "no" or "uncertain". Only present for the deer species you enabled for antler checking.

sex_hint

string

antlers == "yes"

Always "likely_male". Present only when antlers were clearly detected.

antler_box

number[4]

antlers == "yes"

Bounding box of the antlers, same format as box. It sits on the head and may extend above the animal's box.

Person and vehicle detections carry a box and label but no species or antler fields.


Bounding boxes

All boxes — box and antler_box — use the same format: four numbers [x1, y1, x2, y2], each between 0 and 1, measured from the top-left of the full image. This makes them resolution-independent, so the same response works whether you render a thumbnail or the full-size image.

To convert a box to pixels for drawing an overlay:

js

const [x1, y1, x2, y2] = det.box;
const left   = x1 * imageWidth;
const top    = y1 * imageHeight;
const width  = (x2 - x1) * imageWidth;
const height = (y2 - y1) * imageHeight;
// draw a rectangle at (left, top) of size width × height

antler_box is drawn exactly the same way and, when present, marks where the antlers are.


Antler and sex fields

Antler detection is deliberately conservative, and the response reflects that:

  • antlers: "yes" is the only value that adds sex_hint: "likely_male" and an antler_box. Treat it as likely, not certain.

  • antlers: "uncertain" contributes to needs_review so you can take a closer look.

  • antlers: "no" (or the absence of antler fields) never implies a female animal — a doe, cast antlers, or a turned or cropped head simply can't be judged.

Only the deer species you enable for antler checking will carry these fields; all other animals omit them.


needs_review

This flag is true when the result contains something worth a human glance, typically:

  • an "uncertain" antler call, or

  • a recognised species that falls outside the allow-list you sent in species (or your profile defaults).

A flagged result is still returned in full — needs_review simply tells your application to route it for confirmation rather than treat it as final.


Video responses

A video request returns the same top-level fields with media_type: "video", where the counts (species, n_animals, …) are aggregated across the sampled frames, plus additional frame-level detail (a representative key frame and a per-frame breakdown). The exact frame fields are documented separately — see the video example in the docs.


HTTP status

A 200 response always carries the JSON above. Any non-200 status indicates a problem (authentication, credits, validation, etc.) — see the Errors reference for the full list and how to handle each.

Species mapping: DeepFaune and SpeciesNet keys

When you filter results with the species or priors parameter — or when you read a detection back — the species keys come from the recognition model's own vocabulary, not free text. The two models name things differently:

  • EU model → DeepFaune labels (deepfaune_label_en)

  • Global model → SpeciesNet keys (speciesnet_key)

Below is a mapping for the most common game animals TrailCamHub supports. A "—" in the DeepFaune column means the species has no dedicated European label: the global model recognises it, while in Europe it may be read as a near relative or flagged for review. The level after each SpeciesNet key (species, genus or family) shows how precisely the animal is matched.

Animal

DeepFaune (EU)

SpeciesNet (global)

Alpine ibex

ibex

capra ibex (species)

Alpine marmot

marmot

marmota marmota (species)

American beaver

castor canadensis (species)

American bison

bison bison (species)

American black bear

ursus americanus (species)

Aoudad / Barbary sheep

bovidae (family)

Banteng

bos javanicus (species)

Bighorn sheep

ovis canadensis (species)

Bird

Bird

Bobcat

lynx rufus (species)

Brown bear / Grizzly

bear

ursus arctos (species)

Canada lynx

lynx canadensis (species)

Caribou / Reindeer

reindeer

rangifer tarandus (species)

Chamois

chamois

rupicapra rupicapra (species)

Chital / Axis deer

axis axis (species)

Collared peccary

pecari tajacu (species)

Common wallaroo / Euro

osphranter (genus)

Cougar / Mountain lion

puma concolor (species)

Coyote

canis latrans (species)

Coypu / Nutria

nutria

myocastor coypus (species)

Dall / Thinhorn sheep

ovis (genus)

Dingo

canis lupus dingo (species)

Dromedary camel

camelus dromedarius (species)

Eastern grey kangaroo

macropus giganteus (species)

Elk / Wapiti

cervus canadensis (species)

Eurasian beaver

beaver

castor fiber (species)

Eurasian lynx

lynx

lynx lynx (species)

European badger

badger

meles meles (species)

European bison / Wisent

bison

bison bonasus (species)

Fallow deer

fallow deer

dama dama (species)

Feral cattle / Scrub bull

cow

bos taurus (species)

Feral donkey

equid

equus asinus (species)

Feral goat

goat

capra aegagrus hircus (species)

Feral horse / Brumby

equid

equus caballus (species)

Golden jackal

canis aureus (species)

Gray wolf

wolf

canis lupus (species)

Himalayan tahr

bovidae (family)

Hog deer

axis (genus)

Iberian ibex

ibex

capra (genus)

Moose

moose

alces alces (species)

Mouflon

mouflon

ovis orientalis (species)

Mountain goat

oreamnos americanus (species)

Mule deer (incl. black-tailed)

odocoileus hemionus (species)

Muskox

bovidae (family)

Polar bear

ursus (genus)

Pronghorn

antilocapra americana (species)

Pyrenean chamois

chamois

rupicapra (genus)

Raccoon

raccoon

procyon lotor (species)

Raccoon dog

nyctereutes procyonoides (species)

Red deer

red deer

cervus elaphus (species)

Red fox

fox

vulpes vulpes (species)

Red kangaroo

osphranter rufus (species)

Red-necked / Bennett's wallaby

macropodidae (family)

Roe deer

roe deer

capreolus capreolus (species)

Rusa deer

rusa timorensis (species)

Sambar

rusa unicolor (species)

Sika deer

cervus nippon (species)

Water buffalo

bubalus bubalis (species)

Western grey kangaroo

macropus fuliginosus (species)

White-tailed deer

odocoileus virginianus (species)

Wild boar / Feral pig

wild boar

sus scrofa (species)

Wild turkey

meleagris gallopavo (species)

Wolverine

wolverine

gulo gulo (species)

Need a species that isn't listed?

These are the animals TrailCamHub maps; the models themselves recognise many more. For the complete vocabularies, consult the models directly: DeepFaune (Europe) at deepfaune.cnrs.fr, and SpeciesNet (worldwide) in the model card on Kaggle or Google's project on GitHub. If you omit the species parameter, you don't need any of these keys — we fall back to your region plus your profile favourites automatically.

Each detection call draws from your TrailCamHub credit balance — the same balance you use across the app.

What a call costs

  • Image: 1 credit

  • Video: 10 credits

When credits are charged — on submit
Credits are deducted the moment a request is submitted, not when it succeeds. You're paying for the analysis to run, not for a particular result.

Because of that:

  • A call still costs credits even when nothing is found — no animals detected, or no antlers on a checked deer. An empty result is still a result; the analysis ran.

  • Since the charge is taken at submission, a request that's accepted but then can't be completed (for example a URL we can't reach, or a video with no readable frames) is also charged. Make sure your image and video URLs are public and valid before sending.

  • Requests that are rejected before processing — a missing or invalid API key, a malformed body, or an empty balance — are not charged.

What happens at zero balance
If your balance can't cover a call, the request is rejected with HTTP 402 and isn't processed (and isn't charged). Top up to continue.

Checking your balance and buying credits
There's no API endpoint for your balance. Your current credits are shown in the TrailCamHub app, and that's also where you buy more — the same credit packs used across the platform. Plan your usage around the rates above (1 per image, 10 per video, charged up front), since you can't poll the balance programmatically.

What happens if I don't pass the optional parameters?

Only the media URL is required — exactly one of image_url or video_url. Everything else in the body (region_id, model, species, priors, latitude, longitude, country) is an optional override.

When you leave a parameter out, the API falls back to the defaults of the account the API key belongs to — the same settings you configured in TrailCamHub. So a bare request is analysed exactly as it would be inside your own account.

Specifically, when omitted:

  • region_id / model → your account's default region, and that region's model (eu or global).

  • species → the species list for that region plus your profile favourites — i.e. your allow-list.

  • priors → the species you marked as "most likely" in your profile.

  • latitude / longitude / country (global model only) → the location configured for your region.

That means the simplest possible call still works and behaves sensibly:

json

{ "image_url": "https://example.com/photo.jpg" }

Overrides are per-call. Any parameter you do send overrides the default for that single request only — it never changes your saved profile. Pass model, species or coordinates when a specific camera or location differs from your defaults; leave them out to use your account settings.

Tip: if all your requests share the same region and species, set them up once in your TrailCamHub profile and send minimal requests. If you process cameras across different regions or grounds, pass the relevant overrides (region_id, model, species, coordinates) on each call so every image is analysed against the right context.