API Integration Guide
Integrate shrimp detection, counting, and weight estimation into your app. Send a photo, get back per-shrimp detections with segmentation masks, skeleton length, and estimated weight in grams.
Authentication
POST /predict requires an API key sent in the X-API-Key header on every request. The key is configured server-side as API_KEY in backend/.env — ask the team operating this service for your key. Keep it on your server; never ship it to browsers or mobile clients.
X-API-Key: <your-api-key>Health (GET /) and GET /backends need no key, so load balancers and uptime checks work unauthenticated.
POST/predict
Multipart upload of one image. Runs the local YOLOv8-seg model with Poly-Ridge weight estimation and returns detections for every shrimp (and calibration marker) found. For weight estimation the photo must contain the standard calibration marker and be shot top-down.
Query parameters
| Name | Type | Description | |
|---|---|---|---|
| capture_tilt_deg | number | optional | Device tilt in degrees at capture time, from phone sensors. Adds a quality warning when past tolerance. Omit for plain uploads. |
Body (multipart/form-data)
| file | file | required | The image (JPEG/PNG). Content-Type must be an image type or the request is rejected with 400. |
Response — 200 OK
count is the total number of detections including calibration markers — filter predictions by class_name === "shrimp" (or by weight_g != null) for shrimp-only counts. warnings carries non-fatal quality advisories (missing marker, high tilt). Fields like weight_g, length_mm, area_mm2 are null when the scale marker is not detected.
{
"predictions": [
{
"x": 512.4, "y": 380.1,
"width": 210.0, "height": 96.5,
"confidence": 0.92,
"class_name": "shrimp",
"class_id": 0,
"detection_id": "a1b2c3",
"points": [{ "x": 410.0, "y": 350.2 }, ...],
"skeleton_points": [{ "x": 415.1, "y": 372.0 }, ...],
"length_mm": 84.3,
"area_mm2": 612.7,
"weight_g": 7.9,
"scale_mm_per_px": 0.212
}
],
"count": 1,
"class_counts": { "shrimp": 1 },
"image_width": 3024,
"image_height": 4032,
"inference_time_ms": 842.5,
"inference_type": "local_seg",
"warnings": []
}Examples
curl -X POST "https://shrimp-ai.durrowjones.com/api/predict" \
-H "X-API-Key: $SHRIMP_API_KEY" \
-F "file=@shrimp_photo.jpg"import os
import requests
API_URL = "https://shrimp-ai.durrowjones.com/api"
API_KEY = os.environ["SHRIMP_API_KEY"]
with open("shrimp_photo.jpg", "rb") as f:
resp = requests.post(
f"{API_URL}/predict",
headers={"X-API-Key": API_KEY},
files={"file": ("shrimp_photo.jpg", f, "image/jpeg")},
timeout=60,
)
resp.raise_for_status()
result = resp.json()
print(f"Shrimp count: {result['count']}")
for det in result["predictions"]:
if det.get("weight_g") is not None:
print(f" {det['class_name']}: {det['weight_g']:.1f} g "
f"({det['length_mm']:.0f} mm)")const API_URL = "https://shrimp-ai.durrowjones.com/api";
const API_KEY = process.env.SHRIMP_API_KEY!; // keep server-side
async function countShrimp(image: File | Blob, filename: string) {
const form = new FormData();
form.append("file", image, filename);
const res = await fetch(`${API_URL}/predict`, {
method: "POST",
headers: { "X-API-Key": API_KEY },
body: form,
});
if (!res.ok) {
const err = await res.json().catch(() => null);
throw new Error(err?.detail ?? `HTTP ${res.status}`);
}
return res.json(); // PredictionResponse
}Errors
| Status | Meaning |
|---|---|
| 400 | Not an image, or the backend rejected the input. See detail. |
| 401 | Missing or invalid X-API-Key header. |
| 503 | Server has no API_KEY configured, or the selected inference backend is unavailable. |
| 500 | Unexpected failure while processing the image. |
All errors are JSON: { "detail": "<message>" }